Skip to main content

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

1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq, Default)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Esds {
6    pub es_desc: EsDescriptor,
7}
8
9impl AtomExt for Esds {
10    type Ext = ();
11
12    const KIND_EXT: FourCC = FourCC::new(b"esds");
13
14    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: ()) -> Result<Self> {
15        let mut es_desc = None;
16
17        while let Some(desc) = Descriptor::decode_maybe(buf)? {
18            match desc {
19                Descriptor::EsDescriptor(desc) => es_desc = Some(desc),
20                Descriptor::Unknown(tag, _) => {
21                    tracing::warn!("unknown descriptor: {:02X}", tag)
22                }
23                _ => return Err(Error::UnexpectedDescriptor(desc.tag())),
24            }
25        }
26
27        Ok(Esds {
28            es_desc: es_desc.ok_or(Error::MissingDescriptor(EsDescriptor::TAG))?,
29        })
30    }
31
32    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<()> {
33        encode_descriptor(EsDescriptor::TAG, &self.es_desc, buf)
34    }
35}
36
37/// Decode a descriptor body of `size` bytes, tolerating trailing bytes the
38/// specific descriptor doesn't consume.
39///
40/// MPEG-4 (ISO/IEC 14496-1) descriptors are length-prefixed and forward-
41/// compatible: the size field is authoritative and a parser skips to the
42/// declared end, ignoring extension fields (or writer padding) it doesn't
43/// understand. Unlike [`Decode::decode_exact`] this does not `ShortRead` on the
44/// leftover — e.g. a 2-byte `SLConfigDescriptor` (`predefined` + a trailing
45/// byte) decodes instead of failing the whole `esds`. The `size` slice still
46/// bounds the read, so a descriptor can never run past its declared length.
47fn decode_descriptor_body<T: Decode, B: Buf>(buf: &mut B, size: usize) -> Result<T> {
48    if buf.remaining() < size {
49        return Err(Error::OutOfBounds);
50    }
51    let mut inner = buf.slice(size);
52    let res = T::decode(&mut inner)?;
53    buf.advance(size);
54    Ok(res)
55}
56
57/// Encode a typed descriptor — tag, base-128 length prefix, then the body —
58/// borrowing `body` rather than taking ownership, so a caller holding a `&T`
59/// need not clone it into a [`Descriptor`] just to serialize it.
60fn encode_descriptor<T: Encode, B: BufMut>(tag: u8, body: &T, buf: &mut B) -> Result<()> {
61    tag.encode(buf)?;
62
63    // TODO This is inefficient; we could compute the size upfront.
64    let mut tmp = Vec::new();
65    body.encode(&mut tmp)?;
66
67    // Base-128 length prefix, most-significant 7-bit group first (ISO/IEC
68    // 14496-1 §8.3.3, matching this crate's `size << 7 | byte` decoder), and
69    // always at least one byte so a zero-length body still carries its
70    // mandatory `0x00` size prefix.
71    let size = tmp.len() as u32;
72    let mut groups = 1;
73    let mut s = size >> 7;
74    while s > 0 {
75        groups += 1;
76        s >>= 7;
77    }
78    for i in (0..groups).rev() {
79        let mut b = ((size >> (7 * i)) & 0x7F) as u8;
80        if i > 0 {
81            b |= 0x80;
82        }
83        b.encode(buf)?;
84    }
85
86    tmp.encode(buf)
87}
88
89macro_rules! descriptors {
90    ($($name:ident,)*) => {
91        #[derive(Debug, Clone, PartialEq, Eq)]
92        pub enum Descriptor {
93            $(
94                $name($name),
95            )*
96            Unknown(u8, Vec<u8>),
97        }
98
99        impl Decode for Descriptor {
100            fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
101                let tag = u8::decode(buf)?;
102
103                let mut size: u32 = 0;
104                for _ in 0..4 {
105                    let b = u8::decode(buf)?;
106                    size = (size << 7) | (b & 0x7F) as u32;
107                    if b & 0x80 == 0 {
108                        break;
109                    }
110                }
111
112                match tag {
113                    $(
114                        $name::TAG => Ok(decode_descriptor_body::<$name, _>(buf, size as _)?.into()),
115                    )*
116                    _ => Ok(Descriptor::Unknown(tag, Vec::decode_exact(buf, size as _)?)),
117                }
118            }
119        }
120
121        impl DecodeMaybe for Descriptor {
122            fn decode_maybe<B: Buf>(buf: &mut B) -> Result<Option<Self>> {
123                match buf.has_remaining() {
124                    true => Descriptor::decode(buf).map(Some),
125                    false => Ok(None),
126                }
127            }
128        }
129
130        impl Encode for Descriptor {
131            fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
132                match self {
133                    $(
134                        Descriptor::$name(t) => encode_descriptor($name::TAG, t, buf),
135                    )*
136                    Descriptor::Unknown(tag, data) => encode_descriptor(*tag, data, buf),
137                }
138            }
139        }
140
141        impl Descriptor {
142            pub const fn tag(&self) -> u8 {
143                match self {
144                    $(
145                        Descriptor::$name(_) => $name::TAG,
146                    )*
147                    Descriptor::Unknown(tag, _) => *tag,
148                }
149            }
150        }
151
152        $(
153            impl From<$name> for Descriptor {
154                fn from(desc: $name) -> Self {
155                    Descriptor::$name(desc)
156                }
157            }
158        )*
159    };
160}
161
162descriptors! {
163    EsDescriptor,
164    DecoderConfig,
165    DecoderSpecific,
166    SLConfig,
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Default)]
170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
171pub struct EsDescriptor {
172    pub es_id: u16,
173
174    pub dec_config: DecoderConfig,
175    pub sl_config: SLConfig,
176}
177
178impl EsDescriptor {
179    pub const TAG: u8 = 0x03;
180}
181
182impl Decode for EsDescriptor {
183    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
184        let es_id = u16::decode(buf)?;
185        u8::decode(buf)?; // XXX flags must be 0
186
187        let mut dec_config = None;
188        let mut sl_config = None;
189
190        while let Some(desc) = Descriptor::decode_maybe(buf)? {
191            match desc {
192                Descriptor::DecoderConfig(desc) => dec_config = Some(desc),
193                Descriptor::SLConfig(desc) => sl_config = Some(desc),
194                Descriptor::Unknown(tag, _) => tracing::warn!("unknown descriptor: {:02X}", tag),
195                desc => return Err(Error::UnexpectedDescriptor(desc.tag())),
196            }
197        }
198
199        Ok(EsDescriptor {
200            es_id,
201            dec_config: dec_config.ok_or(Error::MissingDescriptor(DecoderConfig::TAG))?,
202            sl_config: sl_config.ok_or(Error::MissingDescriptor(SLConfig::TAG))?,
203        })
204    }
205}
206
207impl Encode for EsDescriptor {
208    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
209        self.es_id.encode(buf)?;
210        0u8.encode(buf)?;
211
212        encode_descriptor(DecoderConfig::TAG, &self.dec_config, buf)?;
213        encode_descriptor(SLConfig::TAG, &self.sl_config, buf)?;
214
215        Ok(())
216    }
217}
218
219#[derive(Debug, Clone, PartialEq, Eq, Default)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221pub struct DecoderConfig {
222    pub object_type_indication: u8,
223    pub stream_type: u8,
224    pub up_stream: u8,
225    pub buffer_size_db: u24,
226    pub max_bitrate: u32,
227    pub avg_bitrate: u32,
228    /// The `DecoderSpecificInfo` (tag 0x05). ISO/IEC 14496-1 makes this
229    /// child optional ("if available"): a stream whose config can be derived
230    /// in-band (e.g. AAC carried with ADTS headers) legitimately omits it, so a
231    /// missing tag 5 must not fail the whole `esds`. `None` means the container
232    /// carried no out-of-band config.
233    pub dec_specific: Option<DecoderSpecific>,
234}
235
236impl DecoderConfig {
237    pub const TAG: u8 = 0x04;
238}
239
240impl Decode for DecoderConfig {
241    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
242        let object_type_indication = u8::decode(buf)?;
243        let byte_a = u8::decode(buf)?;
244        let stream_type = (byte_a & 0xFC) >> 2;
245        let up_stream = byte_a & 0x02;
246        let buffer_size_db = u24::decode(buf)?;
247        let max_bitrate = u32::decode(buf)?;
248        let avg_bitrate = u32::decode(buf)?;
249
250        let mut dec_specific = None;
251
252        while let Some(desc) = Descriptor::decode_maybe(buf)? {
253            match desc {
254                Descriptor::DecoderSpecific(desc) => dec_specific = Some(desc),
255                Descriptor::Unknown(tag, _) => tracing::warn!("unknown descriptor: {:02X}", tag),
256                desc => return Err(Error::UnexpectedDescriptor(desc.tag())),
257            }
258        }
259
260        Ok(DecoderConfig {
261            object_type_indication,
262            stream_type,
263            up_stream,
264            buffer_size_db,
265            max_bitrate,
266            avg_bitrate,
267            dec_specific,
268        })
269    }
270}
271
272impl Encode for DecoderConfig {
273    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
274        self.object_type_indication.encode(buf)?;
275        ((self.stream_type << 2) + (self.up_stream & 0x02) + 1).encode(buf)?; // 1 reserved
276        self.buffer_size_db.encode(buf)?;
277        self.max_bitrate.encode(buf)?;
278        self.avg_bitrate.encode(buf)?;
279
280        if let Some(dec_specific) = &self.dec_specific {
281            encode_descriptor(DecoderSpecific::TAG, dec_specific, buf)?;
282        }
283
284        Ok(())
285    }
286}
287
288#[derive(Debug, Clone, PartialEq, Eq, Default)]
289#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
290pub struct DecoderSpecific {
291    pub profile: u8,
292    pub freq_index: u8,
293    pub chan_conf: u8,
294    /// The complete `DecoderSpecificInfo` payload. For AAC this is the
295    /// AudioSpecificConfig — the `profile` / `freq_index` / `chan_conf` fields
296    /// above are a parsed view of its prefix. For a non-AAC object type (e.g.
297    /// the MPEG-4 Visual VOS/VOL config carried by an `mp4v` `esds`) the fields
298    /// are not meaningful, but the bytes are preserved verbatim so the
299    /// descriptor round-trips faithfully. Empty on a hand-constructed AAC
300    /// config, in which case [`Encode`] re-derives the 2-byte AudioSpecificConfig
301    /// from the fields.
302    pub raw: Vec<u8>,
303}
304
305impl DecoderSpecific {
306    pub const TAG: u8 = 0x05;
307}
308
309/// Best-effort parse of the AAC AudioSpecificConfig prefix (`audioObjectType`,
310/// `samplingFrequencyIndex`, `channelConfiguration`) from the raw payload.
311/// Returns zeros when the payload is too short to parse (e.g. a non-AAC config);
312/// the raw bytes remain the source of truth for round-tripping.
313fn parse_audio_specific_config(raw: &[u8]) -> (u8, u8, u8) {
314    if raw.len() < 2 {
315        return (0, 0, 0);
316    }
317    let byte_a = raw[0];
318    let byte_b = raw[1];
319
320    let mut profile = byte_a >> 3;
321    if profile == 31 {
322        // Escape: 6-bit audioObjectTypeExt = byte_a[2:0] << 3 | byte_b[7:5].
323        profile = 32 + (((byte_a & 7) << 3) | (byte_b >> 5));
324    }
325
326    let freq_index = if profile > 31 {
327        (byte_b >> 1) & 0x0F
328    } else {
329        ((byte_a & 0x07) << 1) + (byte_b >> 7)
330    };
331
332    let chan_conf = if freq_index == 15 {
333        // The 24-bit explicit sample rate precedes the channel config.
334        if raw.len() >= 5 {
335            let sample_rate =
336                (u32::from(raw[2]) << 16) | (u32::from(raw[3]) << 8) | u32::from(raw[4]);
337            ((sample_rate >> 4) & 0x0F) as u8
338        } else {
339            0
340        }
341    } else if profile > 31 {
342        if raw.len() >= 3 {
343            // 4-bit channelConfiguration = byte_b[0] << 3 | raw[2][7:5].
344            ((byte_b & 1) << 3) | (raw[2] >> 5)
345        } else {
346            0
347        }
348    } else {
349        (byte_b >> 3) & 0x0F
350    };
351
352    (profile, freq_index, chan_conf)
353}
354
355impl Decode for DecoderSpecific {
356    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
357        // Capture the complete payload (the descriptor body is already
358        // size-bounded by `decode_descriptor_body`) so any object type
359        // round-trips; the AAC fields are a best-effort parse of its prefix.
360        let raw = Vec::decode(buf)?;
361        let (profile, freq_index, chan_conf) = parse_audio_specific_config(&raw);
362
363        Ok(DecoderSpecific {
364            profile,
365            freq_index,
366            chan_conf,
367            raw,
368        })
369    }
370}
371
372impl Encode for DecoderSpecific {
373    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
374        if self.raw.is_empty() {
375            // Hand-constructed AAC config with no preserved bytes: re-derive the
376            // 2-byte AudioSpecificConfig from the fields.
377            ((self.profile << 3) + (self.freq_index >> 1)).encode(buf)?;
378            ((self.freq_index << 7) + (self.chan_conf << 3)).encode(buf)?;
379        } else {
380            // Emit the preserved payload verbatim (faithful for any object type).
381            self.raw.encode(buf)?;
382        }
383
384        Ok(())
385    }
386}
387
388#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
389#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
390pub struct SLConfig {}
391
392impl SLConfig {
393    pub const TAG: u8 = 0x06;
394}
395
396impl Decode for SLConfig {
397    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
398        u8::decode(buf)?; // pre-defined
399        Ok(SLConfig {})
400    }
401}
402
403impl Encode for SLConfig {
404    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
405        2u8.encode(buf)?; // pre-defined
406        Ok(())
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use super::*;
413
414    // An `esds` whose `SLConfigDescriptor` (tag 0x06) declares 2 bytes — a
415    // `predefined` value plus a trailing byte — instead of the usual 1.
416    // `SLConfig::decode` reads only `predefined`, so the strict `decode_exact`
417    // used to `ShortRead` and fail the whole `esds` with `UnderDecode(esds)`.
418    // ISO 14496-1 makes the descriptor length authoritative, so the trailing
419    // byte must be skipped. Descriptor tree: EsDescriptor(0x03) → DecoderConfig
420    // (0x04) → DecoderSpecific(0x05, AAC-LC 48 kHz) + SLConfig(0x06, 2 bytes).
421    const ESDS_2BYTE_SLCONFIG: &[u8] = &[
422        0x00, 0x00, 0x00, 0x28, b'e', b's', b'd', b's', // esds box, size 40
423        0x00, 0x00, 0x00, 0x00, // version + flags
424        0x03, 0x1a, 0x00, 0x01, 0x00, // EsDescriptor: size 26, es_id 1, flags 0
425        0x04, 0x11, 0x40, 0x15, // DecoderConfig: size 17, oti 0x40, stream/up 0x15
426        0x00, 0x00, 0x00, // buffer_size_db
427        0x00, 0x00, 0x00, 0x00, // max_bitrate
428        0x00, 0x00, 0x00, 0x00, // avg_bitrate
429        0x05, 0x02, 0x11, 0x90, // DecoderSpecific: size 2 (profile 2, freq 3, chan 2)
430        0x06, 0x02, 0x02, 0x15, // SLConfig: size 2 = predefined 0x02 + a trailing byte
431    ];
432
433    #[test]
434    fn test_esds_two_byte_slconfig() {
435        let esds =
436            Esds::decode(&mut &ESDS_2BYTE_SLCONFIG[..]).expect("2-byte SLConfig must be tolerated");
437        let dec = esds
438            .es_desc
439            .dec_config
440            .dec_specific
441            .expect("DecoderSpecificInfo present");
442        assert_eq!(dec.profile, 2, "AAC-LC");
443        assert_eq!(dec.freq_index, 3, "48 kHz");
444    }
445
446    // A `DecoderConfigDescriptor` (tag 0x04) with NO `DecoderSpecificInfo`
447    // (tag 0x05) child — valid per ISO/IEC 14496-1, where the DecSpecificInfo
448    // is optional (e.g. AAC whose config is carried in-band via ADTS headers).
449    // Before the fix the mandatory `MissingDescriptor(0x05)` rejected the whole
450    // `esds`; now the field decodes to `None`. Descriptor tree: EsDescriptor
451    // (0x03) → DecoderConfig(0x04, no tag-5 child) + SLConfig(0x06).
452    const ESDS_NO_DEC_SPECIFIC: &[u8] = &[
453        0x00, 0x00, 0x00, 0x23, b'e', b's', b'd', b's', // esds box, size 35
454        0x00, 0x00, 0x00, 0x00, // version + flags
455        0x03, 0x15, 0x00, 0x01, 0x00, // EsDescriptor: size 21, es_id 1, flags 0
456        0x04, 0x0d, 0x40, 0x15, // DecoderConfig: size 13, oti 0x40 (AAC), stream/up 0x15
457        0x00, 0x00, 0x00, // buffer_size_db
458        0x00, 0x00, 0x00, 0x00, // max_bitrate
459        0x00, 0x00, 0x00, 0x00, // avg_bitrate
460        0x06, 0x01, 0x02, // SLConfig: size 1, predefined 0x02
461    ];
462
463    #[test]
464    fn test_esds_missing_dec_specific() {
465        let esds = Esds::decode(&mut &ESDS_NO_DEC_SPECIFIC[..])
466            .expect("a DecoderConfig without a DecoderSpecificInfo must be tolerated");
467        assert_eq!(esds.es_desc.dec_config.object_type_indication, 0x40);
468        assert!(
469            esds.es_desc.dec_config.dec_specific.is_none(),
470            "no tag-5 child decodes to None, not an error"
471        );
472
473        // And it round-trips: a `None` dec_specific emits no tag-5 descriptor.
474        let mut buf = Vec::new();
475        esds.encode(&mut buf).unwrap();
476        let again = Esds::decode(&mut buf.as_slice()).unwrap();
477        assert_eq!(again, esds);
478    }
479
480    // A zero-length descriptor must still carry its mandatory 1-byte length
481    // prefix (`[tag, 0x00]`). Emitting just the tag would let the following
482    // sibling's first byte be misread as this descriptor's length, corrupting
483    // the rest of the tree.
484    #[test]
485    fn test_unknown_descriptor_empty_body_roundtrips() {
486        let mut buf = Vec::new();
487        Descriptor::Unknown(0x7F, Vec::new())
488            .encode(&mut buf)
489            .unwrap();
490        // A second descriptor: if the empty body dropped its length byte, this
491        // one would be swallowed and fail to decode.
492        Descriptor::from(SLConfig {}).encode(&mut buf).unwrap();
493
494        assert_eq!(&buf[..2], &[0x7F, 0x00], "empty body still emits a length");
495
496        let mut cur = buf.as_slice();
497        assert_eq!(
498            Descriptor::decode(&mut cur).unwrap(),
499            Descriptor::Unknown(0x7F, Vec::new())
500        );
501        assert_eq!(
502            Descriptor::decode(&mut cur).unwrap().tag(),
503            SLConfig::TAG,
504            "the following descriptor is intact"
505        );
506    }
507
508    // A body >= 128 bytes needs a multi-byte base-128 length. It must be emitted
509    // most-significant group first, so the decoder (which accumulates
510    // `size << 7 | byte`) reads it back unchanged. 200 => `[0x81, 0x48]`.
511    #[test]
512    fn test_descriptor_multibyte_length_roundtrips() {
513        let body = vec![0xABu8; 200];
514        let mut buf = Vec::new();
515        Descriptor::Unknown(0x7F, body.clone())
516            .encode(&mut buf)
517            .unwrap();
518        assert_eq!(
519            &buf[..3],
520            &[0x7F, 0x81, 0x48],
521            "length 200, MSB group first"
522        );
523
524        let mut cur = buf.as_slice();
525        assert_eq!(
526            Descriptor::decode(&mut cur).unwrap(),
527            Descriptor::Unknown(0x7F, body)
528        );
529    }
530
531    // AudioSpecificConfig with the 5-bit audioObjectType == 31 escape. The real
532    // AOT is `32 + audioObjectTypeExt`, a 6-bit field split as byte_a[2:0]<<3 |
533    // byte_b[7:5]; the 4-bit channelConfiguration is byte_b[0]<<3 | raw[2][7:5].
534    // These bytes encode AOT 42, samplingFrequencyIndex 4, channelConfig 9.
535    #[test]
536    fn test_asc_escape_bit_packing() {
537        let (profile, freq_index, chan_conf) = parse_audio_specific_config(&[0xF9, 0x49, 0x20]);
538        assert_eq!(profile, 42, "32 + ((0xF9 & 7) << 3 | 0x49 >> 5)");
539        assert_eq!(freq_index, 4, "(0x49 >> 1) & 0x0F");
540        assert_eq!(chan_conf, 9, "((0x49 & 1) << 3) | (0x20 >> 5)");
541    }
542}