Expand description
Pure-Rust fragmented-MP4 (fMP4) muxer for the built-in recorder.
Hand-rolled rather than pulled in as a dependency for two reasons: libavformat would be a hard
runtime dependency of every build, and the pure-Rust mp4 crates only write moov-trailing
progressive files, which lose everything on a crash. Fragmented MP4 needs no trailer and no
seeking — each moof+mdat pair is self-contained — so a file truncated by a crash or SIGKILL
stays playable up to the last fragment, and the writer works on any Write sink.
Timestamps are caller-supplied wall-clock microseconds (damage-driven capture emits sparse,
irregular frames), carried at a 90 kHz track timescale with one sample per fragment: tfdt
anchors every sample at its true capture time, so variable framerate needs no constant-rate
lie. The sample duration is only known once the NEXT frame arrives, so one sample is always
buffered and flushed a frame behind (a clean stop closes it with the median observed
duration); on SIGKILL at most that one buffered frame is lost — every fragment already
written remains playable.
Codec support is deliberately split: [annexb_to_sample] and the H.264-specific parameter-set
capture live in H264SampleBuilder, while the fragment/box writer below is codec-agnostic
(bytes + sync flag + timestamps + a ready-made stsd sample entry). HEVC or AV1 recording
later means a new sample builder emitting an hvc1/av01 entry, not a new muxer.
An optional Opus audio track (track 2) takes packets with their decode time on the 48 kHz clock and each one’s duration from its own TOC byte; they go out in fragments of their own, so a still desktop that yields no video fragment still keeps the file growing.
Structs§
- Audio
Track Config - The audio track’s
stsdsample entry. - BitReader 🔒
- MSB-first bit reader over an unescaped RBSP, with Exp-Golomb decode for SPS parsing.
- Built
Sample - One converted access unit ready for the fragment writer.
- Fragment
Writer - Codec-agnostic fMP4 fragment writer:
ftyp+moovonce, then onemoof+mdatpair per video sample, each anchored at its wall-clock decode time viatfdt, and one per batch of audio samples. - H264
Sample Builder - H.264-specific front end: captures SPS/PPS from the stream, gates output on the first
IDR, converts Annex-B access units to AVCC samples, and builds the
avc1sample entry. - Mp4Stats
- Aggregate counters reported when the writer finishes.
- Pending
Sample 🔒 - One buffered access unit awaiting its duration (known when the next one arrives).
- Track
Config - A codec’s contribution to the init segment: its
stsdsample entry (with decoder configuration record) plus the display dimensions fortkhd.
Enums§
- Trak
Media 🔒 - The
tkhdfields that differ by media: a video track states its display size, an audio track its volume.
Constants§
- AUDIO_
FLUSH_ 🔒SAMPLES - Audio packets buffered before they go out as a fragment of their own: half a second of 20 ms packets.
- DEFAULT_
LAST_ 🔒DURATION - Fallback duration for the final buffered sample when only one frame was ever written (no observed inter-frame delta to take a median of): 1/30 s.
- MATRIX_
IDENTITY 🔒 - The unity transformation matrix
mvhdandtkhdcarry: 0x00010000, 0x00010000, and 0x40000000 on the diagonal (16.16 fixed point for the first two, 2.30 for the last). - OPUS_
TIMESCALE - Opus in ISOBMFF runs its track at Opus’s own 48 kHz whatever the input rate.
- TIMESCALE 🔒
- 90 kHz: the conventional H.264 track timescale, exactly representing common frame intervals.
Functions§
- dinf 🔒
- The data reference every track shares: an empty
urlentry flagged self-contained. - mk_box 🔒
- mk_
full_ 🔒box - opus_
packet_ samples - The 48 kHz samples an Opus packet decodes to, from its TOC byte: the configuration’s frame duration times the frame count its code carries.
- opus_
sample_ entry - The
Opussample entry with itsdOpsrecord, the head’s own fields. - parse_
sps_ dimensions - Coded frame dimensions parsed out of an H.264 SPS NAL (with its NAL header byte).
- split_
annexb - Split an Annex-B elementary stream into NAL payloads (start codes removed, emulation prevention bytes kept — the RBSP layer is only unescaped where a parser needs it).
- stbl 🔒
- A sample table holding only the sample entry: every sample lives in a fragment.
- trak 🔒
- One
trak:tkhd(enabled, in_movie) with the display size or, for audio, the volume;mdhdattimescale; the handler; then the media header given and the sample table. - trex 🔒
- A
trexwhose fragments state their own durations, sizes, and flags unlessdefault_durationis set, which the audio track’s fixed packet length is. - unescape_
rbsp 🔒 - Unescape an H.264 RBSP: drop the emulation-prevention byte from every
00 00 03run.