pub(super) fn extract_mp4_ac3_dac3_body(data: &[u8]) -> Option<Vec<u8>> {
extract_mp4_audio_config_body(data, b"ac-3", b"dac3")
}
pub(super) fn extract_mp4_eac3_dec3_body(data: &[u8]) -> Option<Vec<u8>> {
extract_mp4_audio_config_body(data, b"ec-3", b"dec3")
}
fn extract_mp4_audio_config_body(
data: &[u8],
entry_fourcc: &[u8; 4],
cfg_fourcc: &[u8; 4],
) -> Option<Vec<u8>> {
let moov = super::super::find_direct_child(data, b"moov")?;
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.checked_add(size).is_none_or(|end| end > moov.len()) {
break;
}
if btype == b"trak" {
let trak_body = &moov[pos + 8..pos + size];
if let Some(cfg) = extract_audio_cfg_from_trak(trak_body, entry_fourcc, cfg_fourcc) {
return Some(cfg);
}
}
pos += size;
}
None
}
fn extract_audio_cfg_from_trak(
trak: &[u8],
entry_fourcc: &[u8; 4],
cfg_fourcc: &[u8; 4],
) -> Option<Vec<u8>> {
let stsd = super::super::find_box_body(trak, &[b"mdia", b"minf", b"stbl", b"stsd"])?;
if stsd.len() < 16 {
return None;
}
let mut pos = 8; while pos + 8 <= stsd.len() {
let entry_size =
u32::from_be_bytes([stsd[pos], stsd[pos + 1], stsd[pos + 2], stsd[pos + 3]]) as usize;
let entry_type: [u8; 4] = stsd[pos + 4..pos + 8].try_into().ok()?;
if entry_size < 8 || pos.saturating_add(entry_size) > stsd.len() {
break;
}
if &entry_type == entry_fourcc {
let end = pos + entry_size;
let child_start = pos + 8 + 28;
if child_start >= end {
return None;
}
return super::super::find_direct_child(&stsd[child_start..end], cfg_fourcc)
.map(|b| b.to_vec());
}
pos += entry_size;
}
None
}
pub(crate) fn ac3_sample_rate_channels_from_dac3(dac3: &[u8]) -> Option<(u32, u16)> {
if dac3.len() < 3 {
return None;
}
let raw = ((dac3[0] as u32) << 16) | ((dac3[1] as u32) << 8) | dac3[2] as u32;
let fscod = ((raw >> 22) & 0x03) as u8;
let acmod = ((raw >> 11) & 0x07) as u8;
let lfeon = ((raw >> 10) & 0x01) == 1;
let sr = match fscod {
0 => 48_000,
1 => 44_100,
2 => 32_000,
_ => return None,
};
Some((sr, crate::ac3_sync::channel_count(acmod, lfeon)))
}
pub(crate) fn eac3_sample_rate_channels_from_dec3(dec3: &[u8]) -> Option<(u32, u16)> {
if dec3.len() < 5 {
return None;
}
let raw_be = u64::from(dec3[0]) << 32
| u64::from(dec3[1]) << 24
| u64::from(dec3[2]) << 16
| u64::from(dec3[3]) << 8
| u64::from(dec3[4]);
let fscod = ((raw_be >> 22) & 0x03) as u8;
let acmod = ((raw_be >> 9) & 0x07) as u8;
let lfeon = ((raw_be >> 8) & 0x01) == 1;
let sr = crate::ac3_sync::eac3_sample_rate_hz(fscod, 0);
if sr == 0 {
return None;
}
Some((sr, crate::ac3_sync::channel_count(acmod, lfeon)))
}