Sample CodemacOS 11.0+Reviewed 2026-07-21View on Apple Developer

Writing fragmented MPEG-4 files for HTTP Live Streaming

At a glance

Item Summary
Purpose Convert one movie into fragmented MPEG-4 initialization/media segments and an HLS media playlist.
App architecture A command-line orchestrator wires track loading, a reader/writer segment producer, and two Combine consumers for files and playlist text.
Main patterns Reactive fan-out pipeline, AVAssetWriter delegate adapter, configuration/value objects, explicit lifetime tokens.
Project style One file per pipeline stage: configuration, loading, segment generation, index reduction, errors, and top-level composition.

Project structure

fmp4writer/
├── main.swift
├── Configuration.swift
├── TrackLoading.swift
├── SegmentGeneration.swift
├── IndexFileGeneration.swift
└── Error.swift

The flat folder is architectural: files are named for sequential pipeline responsibilities rather than UI features or layers.

Overall architecture

Reference code

fmp4writer/main.swift:41 — top-level composition fans one segment stream into two sinks (abridged).

let segmentGenerator = PassthroughSubject<Segment, Error>()
let indexFileGenerator = segmentGenerator.reduceToIndexFile(using: config)

let segmentFileWriter = segmentGenerator.tryMap { segment in
    let name = segment.fileName(forPrefix: config.segmentFileNamePrefix)
    try segment.data.write(to: outputDirectoryURL.appendingPathComponent(name))
}

segmentAndIndexFileWriter = segmentFileWriter.merge(with: indexFileWriter)
    .sink(receiveCompletion: { completion in /* finish */ }, receiveValue: {})

This is a dataflow-oriented command-line architecture: main.swift composes stages, while stage files own transformations.

Ownership and state

Ownership evidence

fmp4writer/main.swift:20 and fmp4writer/SegmentGeneration.swift:35 — top-level tokens retain the async graph; ReaderWriter owns AVFoundation components (abridged).

var segmentGenerationToken: Any?
var segmentAndIndexFileWriter: AnyCancellable?

class ReaderWriter<S>: NSObject, AVAssetWriterDelegate
where S: Subject, S.Output == Segment, S.Failure == Error {
    private let assetReader: AVAssetReader
    private let assetWriter: AVAssetWriter
    private let subject: S
    private let audioReaderOutput: AVAssetReaderOutput
    private let videoReaderOutput: AVAssetReaderOutput
}
Owner Object or state Relationship Mutation authority
Top-level main.swift Configuration, dispatch group, completion, tokens Process-lifetime orchestration Completion sink writes result and leaves the group.
ReaderWriter Reader/writer, inputs/outputs, index Creates and retains Private methods and writer delegate callback.
PassthroughSubject Segment event stream Created in main, supplied to producer/consumers ReaderWriter sends; downstream operators observe.
IndexFileState Playlist reduction accumulator Value copied by Combine scan Reducer alone produces new state.

Class and protocol design

Reference code

fmp4writer/SegmentGeneration.swift:19 — the producer is generic over Combine’s capability rather than a concrete subject (abridged).

func generateSegments<S>(sourceMedia: SourceMedia,
                         configuration: FMP4WriterConfiguration,
                         subject: S) -> Any?
where S: Subject, S.Output == Segment, S.Failure == Error {
    let readerWriter = try ReaderWriter(
        sourceMedia: sourceMedia,
        configuration: configuration,
        subject: subject
    )
    readerWriter.start()
    return readerWriter
}

The generic boundary is protocol-oriented but narrow: callers choose any matching Subject; ReaderWriter adapts AVAssetWriterDelegate callbacks into Segment values.

Access control

Symbol Access Verified effect Likely rationale
ReaderWriter AV objects, subjects, completion state private Other files cannot disturb read/write lifecycle. Maintains paired reader/writer and completion invariants.
Configuration and value-object fields internal immutable let Pipeline stages can read fixed settings but not mutate them. Small single-target configuration contract.
Stage functions/types internal default main.swift composes them across files. The executable target is the module boundary.
Top-level tokens file top-level internal variables Keep async producer/subscription alive until group completion. Lifetime retention is explicit, not hidden in a singleton.

fmp4writer/SegmentGeneration.swift:35 (abridged):

class ReaderWriter<S>: NSObject, AVAssetWriterDelegate
where S: Subject, S.Output == Segment, S.Failure == Error {
    private let assetReader: AVAssetReader
    private let assetWriter: AVAssetWriter
    private let audioDone = PassthroughSubject<Void, Error>()
    private let videoDone = PassthroughSubject<Void, Error>()
    private var done: AnyCancellable?
    private var segmentIndex = 0
}

Logic ownership and placement

Logic Owning type or file Placement rationale
CLI parsing and pipeline wiring fmp4writer/main.swift Composition root only; media details stay in stages.
Track loading/validation fmp4writer/TrackLoading.swift Produces validated SourceMedia.
Read/transcode/segment callbacks fmp4writer/SegmentGeneration.swift Keeps AVAssetReader/Writer lifecycle together.
Playlist accumulation fmp4writer/IndexFileGeneration.swift Pure-ish Combine reduction from segment reports to text.
Encoding constants fmp4writer/Configuration.swift One immutable object feeds every stage.

Design patterns

Pattern Source evidence Purpose or tradeoff
Reactive fan-out pipeline fmp4writer/main.swift:43 One segment stream drives file output and playlist generation.
Delegate adapter fmp4writer/SegmentGeneration.swift:124 Converts AVAssetWriter segment callbacks into typed values.
Configuration object fmp4writer/Configuration.swift:11 Groups paths, encoding, segmentation, and timing settings.
Value object fmp4writer/SegmentGeneration.swift:11 Moves segment bytes/report/identity between producer and consumers.
Parallel workers + join fmp4writer/SegmentGeneration.swift:74 Audio/video transfer separately; Combine completion finishes only after both.

Naming conventions

  • Files and functions name pipeline stages (TrackLoading, generateSegments, reduceToIndexFile).
  • Data carriers are nouns (SourceMedia, Segment, IndexFileState, FMP4WriterConfiguration).
  • ReaderWriter names its paired framework ownership; private properties retain framework vocabulary.
  • Configuration fields describe output artifacts (segmentFileNamePrefix, indexFileName) and constraints.

Architecture takeaways

  • Make the executable entry point a composition root and keep transformations in stage files.
  • Adapt framework delegate callbacks into a typed stream when multiple downstream consumers need the same events.
  • Retain asynchronous producers and subscriptions explicitly for their required lifetime.
  • Use private ownership inside the stateful reader/writer and immutable values between stages.

Source map

Source file Relevant symbols
fmp4writer/main.swift Composition root and output sinks
fmp4writer/Configuration.swift FMP4WriterConfiguration
fmp4writer/TrackLoading.swift SourceMedia, track validation
fmp4writer/SegmentGeneration.swift Segment, ReaderWriter, delegate adapter
fmp4writer/IndexFileGeneration.swift Playlist reducer/state

Source caveat: the archive uses callback-based AVFoundation loading and Combine. This document describes that source architecture without inferring a newer implementation.