1mod import;
7
8pub use import::*;
9
10use bytes::{Buf, Bytes};
11
12#[derive(Debug, Clone, thiserror::Error)]
14#[non_exhaustive]
15pub enum Error {
16 #[error("AudioSpecificConfig must be at least 2 bytes")]
17 ConfigTooShort,
18
19 #[error("extended audioObjectType requires 2 additional bytes")]
20 ExtendedConfigTooShort,
21
22 #[error("AudioSpecificConfig incomplete")]
23 IncompleteConfig,
24
25 #[error("explicit sample rate requires 3 additional bytes")]
26 ExplicitSampleRateTooShort,
27
28 #[error("unsupported sample rate index: {0}")]
29 UnsupportedSampleRateIndex(u8),
30}
31
32pub type Result<T> = std::result::Result<T, Error>;
33
34pub struct Config {
37 pub profile: u8,
38 pub sample_rate: u32,
39 pub channel_count: u32,
40}
41
42impl Config {
43 pub fn parse<T: Buf>(buf: &mut T) -> Result<Self> {
51 if buf.remaining() < 2 {
52 return Err(Error::ConfigTooShort);
53 }
54
55 let mut reader = BitReader::new(buf);
56
57 let mut object_type = reader.read(5, Error::ConfigTooShort)? as u8;
59 if object_type == 31 {
60 object_type = 32 + reader.read(6, Error::ExtendedConfigTooShort)? as u8;
61 }
62
63 let freq_index = reader.read(4, Error::IncompleteConfig)? as u8;
65 let sample_rate = if freq_index == 15 {
66 reader.read(24, Error::ExplicitSampleRateTooShort)?
67 } else {
68 *SAMPLE_RATES
69 .get(freq_index as usize)
70 .ok_or(Error::UnsupportedSampleRateIndex(freq_index))?
71 };
72
73 let channel_config = reader.read(4, Error::IncompleteConfig)? as u8;
75 let channel_count = channel_count_from_config(channel_config);
76
77 if buf.remaining() > 0 {
80 buf.advance(buf.remaining());
81 }
82
83 Ok(Self {
84 profile: object_type,
85 sample_rate,
86 channel_count,
87 })
88 }
89
90 pub fn encode(&self) -> Bytes {
95 let profile = self.profile & 0x1F;
97
98 let freq_index: u8 = match self.sample_rate {
99 96000 => 0,
100 88200 => 1,
101 64000 => 2,
102 48000 => 3,
103 44100 => 4,
104 32000 => 5,
105 24000 => 6,
106 22050 => 7,
107 16000 => 8,
108 12000 => 9,
109 11025 => 10,
110 8000 => 11,
111 7350 => 12,
112 _ => 0xF, };
114
115 let channel_config = channel_config_from_count(self.channel_count) as u64;
116
117 if freq_index != 0xF {
118 let b0 = (profile << 3) | (freq_index >> 1);
120 let b1 = ((freq_index & 1) << 7) | ((channel_config as u8 & 0x0F) << 3);
121 Bytes::from(vec![b0, b1])
122 } else {
123 let mut bits: u64 = 0;
125 bits |= (profile as u64) << 35;
126 bits |= 0xF_u64 << 31;
127 bits |= (self.sample_rate as u64) << 7;
128 bits |= (channel_config & 0xF) << 3;
129 let all = bits.to_be_bytes();
130 Bytes::copy_from_slice(&all[3..8])
131 }
132 }
133}
134
135const SAMPLE_RATES: [u32; 13] = [
138 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350,
139];
140
141struct BitReader<'a, T: Buf> {
147 buf: &'a mut T,
148 current: u8,
149 bits_left: u8,
150}
151
152impl<'a, T: Buf> BitReader<'a, T> {
153 fn new(buf: &'a mut T) -> Self {
154 Self {
155 buf,
156 current: 0,
157 bits_left: 0,
158 }
159 }
160
161 fn read(&mut self, n: u8, short: Error) -> Result<u32> {
163 let mut value = 0u32;
164 for _ in 0..n {
165 if self.bits_left == 0 {
166 if !self.buf.has_remaining() {
167 return Err(short);
168 }
169 self.current = self.buf.get_u8();
170 self.bits_left = 8;
171 }
172 self.bits_left -= 1;
173 value = (value << 1) | u32::from((self.current >> self.bits_left) & 1);
174 }
175 Ok(value)
176 }
177}
178
179fn channel_count_from_config(channel_config: u8) -> u32 {
184 match channel_config {
185 1..=6 => channel_config as u32,
186 7 => 8,
187 0 => {
188 tracing::warn!("channel_config=0 (program config element) unsupported, defaulting to stereo");
189 2
190 }
191 _ => {
192 tracing::warn!(channel_config, "unsupported channel config, defaulting to stereo");
193 2
194 }
195 }
196}
197
198fn channel_config_from_count(channel_count: u32) -> u8 {
201 match channel_count {
202 1..=6 => channel_count as u8,
203 8 => 7,
204 _ => {
205 tracing::warn!(channel_count, "unsupported channel count, defaulting to stereo");
206 2
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn parses_standard_2_byte_config() {
217 let buf = vec![0x12, 0x10];
221 let cfg = Config::parse(&mut buf.as_slice()).unwrap();
222 assert_eq!(cfg.profile, 2);
223 assert_eq!(cfg.sample_rate, 44100);
224 assert_eq!(cfg.channel_count, 2);
225 }
226
227 #[test]
228 fn round_trip_explicit_sample_rate() {
229 let cfg = Config {
233 profile: 2,
234 sample_rate: 44_056, channel_count: 2,
236 };
237 let encoded = cfg.encode();
238 assert_eq!(encoded.len(), 5, "explicit-rate config is 5 bytes");
239
240 let parsed = Config::parse(&mut encoded.as_ref()).unwrap();
241 assert_eq!(parsed.profile, 2);
242 assert_eq!(parsed.sample_rate, 44_056);
243 assert_eq!(parsed.channel_count, 2);
244 }
245
246 #[test]
247 fn parses_extended_object_type() {
248 let buf: [u8; 3] = [0xF8, 0x86, 0x40];
252 let cfg = Config::parse(&mut buf.as_slice()).unwrap();
253 assert_eq!(cfg.profile, 36);
254 assert_eq!(cfg.sample_rate, 48_000);
255 assert_eq!(cfg.channel_count, 2);
256 }
257
258 #[test]
259 fn round_trip_5_1_channels() {
260 let cfg = Config {
262 profile: 2,
263 sample_rate: 48000,
264 channel_count: 6,
265 };
266 let encoded = cfg.encode();
267 let parsed = Config::parse(&mut encoded.as_ref()).unwrap();
268 assert_eq!(parsed.channel_count, 6);
269 }
270
271 #[test]
272 fn round_trip_7_1_channels() {
273 let cfg = Config {
275 profile: 2,
276 sample_rate: 48000,
277 channel_count: 8,
278 };
279 let encoded = cfg.encode();
280 let parsed = Config::parse(&mut encoded.as_ref()).unwrap();
281 assert_eq!(parsed.channel_count, 8, "7.1 surround should round-trip as 8 channels");
282 }
283
284 #[test]
285 fn channel_config_zero_falls_back_to_stereo() {
286 assert_eq!(channel_count_from_config(0), 2);
288 }
289
290 #[test]
291 fn unsupported_channel_count_falls_back_to_stereo_config() {
292 assert_eq!(channel_config_from_count(9), 2);
293 }
294}