pub(super) fn extract_mp4_opus_dops_body(data: &[u8]) -> 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(dops) = extract_dops_from_trak(trak_body) {
return Some(dops);
}
}
pos += size;
}
None
}
fn extract_dops_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() < 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 == b"Opus" {
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], b"dOps")
.map(|b| b.to_vec());
}
pos += entry_size;
}
None
}
pub(super) fn dops_to_opus_head(dops: &[u8]) -> Option<Vec<u8>> {
if dops.len() < 11 {
return None;
}
let output_channels = dops[1];
let pre_skip = u16::from_be_bytes([dops[2], dops[3]]);
let input_sample_rate = u32::from_be_bytes([dops[4], dops[5], dops[6], dops[7]]);
let output_gain = i16::from_be_bytes([dops[8], dops[9]]);
let channel_mapping_family = dops[10];
let extra_tail = if channel_mapping_family != 0 {
if dops.len() < 13 {
return None;
}
let tail_len = 2 + dops[12] as usize;
if dops.len() < 11 + tail_len {
return None;
}
dops[11..11 + tail_len].to_vec()
} else {
Vec::new()
};
let mut head = Vec::with_capacity(11 + extra_tail.len());
head.push(1u8); head.push(output_channels);
head.extend_from_slice(&pre_skip.to_le_bytes());
head.extend_from_slice(&input_sample_rate.to_le_bytes());
head.extend_from_slice(&(output_gain as u16).to_le_bytes());
head.push(channel_mapping_family);
head.extend_from_slice(&extra_tail);
Some(head)
}