At a glance
| Item |
Summary |
| Purpose |
Copy a QuickTime movie while adding a timed display-mask metadata track associated with its enabled video track. |
| App architecture |
A two-file command-line tool: an AsyncParsableCommand validates arguments and hands one operation to MovieProcessor. |
| Main patterns |
Command entry point, orchestration facade, asynchronous reader-to-writer pipeline. |
| Project style |
Flat executable target with one entry file, one processor file, and small supporting value/error types. |
Project structure
AVAddDisplayMaskTrack.swift # ArgumentParser entry point
MovieProcessor.swift # Asset validation, track setup, transfer, metadata generation
AVAddDisplayMaskTrack.xcodeproj/
Configuration/
README.md
video-raging-river.mp4 # Input fixture
Structure observations
- Feature code is flat because the executable has one use case.
MovieProcessor.swift keeps the workflow and its VideoTrackInfo, BouncingBoxInfo, and ProcessingError support types together.
- There is no UI, persistence layer, protocol abstraction, or package boundary in the extracted source.
Overall architecture
flowchart LR
CLI["AVAddDisplayMaskTrack"] --> Processor["MovieProcessor"]
Processor --> Reader["AVAssetReader"]
Processor --> Metadata["Timed display-mask metadata"]
Reader --> Writer["AVAssetWriter"]
Metadata --> Writer
Writer --> Movie["Output .mov"]
Reference code
AVAddDisplayMaskTrack.swift:41 — run()
mutating func run() async throws {
let processor = MovieProcessor()
try await processor.processMovie(
inputPath: inputPath,
outputPath: outputPath,
displayMaskType: displayMaskType
)
}
Interpretation
The entry point owns argument handling only. MovieProcessor owns the complete media operation: it creates the reader and writer, wires passthrough tracks, adds metadata, and waits for completion. This is directly observed, not an inferred multi-layer architecture.
Ownership and state
classDiagram
AVAddDisplayMaskTrack --> MovieProcessor : creates per run
MovieProcessor *-- AVAssetReaderOutput : retains many
MovieProcessor *-- AVAssetWriterInput : retains many
MovieProcessor *-- BouncingBoxInfo : owns animation state
MovieProcessor --> AVAssetReader : configures and uses
MovieProcessor --> AVAssetWriter : configures and uses
Ownership evidence
MovieProcessor.swift:11 — processor state
class MovieProcessor {
private var readerOutputs: [AVAssetReaderOutput] = []
private var writerInputs: [AVAssetWriterInput] = []
private var metadataInput: AVAssetWriterInput?
private var currentBoxInfo = BouncingBoxInfo()
}
| Owner |
Object or state |
Relationship |
Mutation authority |
AVAddDisplayMaskTrack |
MovieProcessor |
Creates a processor for one command invocation |
The local run() scope owns the reference. |
MovieProcessor |
Reader outputs and writer inputs |
Retains corresponding track endpoints during transfer |
MovieProcessor only. |
MovieProcessor |
Metadata adaptor/input |
Creates and retains the new metadata-track writer endpoint |
MovieProcessor only. |
MovieProcessor |
Bouncing box and sample time |
Advances animation state while appending timed groups |
MovieProcessor only. |
Class and protocol design
There is no app-defined protocol boundary. The concrete MovieProcessor is the single service object, while structs carry track and animation data.
| Type |
Responsibility |
Depends on |
AVAddDisplayMaskTrack |
Parse CLI arguments and start the use case |
ArgumentParser, MovieProcessor |
MovieProcessor |
Coordinate asset validation, passthrough, metadata creation, and completion |
AVAssetReader, AVAssetWriter, Core Media metadata APIs |
VideoTrackInfo |
Carry dimensions, duration, timescale, and frame rate |
CoreGraphics, CoreMedia |
BouncingBoxInfo |
Carry mutable rectangle position and direction |
Swift value types |
ProcessingError |
Convert workflow failures into user-facing descriptions |
LocalizedError |
Access control
| Symbol |
Access |
Verified effect |
Likely rationale |
| Reader/writer and animation properties |
private |
Only MovieProcessor can read or mutate transfer state |
Keeps asynchronous pipeline invariants inside its coordinator. |
| Setup, transfer, metadata, and progress helpers |
private |
Callers see only processMovie(...) |
Presents one use-case-level operation and hides step ordering. |
processMovie(...) |
implicit internal |
The executable target can invoke it, but external modules cannot |
No public library API is needed for a command target. |
| Supporting structs and error enum |
implicit internal |
Available within the executable module |
They are implementation-level collaborators, not exported contracts. |
Reference code
MovieProcessor.swift:78 — hidden pipeline step
private func setupExistingTracksForPassthroughAndReturnEnabledVideoInput(
from asset: AVAsset,
reader: AVAssetReader,
writer: AVAssetWriter
) async throws -> (videoInput: AVAssetWriterInput, videoInfo: VideoTrackInfo) {
// ...
}
No public, open, or fileprivate declaration appears in the two Swift source files.
Logic ownership and placement
| Logic |
Owning type or file |
Placement rationale |
| Argument parsing and console outcome |
AVAddDisplayMaskTrack |
Kept at the process boundary. |
| Input validation and output replacement |
MovieProcessor.processMovie |
Part of orchestration before AVFoundation setup. |
| Existing-track passthrough |
MovieProcessor private helper |
Must coordinate paired reader outputs and writer inputs. |
| Static/dynamic rectangle calculation |
MovieProcessor private helpers |
Shares the processor’s sample time and bouncing-box state. |
| Domain error text |
ProcessingError |
Centralizes failure cases apart from media operations. |
Design patterns
| Pattern |
Source evidence |
Purpose or tradeoff |
| Command entry point |
AVAddDisplayMaskTrack.swift:15 and AsyncParsableCommand |
Maps terminal arguments to one asynchronous operation. |
| Facade/orchestrator |
MovieProcessor.swift:22 and processMovie(...) |
Gives the entry point one method while retaining step ordering internally. |
| Reader-writer pipeline |
MovieProcessor.swift:183 and setupMediaDataTransfer(...) |
Streams existing samples without transcoding, alongside a generated metadata track. |
| Strategy by value |
MovieProcessor.swift:236 and maskType branch |
Selects fixed or bouncing-mask behavior without separate strategy types; compact but integer-driven. |
Naming conventions
- Types: role-oriented nouns (
MovieProcessor, VideoTrackInfo, ProcessingError).
- Methods: command phrases (
processMovie, addDisplayMaskMetadataTrack, monitorProgress).
- Booleans/status: framework-style
is... checks such as isReadable and isEnabled.
- Files: the entry file matches the executable type; the service file matches its primary class.
Architecture takeaways
- A one-use-case CLI can keep a minimal two-object boundary: parsing at the edge and media orchestration in one service.
- Private step methods make the required reader/writer setup order explicit without exposing partial operations.
- Passthrough and generated metadata share one writer session, so their lifecycle and completion are intentionally co-owned.
- The lack of protocol abstraction is proportionate to a fixed, non-interactive sample rather than evidence of a reusable library design.
Source map
| Source file |
Relevant symbols |
AVAddDisplayMaskTrack.swift |
AVAddDisplayMaskTrack, run() |
MovieProcessor.swift |
MovieProcessor, VideoTrackInfo, BouncingBoxInfo, ProcessingError |