scuffle_aac/
lib.rs

1//! A crate for decoding AAC audio headers.
2//!
3//! ## License
4//!
5//! This project is licensed under the [MIT](./LICENSE.MIT) or
6//! [Apache-2.0](./LICENSE.Apache-2.0) license. You can choose between one of
7//! them if you use this work.
8//!
9//! `SPDX-License-Identifier: MIT OR Apache-2.0`
10#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
11
12use std::io;
13
14use num_derive::FromPrimitive;
15use num_traits::FromPrimitive;
16use scuffle_bytes_util::BitReader;
17
18/// A Partial Audio Specific Config
19/// ISO/IEC 14496-3:2019(E) - 1.6
20///
21/// This struct does not represent the full AudioSpecificConfig, it only
22/// represents the top few fields.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[must_use]
25pub struct PartialAudioSpecificConfig {
26    /// Audio Object Type
27    pub audio_object_type: AudioObjectType,
28    /// Sampling Frequency
29    pub sampling_frequency: u32,
30    /// Channel Configuration
31    pub channel_configuration: u8,
32}
33
34/// SBR Audio Object Type
35/// ISO/IEC 14496-3:2019(E) - 1.5.1.2.6
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[must_use]
38pub enum AudioObjectType {
39    AacMain,
40    AacLowComplexity,
41    Unknown(u16),
42}
43
44impl AudioObjectType {
45    /// Converts an AudioObjectType to a u16
46    pub const fn as_u16(&self) -> u16 {
47        match self {
48            AudioObjectType::AacMain => 1,
49            AudioObjectType::AacLowComplexity => 2,
50            AudioObjectType::Unknown(value) => *value,
51        }
52    }
53
54    /// Converts a u16 to an AudioObjectType
55    pub const fn from_u16(value: u16) -> Self {
56        match value {
57            1 => AudioObjectType::AacMain,
58            2 => AudioObjectType::AacLowComplexity,
59            _ => AudioObjectType::Unknown(value),
60        }
61    }
62}
63
64impl From<u16> for AudioObjectType {
65    fn from(value: u16) -> Self {
66        Self::from_u16(value)
67    }
68}
69
70impl From<AudioObjectType> for u16 {
71    fn from(value: AudioObjectType) -> Self {
72        value.as_u16()
73    }
74}
75
76/// Sampling Frequency Index
77///
78/// The purpose of the FrequencyIndex is to encode commonly used frequencies in
79/// 4 bits to save space. These are the set of commonly used frequencies defined
80/// in the specification.
81///
82/// ISO/IEC 14496-3:2019(E) - 1.6.2.4 (Table 1.22)
83#[derive(FromPrimitive, Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord)]
84#[repr(u8)]
85#[must_use]
86pub enum SampleFrequencyIndex {
87    /// 96000 Hz
88    Freq96000 = 0x0,
89    /// 88200 Hz
90    Freq88200 = 0x1,
91    /// 64000 Hz
92    Freq64000 = 0x2,
93    /// 48000 Hz
94    Freq48000 = 0x3,
95    /// 44100 Hz
96    Freq44100 = 0x4,
97    /// 32000 Hz
98    Freq32000 = 0x5,
99    /// 24000 Hz
100    Freq24000 = 0x6,
101    /// 22050 Hz
102    Freq22050 = 0x7,
103    /// 16000 Hz
104    Freq16000 = 0x8,
105    /// 12000 Hz
106    Freq12000 = 0x9,
107    /// 11025 Hz
108    Freq11025 = 0xA,
109    /// 8000 Hz
110    Freq8000 = 0xB,
111    /// 7350 Hz
112    Freq7350 = 0xC,
113    /// Reserved
114    FreqReserved = 0xD,
115    /// Reserved
116    FreqReserved2 = 0xE,
117    /// Escape (Meaning the frequency is not in the table, and we need to read
118    /// an additional 24 bits to get the frequency)
119    FreqEscape = 0xF,
120}
121
122impl SampleFrequencyIndex {
123    /// Convert the SampleFrequencyIndex to the actual frequency in Hz
124    pub const fn to_freq(&self) -> Option<u32> {
125        match self {
126            SampleFrequencyIndex::Freq96000 => Some(96000),
127            SampleFrequencyIndex::Freq88200 => Some(88200),
128            SampleFrequencyIndex::Freq64000 => Some(64000),
129            SampleFrequencyIndex::Freq48000 => Some(48000),
130            SampleFrequencyIndex::Freq44100 => Some(44100),
131            SampleFrequencyIndex::Freq32000 => Some(32000),
132            SampleFrequencyIndex::Freq24000 => Some(24000),
133            SampleFrequencyIndex::Freq22050 => Some(22050),
134            SampleFrequencyIndex::Freq16000 => Some(16000),
135            SampleFrequencyIndex::Freq12000 => Some(12000),
136            SampleFrequencyIndex::Freq11025 => Some(11025),
137            SampleFrequencyIndex::Freq8000 => Some(8000),
138            SampleFrequencyIndex::Freq7350 => Some(7350),
139            SampleFrequencyIndex::FreqReserved => None,
140            SampleFrequencyIndex::FreqReserved2 => None,
141            SampleFrequencyIndex::FreqEscape => None,
142        }
143    }
144}
145
146impl PartialAudioSpecificConfig {
147    /// Parse the Audio Specific Config from given bytes
148    /// The implementation is based on ISO/IEC 14496-3:2019(E) - 1.6.2.1 (Table
149    /// 1.19) This does not parse the entire AAC Data, it only parses the
150    /// top few fields.
151    /// - Audio Object Type
152    /// - Sampling Frequency
153    /// - Channel Configuration
154    pub fn parse(data: &[u8]) -> io::Result<Self> {
155        let mut bitreader = BitReader::new_from_slice(data);
156
157        // GetAudioObjectType() # ISO/IEC 14496-3:2019(E) - 1.6.2.1 (Table 1.20)
158        let mut audio_object_type = bitreader.read_bits(5)? as u16;
159        if audio_object_type == 31 {
160            audio_object_type = 32 + bitreader.read_bits(6)? as u16;
161        }
162
163        // The table calls for us to read a 4-bit value. If the value is type FreqEscape
164        // (0xF), we need to read 24 bits to get the sampling frequency.
165        let sampling_frequency_index = SampleFrequencyIndex::from_u8(bitreader.read_bits(4)? as u8)
166            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?;
167
168        let sampling_frequency = match sampling_frequency_index {
169            // Uses the extended sampling frequency to represent the freq as a non-common value
170            SampleFrequencyIndex::FreqEscape => bitreader.read_bits(24)? as u32,
171            _ => sampling_frequency_index
172                .to_freq()
173                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?,
174        };
175
176        // 4 Bits to get the channel configuration
177        let channel_configuration = bitreader.read_bits(4)? as u8;
178
179        Ok(Self {
180            audio_object_type: audio_object_type.into(),
181            sampling_frequency,
182            channel_configuration,
183        })
184    }
185}
186
187#[cfg(test)]
188#[cfg_attr(all(test, coverage_nightly), coverage(off))]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn test_aac_config_parse() {
194        let data = [
195            0x12, 0x10, 0x56, 0xe5, 0x00, 0x2d, 0x96, 0x01, 0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00,
196        ];
197
198        let config = PartialAudioSpecificConfig::parse(&data).unwrap();
199        assert_eq!(config.audio_object_type, AudioObjectType::AacLowComplexity);
200        assert_eq!(config.sampling_frequency, 44100);
201        assert_eq!(config.channel_configuration, 2);
202    }
203
204    #[test]
205    fn test_idx_to_freq() {
206        let cases = [
207            (SampleFrequencyIndex::FreqEscape, None),
208            (SampleFrequencyIndex::FreqReserved2, None),
209            (SampleFrequencyIndex::FreqReserved, None),
210            (SampleFrequencyIndex::Freq7350, Some(7350)),
211            (SampleFrequencyIndex::Freq8000, Some(8000)),
212            (SampleFrequencyIndex::Freq11025, Some(11025)),
213            (SampleFrequencyIndex::Freq12000, Some(12000)),
214            (SampleFrequencyIndex::Freq16000, Some(16000)),
215            (SampleFrequencyIndex::Freq22050, Some(22050)),
216            (SampleFrequencyIndex::Freq24000, Some(24000)),
217            (SampleFrequencyIndex::Freq32000, Some(32000)),
218            (SampleFrequencyIndex::Freq44100, Some(44100)),
219            (SampleFrequencyIndex::Freq48000, Some(48000)),
220            (SampleFrequencyIndex::Freq64000, Some(64000)),
221            (SampleFrequencyIndex::Freq88200, Some(88200)),
222            (SampleFrequencyIndex::Freq96000, Some(96000)),
223        ];
224
225        for (idx, freq) in cases {
226            assert_eq!(freq, idx.to_freq(), "Expected frequency for {:?}", idx);
227        }
228    }
229}