Skip to main content

oxideav_mp4/
lib.rs

1//! Pure-Rust MP4 / ISO Base Media File Format container.
2//!
3//! Scope: demuxer for probe + remux of audio and video tracks, plus a
4//! moov-at-end muxer with optional faststart (moov-at-front) rewrite.
5//! Three brand presets are registered: `mp4`, `mov`, and `ismv` — all
6//! share one implementation and only differ in their `ftyp` preset.
7
8pub mod boxes;
9pub mod cenc;
10pub mod cenc_cipher;
11pub mod codec_id;
12pub mod demux;
13pub mod frag;
14pub mod muxer;
15pub mod options;
16mod sample_entries;
17pub mod sample_groups;
18pub mod styp;
19
20pub use options::{BrandPreset, FragmentCadence, FragmentedOptions, Mp4MuxerOptions};
21
22use oxideav_core::ContainerRegistry;
23
24pub fn register_containers(reg: &mut ContainerRegistry) {
25    reg.register_demuxer("mp4", demux::open);
26    reg.register_muxer("mp4", muxer::open);
27    reg.register_muxer("mov", muxer::open_mov);
28    reg.register_muxer("ismv", muxer::open_ismv);
29    // Fragmented MP4: emit init-segment (ftyp+moov+mvex) then per-fragment
30    // styp+moof+mdat. Default cadence: every 2 seconds (see
31    // FragmentedOptions::default). Suitable for DASH / HLS / CMAF output
32    // when piped through a segment slicer.
33    reg.register_muxer("dash", muxer::open_dash);
34    reg.register_muxer("cmaf", muxer::open_dash);
35    reg.register_extension("mp4", "mp4");
36    reg.register_extension("m4a", "mp4");
37    reg.register_extension("m4v", "mp4");
38    reg.register_extension("mov", "mov");
39    reg.register_extension("3gp", "mp4");
40    reg.register_extension("ismv", "ismv");
41    reg.register_extension("m4s", "dash");
42    reg.register_probe("mp4", probe);
43}
44
45/// Install the MP4 / MOV / ISMV / DASH / CMAF containers into a
46/// [`oxideav_core::RuntimeContext`].
47///
48/// Convenience wrapper around [`register_containers`] that matches the
49/// uniform `register(&mut RuntimeContext)` entry point every sibling
50/// crate exposes.
51///
52/// Also wired into [`oxideav_meta::register_all`] via the
53/// [`oxideav_core::register!`] macro below.
54pub fn register(ctx: &mut oxideav_core::RuntimeContext) {
55    register_containers(&mut ctx.containers);
56}
57
58oxideav_core::register!("mp4", register);
59
60/// `....ftyp` at offset 0 — ISO base media file format. Some files lead
61/// with a `wide` or `free` box before `ftyp`, so accept that with a
62/// slightly lower confidence.
63fn probe(p: &oxideav_core::ProbeData) -> u8 {
64    if p.buf.len() < 8 {
65        return 0;
66    }
67    if &p.buf[4..8] == b"ftyp" {
68        return 100;
69    }
70    if p.buf.len() >= 16
71        && matches!(&p.buf[4..8], b"wide" | b"free" | b"skip")
72        && &p.buf[12..16] == b"ftyp"
73    {
74        return 90;
75    }
76    // QuickTime sometimes writes `moov` first, no `ftyp`.
77    if &p.buf[4..8] == b"moov" {
78        return 50;
79    }
80    0
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn register_via_runtime_context_installs_container() {
89        let mut ctx = oxideav_core::RuntimeContext::new();
90        register(&mut ctx);
91        assert_eq!(ctx.containers.container_for_extension("mp4"), Some("mp4"));
92        assert_eq!(ctx.containers.container_for_extension("mov"), Some("mov"));
93        assert_eq!(ctx.containers.container_for_extension("m4s"), Some("dash"));
94    }
95}