scuffle_aac/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//! A crate for decoding AAC audio headers.
//!
//! ## License
//!
//! This project is licensed under the [MIT](./LICENSE.MIT) or
//! [Apache-2.0](./LICENSE.Apache-2.0) license. You can choose between one of
//! them if you use this work.
//!
//! `SPDX-License-Identifier: MIT OR Apache-2.0`
#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]

use std::io;

use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use scuffle_bytes_util::BitReader;

/// A Partial Audio Specific Config
/// ISO/IEC 14496-3:2019(E) - 1.6
///
/// This struct does not represent the full AudioSpecificConfig, it only
/// represents the top few fields.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use]
pub struct PartialAudioSpecificConfig {
    /// Audio Object Type
    pub audio_object_type: AudioObjectType,
    /// Sampling Frequency
    pub sampling_frequency: u32,
    /// Channel Configuration
    pub channel_configuration: u8,
}

/// SBR Audio Object Type
/// ISO/IEC 14496-3:2019(E) - 1.5.1.2.6
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use]
pub enum AudioObjectType {
    AacMain,
    AacLowComplexity,
    Unknown(u16),
}

impl AudioObjectType {
    /// Converts an AudioObjectType to a u16
    pub const fn as_u16(&self) -> u16 {
        match self {
            AudioObjectType::AacMain => 1,
            AudioObjectType::AacLowComplexity => 2,
            AudioObjectType::Unknown(value) => *value,
        }
    }

    /// Converts a u16 to an AudioObjectType
    pub const fn from_u16(value: u16) -> Self {
        match value {
            1 => AudioObjectType::AacMain,
            2 => AudioObjectType::AacLowComplexity,
            _ => AudioObjectType::Unknown(value),
        }
    }
}

impl From<u16> for AudioObjectType {
    fn from(value: u16) -> Self {
        Self::from_u16(value)
    }
}

impl From<AudioObjectType> for u16 {
    fn from(value: AudioObjectType) -> Self {
        value.as_u16()
    }
}

/// Sampling Frequency Index
///
/// The purpose of the FrequencyIndex is to encode commonly used frequencies in
/// 4 bits to save space. These are the set of commonly used frequencies defined
/// in the specification.
///
/// ISO/IEC 14496-3:2019(E) - 1.6.2.4 (Table 1.22)
#[derive(FromPrimitive, Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord)]
#[repr(u8)]
#[must_use]
pub enum SampleFrequencyIndex {
    /// 96000 Hz
    Freq96000 = 0x0,
    /// 88200 Hz
    Freq88200 = 0x1,
    /// 64000 Hz
    Freq64000 = 0x2,
    /// 48000 Hz
    Freq48000 = 0x3,
    /// 44100 Hz
    Freq44100 = 0x4,
    /// 32000 Hz
    Freq32000 = 0x5,
    /// 24000 Hz
    Freq24000 = 0x6,
    /// 22050 Hz
    Freq22050 = 0x7,
    /// 16000 Hz
    Freq16000 = 0x8,
    /// 12000 Hz
    Freq12000 = 0x9,
    /// 11025 Hz
    Freq11025 = 0xA,
    /// 8000 Hz
    Freq8000 = 0xB,
    /// 7350 Hz
    Freq7350 = 0xC,
    /// Reserved
    FreqReserved = 0xD,
    /// Reserved
    FreqReserved2 = 0xE,
    /// Escape (Meaning the frequency is not in the table, and we need to read
    /// an additional 24 bits to get the frequency)
    FreqEscape = 0xF,
}

impl SampleFrequencyIndex {
    /// Convert the SampleFrequencyIndex to the actual frequency in Hz
    pub const fn to_freq(&self) -> Option<u32> {
        match self {
            SampleFrequencyIndex::Freq96000 => Some(96000),
            SampleFrequencyIndex::Freq88200 => Some(88200),
            SampleFrequencyIndex::Freq64000 => Some(64000),
            SampleFrequencyIndex::Freq48000 => Some(48000),
            SampleFrequencyIndex::Freq44100 => Some(44100),
            SampleFrequencyIndex::Freq32000 => Some(32000),
            SampleFrequencyIndex::Freq24000 => Some(24000),
            SampleFrequencyIndex::Freq22050 => Some(22050),
            SampleFrequencyIndex::Freq16000 => Some(16000),
            SampleFrequencyIndex::Freq12000 => Some(12000),
            SampleFrequencyIndex::Freq11025 => Some(11025),
            SampleFrequencyIndex::Freq8000 => Some(8000),
            SampleFrequencyIndex::Freq7350 => Some(7350),
            SampleFrequencyIndex::FreqReserved => None,
            SampleFrequencyIndex::FreqReserved2 => None,
            SampleFrequencyIndex::FreqEscape => None,
        }
    }
}

impl PartialAudioSpecificConfig {
    /// Parse the Audio Specific Config from given bytes
    /// The implementation is based on ISO/IEC 14496-3:2019(E) - 1.6.2.1 (Table
    /// 1.19) This does not parse the entire AAC Data, it only parses the
    /// top few fields.
    /// - Audio Object Type
    /// - Sampling Frequency
    /// - Channel Configuration
    pub fn parse(data: &[u8]) -> io::Result<Self> {
        let mut bitreader = BitReader::new_from_slice(data);

        // GetAudioObjectType() # ISO/IEC 14496-3:2019(E) - 1.6.2.1 (Table 1.20)
        let mut audio_object_type = bitreader.read_bits(5)? as u16;
        if audio_object_type == 31 {
            audio_object_type = 32 + bitreader.read_bits(6)? as u16;
        }

        // The table calls for us to read a 4-bit value. If the value is type FreqEscape
        // (0xF), we need to read 24 bits to get the sampling frequency.
        let sampling_frequency_index = SampleFrequencyIndex::from_u8(bitreader.read_bits(4)? as u8)
            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?;

        let sampling_frequency = match sampling_frequency_index {
            // Uses the extended sampling frequency to represent the freq as a non-common value
            SampleFrequencyIndex::FreqEscape => bitreader.read_bits(24)? as u32,
            _ => sampling_frequency_index
                .to_freq()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?,
        };

        // 4 Bits to get the channel configuration
        let channel_configuration = bitreader.read_bits(4)? as u8;

        Ok(Self {
            audio_object_type: audio_object_type.into(),
            sampling_frequency,
            channel_configuration,
        })
    }
}

#[cfg(test)]
#[cfg_attr(all(test, coverage_nightly), coverage(off))]
mod tests {
    use super::*;

    #[test]
    fn test_aac_config_parse() {
        let data = [
            0x12, 0x10, 0x56, 0xe5, 0x00, 0x2d, 0x96, 0x01, 0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00,
        ];

        let config = PartialAudioSpecificConfig::parse(&data).unwrap();
        assert_eq!(config.audio_object_type, AudioObjectType::AacLowComplexity);
        assert_eq!(config.sampling_frequency, 44100);
        assert_eq!(config.channel_configuration, 2);
    }

    #[test]
    fn test_idx_to_freq() {
        let cases = [
            (SampleFrequencyIndex::FreqEscape, None),
            (SampleFrequencyIndex::FreqReserved2, None),
            (SampleFrequencyIndex::FreqReserved, None),
            (SampleFrequencyIndex::Freq7350, Some(7350)),
            (SampleFrequencyIndex::Freq8000, Some(8000)),
            (SampleFrequencyIndex::Freq11025, Some(11025)),
            (SampleFrequencyIndex::Freq12000, Some(12000)),
            (SampleFrequencyIndex::Freq16000, Some(16000)),
            (SampleFrequencyIndex::Freq22050, Some(22050)),
            (SampleFrequencyIndex::Freq24000, Some(24000)),
            (SampleFrequencyIndex::Freq32000, Some(32000)),
            (SampleFrequencyIndex::Freq44100, Some(44100)),
            (SampleFrequencyIndex::Freq48000, Some(48000)),
            (SampleFrequencyIndex::Freq64000, Some(64000)),
            (SampleFrequencyIndex::Freq88200, Some(88200)),
            (SampleFrequencyIndex::Freq96000, Some(96000)),
        ];

        for (idx, freq) in cases {
            assert_eq!(freq, idx.to_freq(), "Expected frequency for {:?}", idx);
        }
    }
}