pub const SFI_FREQS: [u32; 13] = [
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AscSignaling {
NoExtension,
ImplicitMaybe,
ExplicitSbr,
ExplicitPs,
}
#[derive(Debug, Clone)]
pub struct ParsedAsc {
pub aot: u8,
pub sample_rate: u32,
pub channels: u16,
pub sbr_present: bool,
pub ps_present: bool,
pub sbr_sample_rate: Option<u32>,
pub signaling: AscSignaling,
}
pub fn parse_aac_asc(asc: &[u8]) -> Option<ParsedAsc> {
if asc.len() < 2 {
return None;
}
let mut br = BitReader::new(asc);
let leading_aot = read_aot(&mut br)?;
let leading_sfi = br.bits(4)? as usize;
let leading_sample_rate = decode_sfi(leading_sfi, &mut br)?;
let leading_chan_cfg = br.bits(4)? as u16;
if leading_aot == 5 || leading_aot == 29 {
let ext_sfi = br.bits(4)? as usize;
let _sbr_rate_redundant = decode_sfi(ext_sfi, &mut br)?;
let core_aot = read_aot(&mut br)?;
let sbr_output_rate = leading_sample_rate;
let core_rate = sbr_output_rate / 2;
return Some(ParsedAsc {
aot: core_aot,
sample_rate: core_rate,
channels: leading_chan_cfg,
sbr_present: true,
ps_present: leading_aot == 29,
sbr_sample_rate: Some(sbr_output_rate),
signaling: if leading_aot == 29 {
AscSignaling::ExplicitPs
} else {
AscSignaling::ExplicitSbr
},
});
}
let signaling = if leading_aot == 2 && leading_sample_rate <= 24_000 {
AscSignaling::ImplicitMaybe
} else {
AscSignaling::NoExtension
};
Some(ParsedAsc {
aot: leading_aot,
sample_rate: leading_sample_rate,
channels: leading_chan_cfg,
sbr_present: false,
ps_present: false,
sbr_sample_rate: None,
signaling,
})
}
pub fn effective_output_channels(parsed: &ParsedAsc) -> u16 {
let raw = parsed.channels;
if parsed.ps_present && raw == 1 {
return 2;
}
if raw == 0 { 2 } else { raw }
}
pub fn upgrade_to_explicit_signaling(asc: &[u8]) -> Option<Vec<u8>> {
let parsed = parse_aac_asc(asc)?;
if parsed.signaling != AscSignaling::ImplicitMaybe {
return None;
}
if parsed.aot != 2 {
return None;
}
if parsed.sample_rate > 24_000 {
return None;
}
let sbr_rate = parsed.sample_rate * 2;
let sbr_sfi = sfi_for_rate(sbr_rate);
let mut br = BitReader::new(asc);
br.bits(5)?; let leading_sfi = br.bits(4)? as usize;
if leading_sfi == 0xF {
br.bits(24)?; }
br.bits(4)?;
let tail_bits: Vec<u8> = drain_remaining_bits(&mut br);
let mut bw = BitWriter::new();
bw.bits(5, 5); match sbr_sfi {
Some(idx) => bw.bits(idx, 4), None => {
bw.bits(0xF, 4); bw.bits(sbr_rate, 24);
}
}
bw.bits(parsed.channels as u32, 4); match sbr_sfi {
Some(idx) => bw.bits(idx, 4),
None => {
bw.bits(0xF, 4);
bw.bits(sbr_rate, 24);
}
}
bw.bits(2, 5); for bit in &tail_bits {
bw.bits(*bit as u32, 1);
}
Some(bw.into_bytes())
}
fn sfi_for_rate(rate: u32) -> Option<u32> {
SFI_FREQS.iter().position(|r| *r == rate).map(|p| p as u32)
}
fn read_aot(br: &mut BitReader<'_>) -> Option<u8> {
let raw = br.bits(5)? as u8;
if raw == 31 {
let ext = br.bits(6)? as u8;
Some(32 + ext)
} else {
Some(raw)
}
}
fn decode_sfi(sfi: usize, br: &mut BitReader<'_>) -> Option<u32> {
if sfi == 0xF {
let r = br.bits(24)? as u32;
if r == 0 { None } else { Some(r) }
} else {
SFI_FREQS.get(sfi).copied()
}
}
fn drain_remaining_bits(br: &mut BitReader<'_>) -> Vec<u8> {
let mut out = Vec::new();
while let Some(b) = br.bits(1) {
out.push(b as u8);
}
out
}
struct BitReader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> BitReader<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
fn bits(&mut self, n: u32) -> Option<u64> {
let mut v: u64 = 0;
for _ in 0..n {
let byte = *self.data.get(self.pos / 8)?;
let bit = (byte >> (7 - (self.pos % 8))) & 1;
v = (v << 1) | bit as u64;
self.pos += 1;
}
Some(v)
}
}
struct BitWriter {
buf: Vec<u8>,
bit_pos: u32,
}
impl BitWriter {
fn new() -> Self {
Self {
buf: Vec::new(),
bit_pos: 0,
}
}
fn bits(&mut self, value: u32, n: u32) {
for i in (0..n).rev() {
let bit = ((value >> i) & 1) as u8;
if self.bit_pos.is_multiple_of(8) {
self.buf.push(0);
}
let byte_idx = (self.bit_pos / 8) as usize;
let bit_offset = 7 - (self.bit_pos % 8) as u8;
self.buf[byte_idx] |= bit << bit_offset;
self.bit_pos += 1;
}
}
fn into_bytes(self) -> Vec<u8> {
self.buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_aac_lc_stereo_48k() {
let asc = vec![0x11, 0x90];
let p = parse_aac_asc(&asc).expect("parse should succeed");
assert_eq!(p.aot, 2, "AOT");
assert_eq!(p.sample_rate, 48_000, "sample_rate");
assert_eq!(p.channels, 2, "channels");
assert!(!p.sbr_present);
assert!(!p.ps_present);
assert_eq!(p.signaling, AscSignaling::NoExtension);
assert_eq!(effective_output_channels(&p), 2);
}
#[test]
fn parse_aac_lc_stereo_44_1k() {
let asc = vec![0x12, 0x10];
let p = parse_aac_asc(&asc).expect("parse should succeed");
assert_eq!(p.aot, 2);
assert_eq!(p.sample_rate, 44_100);
assert_eq!(p.channels, 2);
assert_eq!(p.signaling, AscSignaling::NoExtension);
}
#[test]
fn parse_he_aac_v1_5_1_explicit() {
let asc = vec![0x29, 0xB1, 0x88];
let p = parse_aac_asc(&asc).expect("parse should succeed");
assert_eq!(p.aot, 2, "core aot should be LC");
assert_eq!(p.sample_rate, 24_000, "LC core rate (half of SBR)");
assert_eq!(p.channels, 6, "5.1 channel config");
assert!(p.sbr_present, "SBR layer must be present");
assert!(!p.ps_present, "PS not present in v1");
assert_eq!(p.sbr_sample_rate, Some(48_000), "SBR output rate");
assert_eq!(p.signaling, AscSignaling::ExplicitSbr);
assert_eq!(effective_output_channels(&p), 6);
}
#[test]
fn parse_he_aac_v2_mono_ps_explicit() {
let asc = vec![0xEA, 0x0A, 0x08];
let p = parse_aac_asc(&asc).expect("parse should succeed");
assert_eq!(p.aot, 2, "core aot is LC");
assert_eq!(p.sample_rate, 22_050, "core rate (half of SBR)");
assert_eq!(p.channels, 1, "mono channel config (PS upmixes at decode)");
assert!(p.sbr_present, "PS implies SBR");
assert!(p.ps_present, "PS layer signaled");
assert_eq!(p.sbr_sample_rate, Some(44_100));
assert_eq!(p.signaling, AscSignaling::ExplicitPs);
assert_eq!(effective_output_channels(&p), 2);
}
#[test]
fn parse_implicit_he_aac_flagged() {
let asc = vec![0x13, 0x08];
let p = parse_aac_asc(&asc).expect("parse");
assert_eq!(p.aot, 2);
assert_eq!(p.sample_rate, 24_000);
assert_eq!(
p.signaling,
AscSignaling::ImplicitMaybe,
"low-rate AAC-LC must be flagged as implicit-HE candidate"
);
}
#[test]
fn upgrade_24k_mono_lc_to_explicit_he_aac_v1() {
let asc = vec![0x13, 0x08]; let upgraded =
upgrade_to_explicit_signaling(&asc).expect("upgrade should succeed for ≤24 kHz LC");
let reparsed = parse_aac_asc(&upgraded).expect("upgraded ASC parses");
assert_eq!(
reparsed.signaling,
AscSignaling::ExplicitSbr,
"upgraded ASC must be explicit-SBR"
);
assert_eq!(reparsed.sample_rate, 24_000, "core rate is half of SBR");
assert_eq!(
reparsed.sbr_sample_rate,
Some(48_000),
"SBR rate is 2× core"
);
assert_eq!(reparsed.channels, 1);
}
#[test]
fn upgrade_rejects_high_rate_lc() {
let asc = vec![0x11, 0x90]; assert!(upgrade_to_explicit_signaling(&asc).is_none());
}
#[test]
fn upgrade_rejects_already_explicit() {
let asc = vec![0x29, 0x89, 0x98]; assert!(upgrade_to_explicit_signaling(&asc).is_none());
}
#[test]
fn parse_xhe_aac_aot_escape() {
let asc = vec![0xF9, 0x46, 0x40];
let p = parse_aac_asc(&asc).expect("parse");
assert_eq!(p.aot, 42, "xHE-AAC USAC AOT=42 via escape");
assert_eq!(p.sample_rate, 48_000);
assert_eq!(p.channels, 2);
}
#[test]
fn parse_inline_24bit_sample_rate() {
let mut bw = BitWriter::new();
bw.bits(2, 5); bw.bits(0xF, 4); bw.bits(88_200, 24); bw.bits(2, 4); let asc = bw.into_bytes();
let p = parse_aac_asc(&asc).expect("parse");
assert_eq!(p.aot, 2);
assert_eq!(p.sample_rate, 88_200);
assert_eq!(p.channels, 2);
}
#[test]
fn bit_writer_roundtrip() {
let mut bw = BitWriter::new();
bw.bits(0b10101, 5);
bw.bits(0b1100, 4);
let bytes = bw.into_bytes();
assert_eq!(bytes, vec![0xAE, 0x00]);
let mut br = BitReader::new(&bytes);
assert_eq!(br.bits(5), Some(0b10101));
assert_eq!(br.bits(4), Some(0b1100));
}
#[test]
fn parse_aac_lc_5_1_at_48k() {
let asc = vec![0x11, 0xB0];
let p = parse_aac_asc(&asc).expect("parse");
assert_eq!(p.aot, 2);
assert_eq!(p.sample_rate, 48_000);
assert_eq!(p.channels, 6, "5.1 channel config");
assert_eq!(
p.signaling,
AscSignaling::NoExtension,
"48 kHz LC has no implicit HE-AAC interpretation"
);
assert_eq!(effective_output_channels(&p), 6);
}
#[test]
fn parse_aac_lc_7_1_at_48k() {
let asc = vec![0x11, 0xB8];
let p = parse_aac_asc(&asc).expect("parse");
assert_eq!(p.channels, 7, "7.1 channel config");
assert_eq!(effective_output_channels(&p), 7);
}
#[test]
fn parse_rejects_truncated() {
assert!(parse_aac_asc(&[]).is_none());
assert!(parse_aac_asc(&[0x12]).is_none());
}
}