use crate::engine::asc::write_lc;
use crate::error::Result;
const OBJECT_TYPE_AAC: u8 = 0x40;
const STREAM_TYPE_AUDIO: u8 = 0x15;
const PRIMING_SAMPLES: u64 = 1024;
fn be32(out: &mut Vec<u8>, v: u32) {
out.extend_from_slice(&v.to_be_bytes());
}
fn be16(out: &mut Vec<u8>, v: u16) {
out.extend_from_slice(&v.to_be_bytes());
}
fn be64(out: &mut Vec<u8>, v: u64) {
out.extend_from_slice(&v.to_be_bytes());
}
fn start_box(out: &mut Vec<u8>, typ: &[u8; 4]) -> usize {
let at = out.len();
be32(out, 0);
out.extend_from_slice(typ);
at
}
fn end_box(out: &mut [u8], at: usize) {
let size = (out.len() - at) as u32;
out[at..at + 4].copy_from_slice(&size.to_be_bytes());
}
pub fn mux_aac_lc(
payloads: &[Vec<u8>],
fs_index: u8,
channels: usize,
sample_rate: u32,
) -> Result<Vec<u8>> {
let n_frames = payloads.len() as u32;
let total_samples = u64::from(n_frames) * 1024;
let duration_ms = total_samples * 1000 / u64::from(sample_rate.max(1));
let bits_ms = payloads.iter().map(Vec::len).sum::<usize>() as u64 * 8000;
let bitrate = bits_ms.checked_div(duration_ms).unwrap_or(0) as u32;
let asc = write_lc(fs_index, channels as u8);
let mut out = Vec::new();
let ftyp = start_box(&mut out, b"ftyp");
out.extend_from_slice(b"M4A ");
be32(&mut out, 0); out.extend_from_slice(b"M4A mp42isom");
end_box(&mut out, ftyp);
let mdat = start_box(&mut out, b"mdat");
let media_offset = out.len() as u32;
for p in payloads {
out.extend_from_slice(p);
}
end_box(&mut out, mdat);
let moov = start_box(&mut out, b"moov");
write_mvhd(&mut out, duration_ms);
let trak = start_box(&mut out, b"trak");
write_tkhd(&mut out, duration_ms);
write_edts(&mut out, duration_ms);
let mdia = start_box(&mut out, b"mdia");
write_mdhd(&mut out, sample_rate, total_samples);
write_hdlr(&mut out);
let minf = start_box(&mut out, b"minf");
let smhd = start_box(&mut out, b"smhd");
be32(&mut out, 0); be16(&mut out, 0); be16(&mut out, 0);
end_box(&mut out, smhd);
write_dinf(&mut out);
let stbl = start_box(&mut out, b"stbl");
write_stsd(&mut out, &asc, channels, sample_rate, bitrate);
write_stts(&mut out, n_frames);
write_stsc(&mut out, n_frames);
write_stsz(&mut out, payloads);
let stco = start_box(&mut out, b"stco");
be32(&mut out, 0);
be32(&mut out, 1);
be32(&mut out, media_offset);
end_box(&mut out, stco);
end_box(&mut out, stbl);
end_box(&mut out, minf);
end_box(&mut out, mdia);
end_box(&mut out, trak);
end_box(&mut out, moov);
Ok(out)
}
fn write_matrix(out: &mut Vec<u8>) {
for v in [0x0001_0000u32, 0, 0, 0, 0x0001_0000, 0, 0, 0, 0x4000_0000] {
be32(out, v);
}
}
fn write_mvhd(out: &mut Vec<u8>, duration_ms: u64) {
let b = start_box(out, b"mvhd");
be32(out, 0); be32(out, 0); be32(out, 0); be32(out, 1000); be32(out, duration_ms.min(u64::from(u32::MAX)) as u32);
be32(out, 0x0001_0000); be16(out, 0x0100); be16(out, 0);
be64(out, 0);
write_matrix(out);
for _ in 0..6 {
be32(out, 0); }
be32(out, 2); end_box(out, b);
}
fn write_tkhd(out: &mut Vec<u8>, duration_ms: u64) {
let b = start_box(out, b"tkhd");
be32(out, 0x0000_0003); be32(out, 0); be32(out, 0); be32(out, 1); be32(out, 0);
be32(out, duration_ms.min(u64::from(u32::MAX)) as u32);
be64(out, 0);
be16(out, 0); be16(out, 0); be16(out, 0x0100); be16(out, 0);
write_matrix(out);
be32(out, 0); be32(out, 0); end_box(out, b);
}
fn write_edts(out: &mut Vec<u8>, duration_ms: u64) {
let edts = start_box(out, b"edts");
let elst = start_box(out, b"elst");
be32(out, 0); be32(out, 1); be32(out, duration_ms.min(u64::from(u32::MAX)) as u32); be32(out, PRIMING_SAMPLES as u32); be16(out, 1); be16(out, 0); end_box(out, elst);
end_box(out, edts);
}
fn write_mdhd(out: &mut Vec<u8>, sample_rate: u32, total_samples: u64) {
let b = start_box(out, b"mdhd");
be32(out, 0); be32(out, 0); be32(out, 0); be32(out, sample_rate); be32(out, total_samples.min(u64::from(u32::MAX)) as u32);
be16(out, 0x55C4); be16(out, 0);
end_box(out, b);
}
fn write_hdlr(out: &mut Vec<u8>) {
let b = start_box(out, b"hdlr");
be32(out, 0); be32(out, 0); out.extend_from_slice(b"soun");
be32(out, 0);
be32(out, 0);
be32(out, 0); out.push(0); end_box(out, b);
}
fn write_dinf(out: &mut Vec<u8>) {
let dinf = start_box(out, b"dinf");
let dref = start_box(out, b"dref");
be32(out, 0); be32(out, 1); let url = start_box(out, b"url ");
be32(out, 1); end_box(out, url);
end_box(out, dref);
end_box(out, dinf);
}
fn write_stsd(out: &mut Vec<u8>, asc: &[u8], channels: usize, sample_rate: u32, bitrate: u32) {
let stsd = start_box(out, b"stsd");
be32(out, 0); be32(out, 1); let mp4a = start_box(out, b"mp4a");
for _ in 0..6 {
out.push(0); }
be16(out, 1); be16(out, 0); be16(out, 0); be32(out, 0); be16(out, channels as u16);
be16(out, 16); be16(out, 0); be16(out, 0); be32(out, sample_rate << 16); write_esds(out, asc, bitrate);
end_box(out, mp4a);
end_box(out, stsd);
}
fn write_esds(out: &mut Vec<u8>, asc: &[u8], bitrate: u32) {
let esds = start_box(out, b"esds");
be32(out, 0); let dc_len = 13 + 2 + asc.len() + 2 + 1;
let es_len = 3 + 2 + dc_len;
out.push(0x03);
out.push(es_len as u8);
be16(out, 1); out.push(0); out.push(0x04);
out.push(dc_len as u8);
out.push(OBJECT_TYPE_AAC);
out.push(STREAM_TYPE_AUDIO);
out.push(0);
out.push(0);
out.push(0);
be32(out, bitrate); be32(out, bitrate); out.push(0x05);
out.push(asc.len() as u8);
out.extend_from_slice(asc);
out.push(0x06);
out.push(1);
out.push(2); end_box(out, esds);
}
fn write_stts(out: &mut Vec<u8>, n_frames: u32) {
let b = start_box(out, b"stts");
be32(out, 0);
be32(out, 1); be32(out, n_frames);
be32(out, 1024);
end_box(out, b);
}
fn write_stsc(out: &mut Vec<u8>, n_frames: u32) {
let b = start_box(out, b"stsc");
be32(out, 0);
be32(out, 1); be32(out, 1); be32(out, n_frames); be32(out, 1); end_box(out, b);
}
fn write_stsz(out: &mut Vec<u8>, payloads: &[Vec<u8>]) {
let b = start_box(out, b"stsz");
be32(out, 0);
be32(out, 0); be32(out, payloads.len() as u32);
for p in payloads {
be32(out, p.len() as u32);
}
end_box(out, b);
}
#[cfg(test)]
#[path = "m4a_write_tests.rs"]
mod m4a_write_tests;