use super::data::AudioSpecificConfig;
pub(crate) fn read_descriptor_length(buf: &[u8], pos: &mut usize) -> Option<u32> {
let mut length = 0u32;
for _ in 0..4 {
if *pos >= buf.len() {
return None;
}
let b = buf[*pos];
*pos += 1;
length = (length << 7) | (b & 0x7F) as u32;
if b & 0x80 == 0 {
break;
}
}
Some(length)
}
struct BitReader<'a> {
buf: &'a [u8],
bit: usize,
}
impl<'a> BitReader<'a> {
fn new(buf: &'a [u8]) -> Self {
Self { buf, bit: 0 }
}
fn read(&mut self, n: usize) -> Option<u32> {
debug_assert!(n <= 32);
let mut v = 0u32;
for _ in 0..n {
let byte = self.buf.get(self.bit / 8)?;
v = (v << 1) | ((byte >> (7 - self.bit % 8)) & 1) as u32;
self.bit += 1;
}
Some(v)
}
}
fn aac_sample_rate(index: u32) -> Option<u32> {
[
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350,
]
.get(index as usize)
.copied()
}
fn audio_object_type_name(aot: u8) -> &'static str {
match aot {
1 => "AAC Main",
2 => "AAC-LC",
3 => "AAC SSR",
4 => "AAC LTP",
5 => "SBR",
6 => "AAC Scalable",
23 => "AAC-LD",
29 => "PS",
39 => "AAC-ELD",
42 => "xHE-AAC (USAC)",
_ => "MPEG-4 Audio",
}
}
pub(crate) fn parse_audio_specific_config(buf: &[u8]) -> Option<AudioSpecificConfig> {
let mut r = BitReader::new(buf);
let read_aot = |r: &mut BitReader| -> Option<u8> {
let aot = r.read(5)?;
if aot == 31 {
Some((32 + r.read(6)?) as u8)
} else {
Some(aot as u8)
}
};
let read_rate = |r: &mut BitReader| -> Option<u32> {
let idx = r.read(4)?;
if idx == 15 {
r.read(24)
} else {
aac_sample_rate(idx)
}
};
let mut aot = read_aot(&mut r)?;
let sample_rate = read_rate(&mut r)?;
let channel_configuration = r.read(4)? as u8;
let mut sbr = false;
let mut ps = false;
let mut extension_sample_rate = None;
if aot == 5 || aot == 29 {
sbr = true;
ps = aot == 29;
extension_sample_rate = Some(read_rate(&mut r)?);
aot = read_aot(&mut r)?;
} else if matches!(aot, 1..=4 | 6 | 7 | 17 | 19..=23) {
let ga_ok = (|| -> Option<bool> {
let _frame_length_flag = r.read(1)?;
if r.read(1)? == 1 {
r.read(14)?; }
Some(r.read(1)? == 0)
})();
if ga_ok == Some(true)
&& r.read(11) == Some(0x2B7)
&& read_aot(&mut r) == Some(5)
&& r.read(1) == Some(1)
{
sbr = true;
extension_sample_rate = read_rate(&mut r);
if r.read(11) == Some(0x548) && r.read(1) == Some(1) {
ps = true;
}
}
}
let profile = if ps {
"HE-AAC v2".to_string()
} else if sbr {
"HE-AAC".to_string()
} else {
audio_object_type_name(aot).to_string()
};
Some(AudioSpecificConfig {
audio_object_type: aot,
profile,
sample_rate,
extension_sample_rate,
channel_configuration,
sbr,
ps,
})
}