const AAC_AUDIO_SAMPLE_ENTRIES: &[&[u8; 4]] = &[b"mp4a", b"enca"];
pub(super) fn hex_prefix(bytes: &[u8], n: usize) -> String {
let mut out = String::with_capacity(n * 2);
for b in bytes.iter().take(n) {
out.push_str(&format!("{b:02x}"));
}
out
}
pub(super) fn extract_aac_asc(data: &[u8]) -> Option<Vec<u8>> {
let moov = super::super::find_direct_child(data, b"moov")?;
let mut pos = 0;
let mut saw_audio_trak = false;
while pos + 8 <= moov.len() {
let size =
u32::from_be_bytes([moov[pos], moov[pos + 1], moov[pos + 2], moov[pos + 3]]) as usize;
let btype = &moov[pos + 4..pos + 8];
if size < 8 || pos.checked_add(size).is_none_or(|end| end > moov.len()) {
break;
}
if btype == b"trak" {
let trak_body = &moov[pos + 8..pos + size];
if trak_is_audio(trak_body) {
saw_audio_trak = true;
if let Some(asc) = extract_asc_from_trak(trak_body) {
return Some(asc);
}
if let Some(asc) = brute_force_find_asc_in_trak(trak_body) {
tracing::warn!(
asc_len = asc.len(),
"audio passthrough recovered ASC via brute-force esds scan; \
the trak's stsd shape is not in our structured handler. \
Capture this file and add coverage so the structured walk \
finds it next time."
);
return Some(asc);
}
}
}
pos += size;
}
if saw_audio_trak {
tracing::warn!(
"audio passthrough skipped: identified an audio trak via smhd, but no \
stsd entry yielded an AudioSpecificConfig. Possible causes: enca with \
unsupported scheme, sample entry fourcc we don't recognise, esds box \
missing or corrupt, mp4 sanitizer mis-aligned a wave-wrapped esds."
);
} else {
tracing::warn!(
"audio passthrough skipped: no trak had a Sound Media Header (smhd). \
Source may be video-only, or its track headers do not conform to ISOBMFF \
§8.4.5.3 (smhd is required for audio traks)."
);
}
None
}
fn trak_is_audio(trak: &[u8]) -> bool {
super::super::find_box_body(trak, &[b"mdia", b"minf", b"smhd"]).is_some()
}
fn extract_asc_from_trak(trak: &[u8]) -> Option<Vec<u8>> {
let stsd = super::super::find_box_body(trak, &[b"mdia", b"minf", b"stbl", b"stsd"])?;
if stsd.len() < 8 {
tracing::warn!(
stsd_len = stsd.len(),
"audio passthrough: stsd shorter than its 8-byte FullBox preamble"
);
return None;
}
let entries = &stsd[8..];
let mut cursor = 0;
while cursor + 8 <= entries.len() {
let entry_size = u32::from_be_bytes([
entries[cursor],
entries[cursor + 1],
entries[cursor + 2],
entries[cursor + 3],
]) as usize;
let entry_type: &[u8; 4] = entries[cursor + 4..cursor + 8].try_into().unwrap();
if entry_size < 8 || cursor + entry_size > entries.len() {
break;
}
if AAC_AUDIO_SAMPLE_ENTRIES.contains(&entry_type) {
if entry_size >= 36 {
let body = &entries[cursor + 8 + 28..cursor + entry_size];
if let Some(asc) = find_esds_recursive(body) {
return Some(asc);
}
}
}
cursor += entry_size;
}
None
}
fn brute_force_find_asc_in_trak(trak: &[u8]) -> Option<Vec<u8>> {
let mut pos = 0;
while pos + 8 <= trak.len() {
if &trak[pos + 4..pos + 8] == b"esds" {
let size = u32::from_be_bytes([trak[pos], trak[pos + 1], trak[pos + 2], trak[pos + 3]])
as usize;
if size >= 12 && pos + size <= trak.len() {
let esds_body = &trak[pos + 12..pos + size];
if let Some(asc) = extract_asc_from_esds(esds_body) {
if !asc.is_empty() {
return Some(asc);
}
}
}
}
pos += 1;
}
None
}
fn find_esds_recursive(body: &[u8]) -> Option<Vec<u8>> {
let mut pos = 0;
while pos + 8 <= body.len() {
let sub_size =
u32::from_be_bytes([body[pos], body[pos + 1], body[pos + 2], body[pos + 3]]) as usize;
let sub_type = &body[pos + 4..pos + 8];
if sub_size < 8 || pos + sub_size > body.len() {
break;
}
if sub_type == b"esds" {
let esds_body = &body[pos + 8 + 4..pos + sub_size];
return extract_asc_from_esds(esds_body);
}
if sub_type == b"wave" {
if let Some(asc) = find_esds_recursive(&body[pos + 8..pos + sub_size]) {
return Some(asc);
}
}
pos += sub_size;
}
None
}
pub(super) fn mp4_has_aac_sample_entry(data: &[u8]) -> bool {
let Some(moov) = super::super::find_direct_child(data, b"moov") else {
return false;
};
let mut pos = 0;
while pos + 8 <= moov.len() {
let size =
u32::from_be_bytes([moov[pos], moov[pos + 1], moov[pos + 2], moov[pos + 3]]) as usize;
let btype = &moov[pos + 4..pos + 8];
if size < 8 || pos + size > moov.len() {
break;
}
if btype == b"trak" {
let trak_body = &moov[pos + 8..pos + size];
if !trak_is_audio(trak_body) {
pos += size;
continue;
}
if let Some(stsd) =
super::super::find_box_body(trak_body, &[b"mdia", b"minf", b"stbl", b"stsd"])
&& stsd.len() >= 8
{
let entries = &stsd[8..];
let mut cursor = 0;
while cursor + 8 <= entries.len() {
let entry_size = u32::from_be_bytes([
entries[cursor],
entries[cursor + 1],
entries[cursor + 2],
entries[cursor + 3],
]) as usize;
if entry_size < 8 || cursor + entry_size > entries.len() {
break;
}
let entry_type: &[u8; 4] =
entries[cursor + 4..cursor + 8].try_into().unwrap();
if AAC_AUDIO_SAMPLE_ENTRIES.contains(&entry_type) {
return true;
}
cursor += entry_size;
}
}
}
pos += size;
}
false
}
fn extract_asc_from_esds(body: &[u8]) -> Option<Vec<u8>> {
let (tag, payload, _rest) = read_descriptor(body)?;
if tag != 0x03 {
return None;
}
if payload.len() < 3 {
return None;
}
let flags = payload[2];
let mut off = 3;
if flags & 0x80 != 0 {
off += 2;
} if flags & 0x40 != 0 {
if off >= payload.len() {
return None;
}
let url_len = payload[off] as usize;
off += 1 + url_len;
}
if flags & 0x20 != 0 {
off += 2;
} if off > payload.len() {
return None;
}
let mut cursor = &payload[off..];
while !cursor.is_empty() {
let (tag, child, rest) = read_descriptor(cursor)?;
cursor = rest;
if tag != 0x04 {
continue;
}
if child.len() < 13 {
return None;
}
let inner = &child[13..];
let mut inner_cursor = inner;
while !inner_cursor.is_empty() {
let (t, dsi_payload, r) = read_descriptor(inner_cursor)?;
inner_cursor = r;
if t == 0x05 {
return Some(dsi_payload.to_vec());
}
}
return None;
}
None
}
fn read_descriptor(data: &[u8]) -> Option<(u8, &[u8], &[u8])> {
if data.is_empty() {
return None;
}
let tag = data[0];
let mut pos = 1;
let mut length: usize = 0;
for _ in 0..4 {
if pos >= data.len() {
return None;
}
let b = data[pos];
pos += 1;
length = (length << 7) | (b & 0x7F) as usize;
if b & 0x80 == 0 {
break;
}
}
if pos + length > data.len() {
return None;
}
let payload = &data[pos..pos + length];
let rest = &data[pos + length..];
Some((tag, payload, rest))
}
pub(super) fn decode_asc_sample_rate(asc: &[u8]) -> Option<u32> {
if asc.len() < 2 {
return None;
}
let mut br = AscBitReader::new(asc);
let aot = br.bits(5)?;
let _extended_aot = if aot == 31 { br.bits(6)? + 32 } else { aot };
let freq_idx = br.bits(4)? as usize;
if freq_idx == 0xF {
let sr = br.bits(24)?;
Some(sr as u32)
} else {
const FREQS: [u32; 13] = [
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000,
7350,
];
FREQS.get(freq_idx).copied()
}
}
pub(super) fn decode_asc_channels(asc: &[u8]) -> Option<u16> {
if asc.len() < 2 {
return None;
}
let mut br = AscBitReader::new(asc);
let aot = br.bits(5)?;
let _ext = if aot == 31 { br.bits(6)? + 32 } else { aot };
let freq_idx = br.bits(4)? as usize;
if freq_idx == 0xF {
let _ = br.bits(24)?;
}
let chan_cfg = br.bits(4)? as u16;
if chan_cfg == 0 { Some(2) } else { Some(chan_cfg) }
}
struct AscBitReader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> AscBitReader<'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)
}
}