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