use crate::AudioInfo;
use crate::ac3_sync::{Ac3SyncInfo, Eac3SyncInfo};
use super::boxes::BoxBuilder;
use super::boxes::write_unity_matrix;
use super::video_track::{build_mdhd, build_dinf};
use super::sample_table::{AudioBuildPlan, build_stsc, build_stsz, build_stco, build_co64};
use super::AudioCodecKind;
pub(super) fn build_audio_trak(
plan: &AudioBuildPlan,
duration_in_movie_ts: u64,
chunk_offsets: &[u64],
use_co64: bool,
) -> Vec<u8> {
let tkhd = build_audio_tkhd(duration_in_movie_ts);
let mdia = build_audio_mdia(plan, chunk_offsets, use_co64);
let mut b = BoxBuilder::new(b"trak");
b.extend(&tkhd);
b.extend(&mdia);
b.finish()
}
fn build_audio_tkhd(duration_in_movie_ts: u64) -> Vec<u8> {
let mut b = BoxBuilder::new(b"tkhd");
b.u8(0); b.extend(&[0, 0, 0x03]); b.u32(0); b.u32(0); b.u32(2); b.u32(0); b.u32(duration_in_movie_ts as u32);
b.u32(0); b.u32(0);
b.u16(0); b.u16(0x0001); b.u16(0x0100); b.u16(0); write_unity_matrix(&mut b);
b.u32(0); b.u32(0); b.finish()
}
fn build_audio_mdia(plan: &AudioBuildPlan, chunk_offsets: &[u64], use_co64: bool) -> Vec<u8> {
let mdhd = build_mdhd(plan.info.timescale, plan.total_duration_in_own_ts);
let hdlr = build_audio_hdlr();
let minf = build_audio_minf(plan, chunk_offsets, use_co64);
let mut b = BoxBuilder::new(b"mdia");
b.extend(&mdhd);
b.extend(&hdlr);
b.extend(&minf);
b.finish()
}
fn build_audio_hdlr() -> Vec<u8> {
let mut b = BoxBuilder::new(b"hdlr");
b.u8(0);
b.extend(&[0, 0, 0]);
b.u32(0); b.extend(b"soun"); b.u32(0);
b.u32(0);
b.u32(0);
b.extend(b"SoundHandler\0");
b.finish()
}
fn build_audio_minf(plan: &AudioBuildPlan, chunk_offsets: &[u64], use_co64: bool) -> Vec<u8> {
let smhd = build_smhd();
let dinf = build_dinf();
let stbl = build_audio_stbl(plan, chunk_offsets, use_co64);
let mut b = BoxBuilder::new(b"minf");
b.extend(&smhd);
b.extend(&dinf);
b.extend(&stbl);
b.finish()
}
fn build_smhd() -> Vec<u8> {
let mut b = BoxBuilder::new(b"smhd");
b.u8(0);
b.extend(&[0, 0, 0]); b.u16(0); b.u16(0); b.finish()
}
fn build_audio_stbl(plan: &AudioBuildPlan, chunk_offsets: &[u64], use_co64: bool) -> Vec<u8> {
let stsd = build_audio_stsd(&plan.info);
let stts = build_audio_stts(&plan.durations);
let stsc = build_stsc(plan.sample_sizes.len() as u32, plan.samples_per_chunk);
let stsz = build_stsz(&plan.sample_sizes);
let chunk_offset_box = if use_co64 {
build_co64(chunk_offsets)
} else {
build_stco(chunk_offsets)
};
let mut b = BoxBuilder::new(b"stbl");
b.extend(&stsd);
b.extend(&stts);
b.extend(&stsc);
b.extend(&stsz);
b.extend(&chunk_offset_box);
b.finish()
}
pub(crate) fn build_audio_stsd(info: &AudioInfo) -> Vec<u8> {
let kind = AudioCodecKind::from_codec_tag(&info.codec)
.expect("with_audio gate already validated codec tag");
let entry = match kind {
AudioCodecKind::Aac => build_mp4a(info),
AudioCodecKind::Opus => build_opus_sample_entry(info),
AudioCodecKind::Ac3 => build_ac3_sample_entry(info),
AudioCodecKind::Eac3 => build_ec3_sample_entry(info),
};
let mut b = BoxBuilder::new(b"stsd");
b.u8(0);
b.extend(&[0, 0, 0]);
b.u32(1); b.extend(&entry);
b.finish()
}
pub(super) fn build_mp4a(info: &AudioInfo) -> Vec<u8> {
let mut b = BoxBuilder::new(b"mp4a");
for _ in 0..6 {
b.u8(0);
} b.u16(1); b.u32(0); b.u32(0); b.u16(info.channels); b.u16(16); b.u16(0); b.u16(0); b.u32(info.sample_rate << 16); b.extend(&build_esds(info));
if let Some(chan) = build_chan_box(info.channels) {
b.extend(&chan);
}
b.finish()
}
pub(crate) fn build_chan_box(channels: u16) -> Option<Vec<u8>> {
let tag: u32 = match channels {
1 | 2 => return None, 6 => (114u32 << 16) | 6, 7 => (127u32 << 16) | 8, _ => return None, };
let mut b = BoxBuilder::new(b"chan");
b.u32(tag); b.u32(0); b.u32(0); Some(b.finish())
}
pub(super) fn build_opus_sample_entry(info: &AudioInfo) -> Vec<u8> {
let mut b = BoxBuilder::new(b"Opus");
for _ in 0..6 {
b.u8(0);
} b.u16(1); b.u32(0); b.u32(0); b.u16(info.channels); b.u16(16); b.u16(0); b.u16(0); b.u32(48_000u32 << 16); b.extend(&build_dops(info));
b.finish()
}
pub(super) fn build_dops(info: &AudioInfo) -> Vec<u8> {
let p = &info.codec_private;
debug_assert!(
p.len() >= 11,
"with_audio gate must enforce dOps minimum size"
);
let output_channels = p[1];
let pre_skip = u16::from_le_bytes([p[2], p[3]]);
let input_sample_rate = u32::from_le_bytes([p[4], p[5], p[6], p[7]]);
let output_gain = i16::from_le_bytes([p[8], p[9]]);
let channel_mapping_family = p[10];
let mut b = BoxBuilder::new(b"dOps");
b.u8(0); b.u8(output_channels); b.u16(pre_skip); b.u32(input_sample_rate); b.u16(output_gain as u16); b.u8(channel_mapping_family);
if channel_mapping_family != 0 {
let trailer_len = 2 + output_channels as usize;
debug_assert!(
p.len() >= 11 + trailer_len,
"family={channel_mapping_family} requires {trailer_len} more bytes after the 11-byte preamble; codec_private has {}",
p.len()
);
b.u8(p[11]); b.u8(p[12]); for i in 0..output_channels as usize {
b.u8(p[13 + i]); }
}
b.finish()
}
pub(super) fn build_ac3_sample_entry(info: &AudioInfo) -> Vec<u8> {
let mut b = BoxBuilder::new(b"ac-3");
for _ in 0..6 {
b.u8(0);
} b.u16(1); b.u32(0); b.u32(0); b.u16(info.channels); b.u16(16); b.u16(0); b.u16(0); b.u32(info.sample_rate << 16); b.extend(&build_dac3(info)); b.finish()
}
pub(super) fn build_ec3_sample_entry(info: &AudioInfo) -> Vec<u8> {
let mut b = BoxBuilder::new(b"ec-3");
for _ in 0..6 {
b.u8(0);
}
b.u16(1);
b.u32(0);
b.u32(0);
b.u16(info.channels);
b.u16(16);
b.u16(0);
b.u16(0);
b.u32(info.sample_rate << 16);
b.extend(&build_dec3(info));
b.finish()
}
pub(super) fn build_dac3(info: &AudioInfo) -> Vec<u8> {
debug_assert_eq!(
info.codec_private.len(),
3,
"with_audio gate must enforce dac3 body == 3 bytes"
);
let mut b = BoxBuilder::new(b"dac3");
b.extend(&info.codec_private);
b.finish()
}
pub(super) fn build_dec3(info: &AudioInfo) -> Vec<u8> {
debug_assert!(
info.codec_private.len() >= 5,
"with_audio gate must enforce dec3 body >= 5 bytes"
);
let mut b = BoxBuilder::new(b"dec3");
b.extend(&info.codec_private);
b.finish()
}
pub fn dac3_body_from_sync(s: &Ac3SyncInfo) -> [u8; 3] {
let mut bw = MsbBitWriter::new();
bw.put(2, s.fscod as u32);
bw.put(5, s.bsid as u32);
bw.put(3, s.bsmod as u32);
bw.put(3, s.acmod as u32);
bw.put(1, if s.lfeon { 1 } else { 0 });
bw.put(5, s.bit_rate_code as u32);
bw.put(5, 0); let bytes = bw.finish();
[bytes[0], bytes[1], bytes[2]]
}
pub fn dec3_body_from_sync(s: &Eac3SyncInfo, data_rate_div2_kbps: u16) -> [u8; 5] {
let mut bw = MsbBitWriter::new();
bw.put(13, (data_rate_div2_kbps & 0x1FFF) as u32);
bw.put(3, 0); bw.put(2, s.fscod as u32);
bw.put(5, 16); bw.put(1, 0); bw.put(1, 0); bw.put(3, s.bsmod as u32);
bw.put(3, s.acmod as u32);
bw.put(1, if s.lfeon { 1 } else { 0 });
bw.put(3, 0); bw.put(4, 0); let bytes = bw.finish();
debug_assert_eq!(bytes.len(), 5, "dec3 single-substream body must be 5 bytes");
[bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]]
}
struct MsbBitWriter {
bytes: Vec<u8>,
bit_pos: usize,
}
impl MsbBitWriter {
fn new() -> Self {
Self {
bytes: Vec::new(),
bit_pos: 0,
}
}
fn put(&mut self, n: usize, v: u32) {
debug_assert!(n <= 24);
for i in (0..n).rev() {
let bit = ((v >> i) & 0x01) as u8;
if self.bit_pos.is_multiple_of(8) {
self.bytes.push(0);
}
let byte_idx = self.bit_pos / 8;
let bit_idx = 7 - (self.bit_pos % 8);
self.bytes[byte_idx] |= bit << bit_idx;
self.bit_pos += 1;
}
}
fn finish(self) -> Vec<u8> {
self.bytes
}
}
fn build_esds(info: &AudioInfo) -> Vec<u8> {
let asc_len = info.asc_bytes.len() as u32;
let mut dsi = Vec::new();
dsi.push(0x05u8);
write_descriptor_length(&mut dsi, asc_len);
dsi.extend_from_slice(&info.asc_bytes);
let mut dcd_payload = Vec::new();
dcd_payload.push(0x40); dcd_payload.push((0x05 << 2) | 0x01); dcd_payload.extend_from_slice(&[0, 0, 0]); dcd_payload.extend_from_slice(&0u32.to_be_bytes()); dcd_payload.extend_from_slice(&0u32.to_be_bytes()); dcd_payload.extend_from_slice(&dsi);
let mut dcd = Vec::new();
dcd.push(0x04);
write_descriptor_length(&mut dcd, dcd_payload.len() as u32);
dcd.extend_from_slice(&dcd_payload);
let mut slc = Vec::new();
slc.push(0x06);
write_descriptor_length(&mut slc, 1);
slc.push(0x02);
let mut es_payload = Vec::new();
es_payload.extend_from_slice(&0u16.to_be_bytes()); es_payload.push(0); es_payload.extend_from_slice(&dcd);
es_payload.extend_from_slice(&slc);
let mut es = Vec::new();
es.push(0x03);
write_descriptor_length(&mut es, es_payload.len() as u32);
es.extend_from_slice(&es_payload);
let mut b = BoxBuilder::new(b"esds");
b.u8(0);
b.extend(&[0, 0, 0]);
b.extend(&es);
b.finish()
}
fn write_descriptor_length(buf: &mut Vec<u8>, len: u32) {
if len < 128 {
buf.push(len as u8);
return;
}
buf.push(((len >> 21) & 0x7F) as u8 | 0x80);
buf.push(((len >> 14) & 0x7F) as u8 | 0x80);
buf.push(((len >> 7) & 0x7F) as u8 | 0x80);
buf.push((len & 0x7F) as u8);
}
pub(super) fn build_audio_stts(durations: &[u32]) -> Vec<u8> {
let mut b = BoxBuilder::new(b"stts");
b.u8(0);
b.extend(&[0, 0, 0]);
let mut runs: Vec<(u32, u32)> = Vec::new();
for &d in durations {
if let Some(last) = runs.last_mut()
&& last.1 == d
{
last.0 += 1;
continue;
}
runs.push((1, d));
}
b.u32(runs.len() as u32);
for (count, delta) in runs {
b.u32(count);
b.u32(delta);
}
b.finish()
}