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