scuffle_aac/
lib.rs

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