Converting side-by-side 3D video to multiview HEVC and spatial video
At a glance
| Item |
Summary |
| Purpose |
Split side-by-side video into tagged left/right eye buffers and encode MV-HEVC, optionally with spatial metadata. |
| App architecture |
A two-file command-line app: argument policy delegates one streaming conversion to a stateful converter. |
| Main patterns |
Command, immutable data carrier, producer-consumer writer callback, and linear media pipeline. |
| Project style |
Flat executable target with one entry file and one converter file. |
Project structure
SideBySideToMVHEVC/
├── SideBySideToMVHEVC.swift # CLI options, validation, and destination
└── Converter.swift # Read, crop, tag, and encode pipeline
Structure observations
- Command policy and codec mechanics have a clear file boundary.
- The converter retains source dimensions and reader state; writer resources exist only for one transcode call.
- No app-defined protocols, UI types, or generalized utility folder are present.
Overall architecture
flowchart LR
CLI["SideBySideToMVHEVC"] --> Metadata["SpatialMetadata (optional)"]
CLI --> Converter["SideBySideConverter"]
Converter --> Reader["AVAssetReaderTrackOutput"]
Reader --> Split["Crop left/right + CMTag"]
Split --> Adapter["Tagged pixel-buffer adaptor"]
Adapter --> Writer["AVAssetWriter"]
Reference code
SideBySideToMVHEVC/SideBySideToMVHEVC.swift:43 — command-to-converter handoff
mutating func run() async throws {
// ... validate optional SpatialMetadata ...
let converter = try await SideBySideConverter(from: inputURL)
await converter.transcodeToMVHEVC(
output: outputURL,
spatialMetadata: spatialMetadata
)
}
Interpretation
The command decides whether the output is plain MV-HEVC or spatial video. SideBySideConverter performs one fixed algorithm: read packed frames, crop two eye images, attach semantic tags, and append them to the writer.
Ownership and state
classDiagram
SideBySideToMVHEVC ..> SideBySideConverter : creates
SideBySideConverter *-- AVAssetReader : retains
SideBySideConverter *-- AVAssetReaderTrackOutput : retains
SideBySideConverter ..> AVAssetWriter : creates per transcode
SideBySideConverter ..> VTPixelTransferSession : creates in writer callback
Ownership evidence
SideBySideToMVHEVC/Converter.swift:24 — converter-owned source state
final class SideBySideConverter: Sendable {
let sideBySideFrameSize: CGSize
let eyeFrameSize: CGSize
let reader: AVAssetReader
let sideBySideTrack: AVAssetReaderTrackOutput
}
| Owner |
Object or state |
Relationship |
Mutation authority |
SideBySideToMVHEVC |
Parsed flags/options and output naming |
Argument Parser populates, run derives |
Mutating command instance |
SideBySideConverter |
Reader, track output, source and eye sizes |
Creates and retains |
Initializer and converter methods |
transcodeToMVHEVC task |
Writer, writer input, adaptor |
Operation-local creation |
Writer callback until completion |
| Writer callback |
Pixel-transfer session and buffer pool access |
Creates/borrows |
Serial Multiview HEVC Writer queue |
Class and protocol design
SideBySideToMVHEVC/SideBySideToMVHEVC.swift:11 — command protocol and metadata value
@main
struct SideBySideToMVHEVC: AsyncParsableCommand { /* options + run */ }
struct SpatialMetadata {
var baselineInMillimeters: Double
var horizontalFOV: Double
var disparityAdjustment: Double
}
| Type |
Responsibility |
Depends on |
SideBySideToMVHEVC |
Parse options, require complete spatial metadata, select filename, invoke converter |
Swift Argument Parser |
SideBySideConverter |
Own the source reader and perform the transcode |
AVFoundation, VideoToolbox, CoreMedia |
SpatialMetadata |
Carry the three values required for spatial encoding |
Value semantics only |
ConversionError |
Report command-validation failure |
Error, CustomStringConvertible |
Protocol use is limited to framework contracts (AsyncParsableCommand, Sendable, and error protocols); there is one concrete converter.
Access control
| Symbol |
Access |
Verified effect |
Likely rationale |
| App types and members without modifiers |
internal by default |
Visible within the executable target only. |
The sample is not a reusable library. |
SideBySideConverter |
final internal |
Cannot be subclassed and is not exported. |
Keeps one defined conversion algorithm. |
| Converter dimensions and reader references |
internal immutable let |
Target code can read but not replace them. |
Immutability protects initialized source assumptions. |
| Writer, adaptor, transfer session, and buffer pool |
local scope |
Only the current operation/callback can use them. |
Bounds their lifecycle to a single transcode. |
convertFrame |
internal |
Callable throughout the target. |
Wider than required for orchestration; likely left visible to make the sample operation easy to inspect or exercise. |
Reference code
SideBySideToMVHEVC/Converter.swift:64 — public surface within the module
func transcodeToMVHEVC(
output videoOutputURL: URL,
spatialMetadata: SpatialMetadata?
) async {
// writer resources are local to this operation
}
Logic ownership and placement
| Logic |
Owning type or file |
Placement rationale |
| Option validation and output filename |
SideBySideToMVHEVC.run |
Command/user policy |
| Source reader setup and dimension calculation |
SideBySideConverter.init |
Establishes converter invariants once |
| Compression and spatial properties |
transcodeToMVHEVC |
Coupled to writer configuration |
| Back-pressure loop |
requestMediaDataWhenReady closure |
Writes only while the input can accept data |
| Eye cropping and CMTag creation |
convertFrame |
Per-frame transformation isolated from writer setup |
Design patterns
| Pattern |
Source evidence |
Purpose or tradeoff |
| Command |
SideBySideToMVHEVC/SideBySideToMVHEVC.swift:12 |
Consolidates parsing, validation, naming, and invocation. |
| Producer-consumer |
SideBySideToMVHEVC/Converter.swift:121 |
requestMediaDataWhenReady honors writer back pressure on a dedicated queue. |
| Processing pipeline |
SideBySideToMVHEVC/Converter.swift:133 |
Reads, crops, tags, appends, and finishes in one visible flow. |
| Data transfer object |
SideBySideToMVHEVC/SideBySideToMVHEVC.swift:84 |
Groups spatial values and makes their optional all-or-none use explicit. |
Naming conventions
- The executable and converter names state both input layout and output format.
- Commands use transformation verbs:
transcodeToMVHEVC and convertFrame.
- Properties name physical meaning and units, for example
baselineInMillimeters and eyeFrameSize.
- Constants retain codec terminology:
MVHEVCVideoLayerIDs, MVHEVCViewIDs, and MVHEVCLeftAndRightViewIDs.
Architecture takeaways
- Separate command validation from codec mechanics even in a two-file utility.
- Retain immutable source state, while keeping writer state local to each conversion.
- A writer-readiness callback provides a natural producer-consumer boundary for media samples.
- Concrete design is proportionate here; protocol abstraction would only pay off with alternate converter implementations.
Source map
| Source file |
Relevant symbols |
SideBySideToMVHEVC/SideBySideToMVHEVC.swift |
SideBySideToMVHEVC, SpatialMetadata, ConversionError |
SideBySideToMVHEVC/Converter.swift |
SideBySideConverter, MV-HEVC identifiers |