Skip to main content

dvb_si/descriptors/
aac.rs

1//! AAC Descriptor — ETSI EN 300 468 Annex H, Table H.1 (tag 0x7C).
2//!
3//! Carried in the PMT ES_info loop to identify MPEG-4 AAC / HE-AAC / HE-AAC v2
4//! audio. Per the SI PDF (etsi_en_300_468_v01.19.01, Annex H §H.2.1, Table H.1,
5//! PDF pp. 196-197) the body is:
6//!   profile_and_level(8)
7//!   if (descriptor_length > 1) {
8//!     AAC_type_flag(1) + SAOC_DE_flag(1) + reserved_zero_future_use(6)
9//!     if (AAC_type_flag == 1) AAC_type(8)
10//!     additional_info_byte(8*N)
11//!   }
12//! The optional block (everything after profile_and_level) is modelled as an
13//! `Option<AacExtension>`: `None` means descriptor_length == 1.
14
15use super::descriptor_body;
16use crate::error::{Error, Result};
17use dvb_common::{Parse, Serialize};
18
19/// Descriptor tag for AAC_descriptor.
20pub const TAG: u8 = 0x7C;
21const HEADER_LEN: usize = 2;
22
23const FLAG_AAC_TYPE: u8 = 0x80;
24const FLAG_SAOC_DE: u8 = 0x40;
25/// reserved_zero_future_use(6) — the spec mandates these are zero.
26const RESERVED_ZERO_MASK: u8 = 0x3F;
27
28/// Decoded AAC component_type — ETSI EN 300 468 Table 26 / Annex H.
29///
30/// Returned by [`AacExtension::decoded_component_type`] when `aac_type` is
31/// `Some`.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct AacComponentType {
34    /// Single mono channel.
35    pub mono: bool,
36    /// Stereo.
37    pub stereo: bool,
38    /// Surround sound (> 2 channels).
39    pub surround: bool,
40    /// Audio description for the visually impaired.
41    pub visually_impaired: bool,
42    /// Audio for the hard of hearing.
43    pub hard_of_hearing: bool,
44    /// Supplementary audio (receiver-mix or broadcast-mix).
45    pub supplementary: bool,
46    /// HE-AAC v2 (SBR + PS) rather than HE-AAC v1.
47    pub v2: bool,
48    /// SAOC-DE ancillary data present.
49    pub saoc_de: bool,
50}
51
52impl AacComponentType {
53    /// Returns a human-readable description of the AAC component type.
54    #[must_use]
55    /// Returns a human-readable name.
56    pub fn name(&self) -> &'static str {
57        match (
58            self.mono,
59            self.stereo,
60            self.surround,
61            self.visually_impaired,
62            self.hard_of_hearing,
63            self.supplementary,
64            self.v2,
65            self.saoc_de,
66        ) {
67            (true, false, false, false, false, false, false, false) => {
68                "HE-AAC audio, single mono channel"
69            }
70            (false, true, false, false, false, false, false, false) => "HE-AAC audio, stereo",
71            (false, false, true, false, false, false, false, false) => {
72                "HE-AAC audio, surround sound"
73            }
74            (_, _, _, true, false, false, false, false) => {
75                "HE-AAC audio description for the visually impaired"
76            }
77            (_, _, _, false, true, false, false, false) => "HE-AAC audio for the hard of hearing",
78            (_, _, _, false, false, true, false, false) => {
79                "HE-AAC receiver-mix supplementary audio"
80            }
81            (false, true, false, false, false, false, true, false) => "HE-AAC v2 audio, stereo",
82            (_, _, _, true, false, false, true, false) => {
83                "HE-AAC v2 audio description for the visually impaired"
84            }
85            (_, _, _, false, true, false, true, false) => "HE-AAC v2 audio for the hard of hearing",
86            (_, _, _, false, false, true, true, false) => {
87                "HE-AAC v2 receiver-mix supplementary audio"
88            }
89            (_, _, _, true, false, true, false, false) => {
90                "HE-AAC receiver-mix audio description for the visually impaired"
91            }
92            (_, _, _, true, false, true, true, false) => {
93                "HE-AAC v2 receiver-mix audio description for the visually impaired"
94            }
95            (_, _, _, _, _, _, _, true) => "HE-AAC or HE-AAC v2 with SAOC-DE ancillary data",
96            _ => "unknown HE-AAC component type",
97        }
98    }
99}
100
101/// Optional extension carried when descriptor_length > 1.
102#[derive(Debug, Clone, PartialEq, Eq)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize))]
104#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
105pub struct AacExtension<'a> {
106    /// SAOC_DE_flag — embedded SAOC-DE parametric data present (Table H.2).
107    pub saoc_de_flag: bool,
108    /// AAC_type — component_type when stream_content is 0x06 (Table 26).
109    /// `Some` iff AAC_type_flag was set.
110    pub aac_type: Option<u8>,
111    /// Trailing additional_info bytes.
112    pub additional_info: &'a [u8],
113}
114
115impl AacExtension<'_> {
116    /// Decodes the `aac_type` field (when present) into a structured
117    /// [`AacComponentType`] per ETSI EN 300 468 Table 26 / Annex H.
118    ///
119    /// Returns `None` when `aac_type` is `None` or when the value is not
120    /// a recognised Table 26 component_type for AAC (stream_content 0x06).
121    #[must_use]
122    pub fn decoded_component_type(&self) -> Option<AacComponentType> {
123        let ct = self.aac_type?;
124        let saoc_de = self.saoc_de_flag;
125        if ct == 0xA0 {
126            return Some(AacComponentType {
127                mono: false,
128                stereo: false,
129                surround: false,
130                visually_impaired: false,
131                hard_of_hearing: false,
132                supplementary: false,
133                v2: false,
134                saoc_de: true,
135            });
136        }
137        let v2 = matches!(ct, 0x43..=0x46 | 0x49..=0x4A);
138        let result = match ct {
139            0x01 => AacComponentType {
140                mono: true,
141                stereo: false,
142                surround: false,
143                visually_impaired: false,
144                hard_of_hearing: false,
145                supplementary: false,
146                v2: false,
147                saoc_de,
148            },
149            0x03 => AacComponentType {
150                mono: false,
151                stereo: true,
152                surround: false,
153                visually_impaired: false,
154                hard_of_hearing: false,
155                supplementary: false,
156                v2: false,
157                saoc_de,
158            },
159            0x05 => AacComponentType {
160                mono: false,
161                stereo: false,
162                surround: true,
163                visually_impaired: false,
164                hard_of_hearing: false,
165                supplementary: false,
166                v2: false,
167                saoc_de,
168            },
169            0x40 | 0x47 | 0x48 => AacComponentType {
170                mono: false,
171                stereo: false,
172                surround: false,
173                visually_impaired: true,
174                hard_of_hearing: false,
175                supplementary: matches!(ct, 0x47 | 0x48),
176                v2: false,
177                saoc_de,
178            },
179            0x41 => AacComponentType {
180                mono: false,
181                stereo: false,
182                surround: false,
183                visually_impaired: false,
184                hard_of_hearing: true,
185                supplementary: false,
186                v2: false,
187                saoc_de,
188            },
189            0x42 => AacComponentType {
190                mono: false,
191                stereo: false,
192                surround: false,
193                visually_impaired: false,
194                hard_of_hearing: false,
195                supplementary: true,
196                v2: false,
197                saoc_de,
198            },
199            0x43 => AacComponentType {
200                mono: false,
201                stereo: true,
202                surround: false,
203                visually_impaired: false,
204                hard_of_hearing: false,
205                supplementary: false,
206                v2: true,
207                saoc_de,
208            },
209            0x44 | 0x49 | 0x4A => AacComponentType {
210                mono: false,
211                stereo: false,
212                surround: false,
213                visually_impaired: true,
214                hard_of_hearing: false,
215                supplementary: matches!(ct, 0x49 | 0x4A),
216                v2,
217                saoc_de,
218            },
219            0x45 => AacComponentType {
220                mono: false,
221                stereo: false,
222                surround: false,
223                visually_impaired: false,
224                hard_of_hearing: true,
225                supplementary: false,
226                v2: true,
227                saoc_de,
228            },
229            0x46 => AacComponentType {
230                mono: false,
231                stereo: false,
232                surround: false,
233                visually_impaired: false,
234                hard_of_hearing: false,
235                supplementary: true,
236                v2: true,
237                saoc_de,
238            },
239            _ => return None,
240        };
241        Some(result)
242    }
243}
244
245/// AAC Descriptor.
246#[derive(Debug, Clone, PartialEq, Eq)]
247#[cfg_attr(feature = "serde", derive(serde::Serialize))]
248#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
249pub struct AacDescriptor<'a> {
250    /// 8-bit profile_and_level (MPEG-4_audio_profile_and_level).
251    pub profile_and_level: u8,
252    /// Optional extension; `None` means the body was a single byte.
253    pub extension: Option<AacExtension<'a>>,
254}
255
256impl<'a> Parse<'a> for AacDescriptor<'a> {
257    type Error = crate::error::Error;
258    fn parse(bytes: &'a [u8]) -> Result<Self> {
259        if bytes.len() < HEADER_LEN + 1 {
260            return Err(Error::BufferTooShort {
261                need: HEADER_LEN + 1,
262                have: bytes.len(),
263                what: "AacDescriptor header+profile",
264            });
265        }
266        let body = descriptor_body(
267            bytes,
268            TAG,
269            "AacDescriptor",
270            "unexpected tag for AAC_descriptor",
271        )?;
272        if body.is_empty() {
273            return Err(Error::InvalidDescriptor {
274                tag: TAG,
275                reason: "AAC_descriptor body shorter than 1 byte",
276            });
277        }
278        let profile_and_level = body[0];
279        let extension = if body.len() > 1 {
280            let flags = body[1];
281            let aac_type_flag = (flags & FLAG_AAC_TYPE) != 0;
282            let saoc_de_flag = (flags & FLAG_SAOC_DE) != 0;
283            let mut pos = 2;
284            let aac_type = if aac_type_flag {
285                if pos >= body.len() {
286                    return Err(Error::InvalidDescriptor {
287                        tag: TAG,
288                        reason: "AAC_type_flag set but AAC_type byte missing",
289                    });
290                }
291                let t = body[pos];
292                pos += 1;
293                Some(t)
294            } else {
295                None
296            };
297            let additional_info = &body[pos..];
298            Some(AacExtension {
299                saoc_de_flag,
300                aac_type,
301                additional_info,
302            })
303        } else {
304            None
305        };
306        Ok(Self {
307            profile_and_level,
308            extension,
309        })
310    }
311}
312
313impl Serialize for AacDescriptor<'_> {
314    type Error = crate::error::Error;
315    fn serialized_len(&self) -> usize {
316        let body = 1 + match &self.extension {
317            None => 0,
318            Some(ext) => 1 + usize::from(ext.aac_type.is_some()) + ext.additional_info.len(),
319        };
320        HEADER_LEN + body
321    }
322
323    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
324        let body_len = self.serialized_len() - HEADER_LEN;
325        if body_len > u8::MAX as usize {
326            return Err(Error::InvalidDescriptor {
327                tag: TAG,
328                reason: "AAC_descriptor body exceeds 255 bytes",
329            });
330        }
331        let len = self.serialized_len();
332        if buf.len() < len {
333            return Err(Error::OutputBufferTooSmall {
334                need: len,
335                have: buf.len(),
336            });
337        }
338        buf[0] = TAG;
339        buf[1] = body_len as u8;
340        buf[2] = self.profile_and_level;
341        let mut pos = 3;
342        if let Some(ext) = &self.extension {
343            let mut flags = 0u8;
344            if ext.aac_type.is_some() {
345                flags |= FLAG_AAC_TYPE;
346            }
347            if ext.saoc_de_flag {
348                flags |= FLAG_SAOC_DE;
349            }
350            // reserved_zero_future_use(6) emitted as zeros per spec.
351            buf[pos] = flags & !RESERVED_ZERO_MASK;
352            pos += 1;
353            if let Some(t) = ext.aac_type {
354                buf[pos] = t;
355                pos += 1;
356            }
357            buf[pos..pos + ext.additional_info.len()].copy_from_slice(ext.additional_info);
358        }
359        Ok(len)
360    }
361}
362impl<'a> crate::traits::DescriptorDef<'a> for AacDescriptor<'a> {
363    const TAG: u8 = TAG;
364    const NAME: &'static str = "AAC";
365}
366
367#[cfg(test)]
368mod tests {
369    use super::*;
370
371    #[test]
372    fn parse_profile_only() {
373        let bytes = [TAG, 1, 0x50];
374        let d = AacDescriptor::parse(&bytes).unwrap();
375        assert_eq!(d.profile_and_level, 0x50);
376        assert!(d.extension.is_none());
377    }
378
379    #[test]
380    fn parse_with_flags_no_aac_type() {
381        // flags: SAOC_DE set, AAC_type clear.
382        let bytes = [TAG, 2, 0x51, FLAG_SAOC_DE];
383        let d = AacDescriptor::parse(&bytes).unwrap();
384        let ext = d.extension.unwrap();
385        assert!(ext.saoc_de_flag);
386        assert!(ext.aac_type.is_none());
387        assert!(ext.additional_info.is_empty());
388    }
389
390    #[test]
391    fn parse_with_aac_type() {
392        let bytes = [TAG, 3, 0x52, FLAG_AAC_TYPE, 0x03];
393        let d = AacDescriptor::parse(&bytes).unwrap();
394        let ext = d.extension.unwrap();
395        assert!(!ext.saoc_de_flag);
396        assert_eq!(ext.aac_type, Some(0x03));
397        assert!(ext.additional_info.is_empty());
398    }
399
400    #[test]
401    fn parse_with_aac_type_and_additional_info() {
402        let bytes = [TAG, 5, 0x52, FLAG_AAC_TYPE | FLAG_SAOC_DE, 0x05, 0xAA, 0xBB];
403        let d = AacDescriptor::parse(&bytes).unwrap();
404        let ext = d.extension.unwrap();
405        assert!(ext.saoc_de_flag);
406        assert_eq!(ext.aac_type, Some(0x05));
407        assert_eq!(ext.additional_info, &[0xAA, 0xBB]);
408    }
409
410    #[test]
411    fn decode_aac_component_type_he_aac_stereo() {
412        let d = AacDescriptor {
413            profile_and_level: 0x50,
414            extension: Some(AacExtension {
415                saoc_de_flag: false,
416                aac_type: Some(0x03),
417                additional_info: &[],
418            }),
419        };
420        let ct = d.extension.unwrap().decoded_component_type().unwrap();
421        assert!(!ct.mono);
422        assert!(ct.stereo);
423        assert!(!ct.surround);
424        assert_eq!(ct.name(), "HE-AAC audio, stereo");
425    }
426
427    #[test]
428    fn decode_aac_component_type_saoc_de() {
429        let d = AacDescriptor {
430            profile_and_level: 0x50,
431            extension: Some(AacExtension {
432                saoc_de_flag: true,
433                aac_type: Some(0xA0),
434                additional_info: &[],
435            }),
436        };
437        let ct = d.extension.unwrap().decoded_component_type().unwrap();
438        assert!(ct.saoc_de);
439        assert_eq!(ct.name(), "HE-AAC or HE-AAC v2 with SAOC-DE ancillary data");
440    }
441
442    #[test]
443    fn decode_aac_component_type_unknown() {
444        let d = AacDescriptor {
445            profile_and_level: 0x50,
446            extension: Some(AacExtension {
447                saoc_de_flag: false,
448                aac_type: Some(0xFF),
449                additional_info: &[],
450            }),
451        };
452        assert!(d.extension.unwrap().decoded_component_type().is_none());
453    }
454
455    #[test]
456    fn parse_rejects_wrong_tag() {
457        let bytes = [0x7B, 1, 0x50];
458        assert!(matches!(
459            AacDescriptor::parse(&bytes).unwrap_err(),
460            Error::InvalidDescriptor { tag: 0x7B, .. }
461        ));
462    }
463
464    #[test]
465    fn parse_rejects_aac_type_flag_without_byte() {
466        // length=2 covers profile + flags only, but AAC_type_flag claims a byte.
467        let bytes = [TAG, 2, 0x50, FLAG_AAC_TYPE];
468        assert!(matches!(
469            AacDescriptor::parse(&bytes).unwrap_err(),
470            Error::InvalidDescriptor { .. }
471        ));
472    }
473
474    #[test]
475    fn parse_rejects_length_overrunning_buffer() {
476        let bytes = [TAG, 4, 0x50];
477        assert!(matches!(
478            AacDescriptor::parse(&bytes).unwrap_err(),
479            Error::BufferTooShort { .. }
480        ));
481    }
482
483    #[test]
484    fn serialize_round_trip_profile_only() {
485        let d = AacDescriptor {
486            profile_and_level: 0x58,
487            extension: None,
488        };
489        let mut buf = vec![0u8; d.serialized_len()];
490        d.serialize_into(&mut buf).unwrap();
491        assert_eq!(buf, vec![TAG, 1, 0x58]);
492        assert_eq!(AacDescriptor::parse(&buf).unwrap(), d);
493    }
494
495    #[test]
496    fn serialize_round_trip_full() {
497        let d = AacDescriptor {
498            profile_and_level: 0x52,
499            extension: Some(AacExtension {
500                saoc_de_flag: true,
501                aac_type: Some(0x40),
502                additional_info: &[0xFE, 0xED],
503            }),
504        };
505        let mut buf = vec![0u8; d.serialized_len()];
506        d.serialize_into(&mut buf).unwrap();
507        assert_eq!(AacDescriptor::parse(&buf).unwrap(), d);
508    }
509
510    #[test]
511    fn serialize_emits_reserved_bits_zero() {
512        let d = AacDescriptor {
513            profile_and_level: 0x50,
514            extension: Some(AacExtension {
515                saoc_de_flag: false,
516                aac_type: None,
517                additional_info: &[],
518            }),
519        };
520        let mut buf = vec![0u8; d.serialized_len()];
521        d.serialize_into(&mut buf).unwrap();
522        // flags byte: no flags set, reserved zeros => 0x00.
523        assert_eq!(buf[3] & RESERVED_ZERO_MASK, 0);
524        assert_eq!(buf[3], 0x00);
525    }
526
527    #[cfg(feature = "serde")]
528    #[test]
529    fn serde_serializes_to_stable_json() {
530        // Borrowed `&[u8]` cannot deserialize from a JSON number array, so we
531        // assert the Serialize impl is wired and emits stable JSON.
532        let d = AacDescriptor {
533            profile_and_level: 0x52,
534            extension: Some(AacExtension {
535                saoc_de_flag: true,
536                aac_type: Some(0x03),
537                additional_info: &[0x11],
538            }),
539        };
540        let j = serde_json::to_string(&d).unwrap();
541        // Valid, re-parseable JSON (key order is map-defined, so we do not
542        // assert byte-for-byte string stability).
543        let _v: serde_json::Value = serde_json::from_str(&j).unwrap();
544        assert!(j.contains("profile_and_level"));
545    }
546}