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