At a glance
| Item |
Summary |
| Purpose |
Classify projected-media input and transcode it to the Apple Projected Media Profile delivery variant. |
| App architecture |
Three-part command-line pipeline: argument orchestration, optional classification, and media conversion. |
| Main patterns |
Command, classifier, immutable pipeline owner, and metadata data carrier. |
| Project style |
One executable target with three primary Swift files and no UI or persistence layer. |
Project structure
ProjectedMediaConversion/
├── ProjectedMediaConversion.swift # CLI, validation, output naming
├── ProjectedMediaClassifier.swift # Input format inspection
└── Converter.swift # Reader, conversion, and writer pipeline
Structure observations
- File names align with the three runtime responsibilities.
- The command creates short-lived collaborators;
APMPConverter retains only source-reading state.
- There are no app-defined protocols or nested architectural folders.
Overall architecture
flowchart LR
CLI["ProjectedMediaConversion"] --> Classifier["ProjectedMediaClassifier (optional)"]
CLI --> Metadata["ProjectedMediaMetadata"]
CLI --> Converter["APMPConverter"]
Converter --> Reader["AVAssetReader"]
Converter --> Transfer["VTPixelTransferSession"]
Converter --> Writer["AVAssetWriter"]
Reference code
ProjectedMediaConversion/ProjectedMediaConversion.swift:47 — command orchestration
mutating func run() async throws {
// ... optionally classify input ...
let converter = try await APMPConverter(from: inputURL)
try await converter.convertToAPMP(
output: outputURL,
projectedMediaMetadata: projectedMediaMetadata
)
}
Interpretation
The entry type owns policy such as argument validation and output naming. The classifier only reads format descriptions. The converter owns the streaming mechanism and chooses mono or frame-packed processing from the metadata value.
Ownership and state
classDiagram
ProjectedMediaConversion ..> ProjectedMediaClassifier : creates when auto-detecting
ProjectedMediaConversion ..> APMPConverter : creates
APMPConverter *-- AVAssetReader : retains
APMPConverter *-- AVAssetReaderTrackOutput : retains
APMPConverter ..> AVAssetWriter : creates per conversion
Ownership evidence
ProjectedMediaConversion/Converter.swift:27 — immutable converter state
final class APMPConverter: Sendable {
let sourceVideoFrameSize: CGSize
let reader: AVAssetReader
let sourceVideoTrack: AVAssetReaderTrackOutput
let sourceVideoTrackProvider:
AVAssetReaderOutput.Provider<CMReadySampleBuffer<CMSampleBuffer.DynamicContent>>
}
| Owner |
Object or state |
Relationship |
Mutation authority |
ProjectedMediaConversion |
Parsed options and output URL |
Argument Parser populates; run derives values |
Mutating command instance |
ProjectedMediaClassifier |
Classification flags and detected strings |
Computes immutable results in init |
Initializer only |
APMPConverter |
Reader, source output, frame size, provider |
Creates and retains |
Converter initialization and conversion methods |
convertToAPMP invocation |
Writer, input receivers, transfer session |
Creates as operation-local resources |
The active conversion task |
Class and protocol design
ProjectedMediaConversion/ProjectedMediaConversion.swift:11 — framework command protocol
@main
struct ProjectedMediaConversion: AsyncParsableCommand {
@Argument var sourceVideoPath: String
@Flag var autoDetect = false
mutating func run() async throws { /* ... */ }
}
| Type |
Responsibility |
Depends on |
ProjectedMediaConversion |
Parse input, validate metadata, choose destination, invoke conversion |
Swift Argument Parser |
ProjectedMediaClassifier |
Interpret projection, packing, codec, and compatibility extensions |
AVFoundation, CoreMedia |
APMPConverter |
Decode, split packed views when necessary, tag buffers, encode, and finish |
AVFoundation, VideoToolbox |
ProjectedMediaMetadata |
Carry projection and optional stereo values |
Value semantics only |
The only protocol-oriented boundaries are framework protocols (AsyncParsableCommand) and Sendable. The converter and classifier are concrete because the executable demonstrates one implementation.
Access control
| Symbol |
Access |
Verified effect |
Likely rationale |
| All app types and members without modifiers |
internal by default |
Usable only within the executable module. |
No library API is intended. |
APMPConverter |
final internal |
Other module types cannot subclass it; external modules cannot access it. |
Fixes the conversion invariant to one implementation. |
| Converter properties |
internal immutable let |
Module code could read them, but cannot replace them after initialization. |
Immutability supplies most encapsulation in this compact sample. |
| Operation-local writer and transfer objects |
lexical local scope |
Inaccessible after convertToAPMP returns. |
Ties resources to one conversion lifecycle. |
convertFrame |
internal |
Any code in the target could call it. |
Wider than strictly necessary, likely to keep sample mechanics directly inspectable. |
Explicit private / fileprivate declarations |
none |
The source relies on internal, immutable, and local scope instead. |
Keeps a short command-line sample easy to inspect. |
Reference code
ProjectedMediaConversion/ProjectedMediaClassifier.swift:12 — module-only, immutable result object
final class ProjectedMediaClassifier {
let convertedFromSpherical: Bool
let isProjectedMediaProfile: Bool
let projectionKind: String?
let viewPackingKind: String?
}
Logic ownership and placement
| Logic |
Owning type or file |
Placement rationale |
| Argument defaults and validation |
ProjectedMediaConversion |
CLI policy belongs at the entry boundary |
| Format classification |
ProjectedMediaClassifier |
Read-only interpretation is separated from mutation and output |
| Compression-property construction |
APMPConverter.convertToAPMP |
Coupled to writer configuration |
| Frame-packed eye cropping and tagging |
APMPConverter.convertFrame |
Closely coupled to source dimensions and encoding layer IDs |
| Output deletion and naming |
ProjectedMediaConversion.run |
User-facing command behavior rather than codec mechanics |
Design patterns
| Pattern |
Source evidence |
Purpose or tradeoff |
| Command |
ProjectedMediaConversion/ProjectedMediaConversion.swift:12 |
Gives one entry point for parsing and orchestration. |
| Classifier |
ProjectedMediaConversion/ProjectedMediaClassifier.swift:22 |
Isolates input inspection from conversion. |
| Processing pipeline |
ProjectedMediaConversion/Converter.swift:154, ProjectedMediaConversion/Converter.swift:164 |
Streams samples from provider to the correct writer receiver. |
| Data transfer object |
ProjectedMediaConversion/ProjectedMediaConversion.swift:89 |
Passes related metadata as one named value instead of positional parameters. |
| Conditional strategy without polymorphism |
ProjectedMediaConversion/Converter.swift:130 |
Selects tagged stereo versus mono receivers with a branch; simple, but not independently replaceable. |
Naming conventions
- Types describe role and output:
ProjectedMediaClassifier, APMPConverter, and ProjectedMediaMetadata.
- The executable type matches the product name.
- Methods use outcome-oriented verbs:
conformsToAPMPDeliveryVariant, convertToAPMP, and convertFrame.
- AVFoundation and VideoToolbox constants retain framework terminology such as layer IDs, view IDs, and projection kind.
Architecture takeaways
- Keep CLI policy separate from a streaming media pipeline.
- Use an immutable classifier when inspection should not alter source or converter state.
- Operation-local writer resources make conversion ownership and cleanup easy to understand.
- A protocol abstraction is unnecessary unless the executable gains multiple converter implementations or test seams.
Source map
| Source file |
Relevant symbols |
ProjectedMediaConversion/ProjectedMediaConversion.swift |
ProjectedMediaConversion, ProjectedMediaMetadata, ConversionError |
ProjectedMediaConversion/ProjectedMediaClassifier.swift |
ProjectedMediaClassifier |
ProjectedMediaConversion/Converter.swift |
APMPConverter, MV-HEVC identifiers |