Skip to main content

mp4_atom/moov/trak/mdia/minf/stbl/stsd/mp4a/
mod.rs

1use crate::*;
2
3// We're trying not to pollute the global namespace
4pub mod esds;
5pub use esds::Esds;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Mp4a {
10    pub audio: Audio,
11    pub esds: Esds,
12    pub btrt: Option<Btrt>,
13    pub taic: Option<Taic>,
14}
15
16impl Atom for Mp4a {
17    const KIND: FourCC = FourCC::new(b"mp4a");
18
19    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
20        let audio = Audio::decode(buf)?;
21
22        let mut btrt = None;
23        let mut esds = None;
24        let mut taic = None;
25
26        // The `esds` sits directly under `mp4a` (ISO-BMFF) or nested inside a
27        // QuickTime `wave` (siDecompressionParam) box (QTFF) — FFmpeg and many
28        // camera muxers emit the latter for AAC-in-MOV.
29        while let Some(atom) = Any::decode_maybe(buf)? {
30            match atom {
31                Any::Btrt(atom) => btrt = atom.into(),
32                Any::Esds(atom) => esds = atom.into(),
33                Any::Taic(atom) => taic = atom.into(),
34                Any::Unknown(kind, body) if kind == WAVE => {
35                    decode_wave(&body, &mut esds, &mut btrt)?;
36                }
37                unknown => Self::decode_unknown(&unknown)?,
38            }
39        }
40        skip_trailing_padding(buf);
41
42        Ok(Mp4a {
43            audio,
44            esds: esds.ok_or(Error::MissingBox(Esds::KIND))?,
45            btrt,
46            taic,
47        })
48    }
49
50    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
51        self.audio.encode(buf)?;
52        self.esds.encode(buf)?;
53        self.btrt.encode(buf)?;
54        self.taic.encode(buf)?;
55
56        Ok(())
57    }
58}
59
60/// QuickTime `wave` (siDecompressionParam) container FourCC.
61const WAVE: FourCC = FourCC::new(b"wave");
62
63/// Scan a QuickTime `wave` box body for the `esds` (and `btrt`) it wraps.
64///
65/// Every other child — the `frma` format box, the nested `mp4a` terminator
66/// stub, the trailing all-zero box — is skipped by size. Those are deliberately
67/// NOT typed-decoded: the nested `mp4a` in particular is a 4-byte stub that is
68/// not a valid audio sample entry, so decoding it as one would fail.
69fn decode_wave(mut wave: &[u8], esds: &mut Option<Esds>, btrt: &mut Option<Btrt>) -> Result<()> {
70    while let Some(header) = Header::decode_maybe(&mut wave)? {
71        // A size-to-end (`None`) or over-long child terminates the scan — the
72        // esds, if present, precedes any such terminator.
73        let size = match header.size {
74            Some(size) if size <= wave.remaining() => size,
75            _ => break,
76        };
77        let mut child = wave.slice(size);
78        if header.kind == Esds::KIND {
79            *esds = Some(Esds::decode_body(&mut child)?);
80        } else if header.kind == Btrt::KIND {
81            *btrt = Some(Btrt::decode_body(&mut child)?);
82        }
83        wave.advance(size);
84    }
85    Ok(())
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_mp4a() {
94        let expected = Mp4a {
95            audio: Audio {
96                data_reference_index: 1,
97                channel_count: 2,
98                sample_size: 16,
99                sample_rate: 48000.into(),
100            },
101            esds: Esds {
102                es_desc: esds::EsDescriptor {
103                    es_id: 2,
104                    dec_config: esds::DecoderConfig {
105                        object_type_indication: 0x40,
106                        stream_type: 0x05,
107                        up_stream: 0,
108                        buffer_size_db: Default::default(),
109                        max_bitrate: 67695,
110                        avg_bitrate: 67695,
111                        dec_specific: Some(esds::DecoderSpecific {
112                            profile: 2,
113                            freq_index: 4,
114                            chan_conf: 2,
115                            raw: vec![0x12, 0x10],
116                        }),
117                    },
118                    sl_config: esds::SLConfig::default(),
119                },
120            },
121            btrt: Some(Btrt {
122                buffer_size_db: 1,
123                max_bitrate: 2,
124                avg_bitrate: 3,
125            }),
126            taic: None,
127        };
128        let mut buf = Vec::new();
129        expected.encode(&mut buf).unwrap();
130
131        let mut buf = buf.as_ref();
132        let decoded = Mp4a::decode(&mut buf).unwrap();
133        assert_eq!(decoded, expected);
134    }
135
136    // Build `[size:u32][fourcc][body]`.
137    fn atom_box(fourcc: &[u8; 4], body: &[u8]) -> Vec<u8> {
138        let mut v = Vec::with_capacity(8 + body.len());
139        v.extend_from_slice(&((8 + body.len()) as u32).to_be_bytes());
140        v.extend_from_slice(fourcc);
141        v.extend_from_slice(body);
142        v
143    }
144
145    // QuickTime (QTFF) `mp4a` sample entry: the AAC `esds` is nested inside a
146    // `wave` (siDecompressionParam) box rather than a direct child of `mp4a`.
147    // FFmpeg and camera muxers (MotionCam, etc.) emit MOV audio this way.
148    #[test]
149    fn test_mp4a_quicktime_wave() {
150        let audio = Audio {
151            data_reference_index: 1,
152            channel_count: 2,
153            sample_size: 16,
154            sample_rate: 48000.into(),
155        };
156        let esds = Esds {
157            es_desc: esds::EsDescriptor {
158                es_id: 2,
159                dec_config: esds::DecoderConfig {
160                    object_type_indication: 0x40,
161                    stream_type: 0x05,
162                    up_stream: 0,
163                    buffer_size_db: Default::default(),
164                    max_bitrate: 67695,
165                    avg_bitrate: 67695,
166                    dec_specific: Some(esds::DecoderSpecific {
167                        profile: 2,
168                        freq_index: 4,
169                        chan_conf: 2,
170                        raw: vec![0x12, 0x10],
171                    }),
172                },
173                sl_config: esds::SLConfig::default(),
174            },
175        };
176
177        let btrt = Btrt {
178            buffer_size_db: 1,
179            max_bitrate: 2,
180            avg_bitrate: 3,
181        };
182
183        // Encode the pieces the QuickTime layout wraps.
184        let mut audio_bytes = Vec::new();
185        audio.encode(&mut audio_bytes).unwrap();
186        let mut esds_box = Vec::new();
187        esds.encode(&mut esds_box).unwrap();
188        let mut btrt_box = Vec::new();
189        btrt.encode(&mut btrt_box).unwrap();
190
191        // wave { frma('mp4a'), mp4a stub (4 zero bytes — NOT a sample entry),
192        //        esds, btrt, all-zero terminator }
193        let mut wave_body = Vec::new();
194        wave_body.extend_from_slice(&atom_box(b"frma", b"mp4a"));
195        wave_body.extend_from_slice(&atom_box(b"mp4a", &[0, 0, 0, 0]));
196        wave_body.extend_from_slice(&esds_box);
197        wave_body.extend_from_slice(&btrt_box);
198        wave_body.extend_from_slice(&[0, 0, 0, 8, 0, 0, 0, 0]);
199
200        // mp4a { audio-header, wave }
201        let mut mp4a_body = audio_bytes;
202        mp4a_body.extend_from_slice(&atom_box(b"wave", &wave_body));
203        let mp4a = atom_box(b"mp4a", &mp4a_body);
204
205        let mut buf = mp4a.as_slice();
206        let decoded = Mp4a::decode(&mut buf).unwrap();
207        assert_eq!(decoded.audio, audio);
208        assert_eq!(decoded.esds, esds);
209        // A `btrt` nested alongside the esds inside `wave` is hoisted too.
210        assert_eq!(decoded.btrt, Some(btrt));
211    }
212
213    #[test]
214    fn test_mp4a_with_taic() {
215        let expected = Mp4a {
216            audio: Audio {
217                data_reference_index: 1,
218                channel_count: 2,
219                sample_size: 16,
220                sample_rate: 48000.into(),
221            },
222            esds: Esds {
223                es_desc: esds::EsDescriptor {
224                    es_id: 2,
225                    dec_config: esds::DecoderConfig {
226                        object_type_indication: 0x40,
227                        stream_type: 0x05,
228                        up_stream: 0,
229                        buffer_size_db: Default::default(),
230                        max_bitrate: 67695,
231                        avg_bitrate: 67695,
232                        dec_specific: Some(esds::DecoderSpecific {
233                            profile: 2,
234                            freq_index: 4,
235                            chan_conf: 2,
236                            raw: vec![0x12, 0x10],
237                        }),
238                    },
239                    sl_config: esds::SLConfig::default(),
240                },
241            },
242            btrt: None,
243            taic: Some(Taic {
244                time_uncertainty: 1,
245                clock_resolution: 2,
246                clock_drift_rate: 4,
247                clock_type: ClockType::CanSync,
248            }),
249        };
250        let mut buf = Vec::new();
251        expected.encode(&mut buf).unwrap();
252
253        let mut buf = buf.as_ref();
254        let decoded = Mp4a::decode(&mut buf).unwrap();
255        assert_eq!(decoded, expected);
256    }
257}