Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Container: Mux + Demux

mediaway-container wraps eight freestanding, sans-io container cores (iso-bmff, ebml-webm, riff-wave-core, adts-core, mpeg-audio, ogg, flv, mpeg-ts-core) behind one shape: register tracks, push packets, poll bytes out — and the mirror for demux. None of it touches a file handle or socket; I/O is entirely the caller’s job.

This guide walks the MP4 (mp4::Muxer/mp4::Demuxer) shape, which every other format variant follows closely.

Muxing is a typestate

Muxer::new() starts in an Open state, where you register every track you’re going to write:

let mut muxer = mp4::Muxer::new();
let video_track = muxer.add_track(StreamInfo::Video {
    id: 0,
    codec: CodecKind::H264,
    time_base: Rational::new(1, 30),
    geometry: VideoGeometry { width: 1920, height: 1080 },
    extra_data: Bytes::new(),
})?;

muxer.begin() transitions OpenLive. That’s a real type change, not a runtime flag — once you call it, add_track is no longer callable on the result. The compiler enforces “register tracks, then stream packets,” not a convention you have to remember.

let mut muxer = muxer.begin();

Streaming packets, streaming bytes

Each Packet carries pts/dts/duration in the track’s own time base, an is_keyframe flag, and the payload bytes:

muxer.push_packet(&Packet {
    stream_id: video_track,
    pts: 0,
    dts: 0,
    duration: 1,
    is_keyframe: true,
    is_discard: false,
    payload: encoded_bytes,
})?;
muxer.flush();

let mut mp4_bytes = Vec::new();
muxer.poll_bytes(&mut mp4_bytes);

poll_bytes drains whatever the muxer has ready into a buffer you own — write it to a file, stream it over a socket, hand it to another crate. The muxer never makes that choice for you.

Demuxing is the mirror

let mut demux = mp4::Demuxer::new();
demux.push_bytes(&mp4_bytes);

for stream in demux.streams() {
    println!("stream {} — {:?}", stream.id(), stream.codec());
}

while let Some(packet) = demux.poll_packet() {
    // route by packet.stream_id
}

push_bytes can be called incrementally as bytes arrive — over a network, say — not just once with a whole buffer like the snippet above.

Extra data (avcC) for H.264

Notice the extra_data: Bytes::new() above — for H.264 tracks you can leave it empty. The muxer derives a proper avcC record from the first packet’s in-band SPS/PPS, rather than requiring the caller to pre-assemble one.

Try it

cargo run --example mux_demux_mp4

examples/container/mux_demux_mp4.rs is the full, compiling version of everything above, including audio (AAC) as a second track — see it run.

Other formats

Same marks, same shape, different framing quirks — WAV needs a known-upfront size, MP3 needs an explicit padding bit, MPEG-TS uses a fixed 90 kHz clock. See Container Support for the full format matrix, and mediaway-container/adr/0002 for why a few formats don’t fit the shared Mux/Demux trait shape exactly.