use crate::mux::BoxBuilder;
use super::{CmafSample, SampleFlags};
pub fn build_mfhd(sequence_number: u32) -> Vec<u8> {
let mut b = BoxBuilder::new(b"mfhd");
b.u8(0); b.extend(&[0, 0, 0]); b.u32(sequence_number);
b.finish()
}
pub fn build_tfhd(
track_id: u32,
default_duration: Option<u32>,
default_size: Option<u32>,
default_flags: Option<u32>,
) -> Vec<u8> {
let mut tf_flags: u32 = 0x020000; if default_duration.is_some() {
tf_flags |= 0x000008;
}
if default_size.is_some() {
tf_flags |= 0x000010;
}
if default_flags.is_some() {
tf_flags |= 0x000020;
}
let mut b = BoxBuilder::new(b"tfhd");
b.u8(0); let flag_bytes = tf_flags.to_be_bytes();
b.extend(&flag_bytes[1..]); b.u32(track_id);
if let Some(d) = default_duration {
b.u32(d);
}
if let Some(s) = default_size {
b.u32(s);
}
if let Some(f) = default_flags {
b.u32(f);
}
b.finish()
}
pub fn build_tfdt(base_media_decode_time: u64) -> Vec<u8> {
let mut b = BoxBuilder::new(b"tfdt");
b.u8(1); b.extend(&[0, 0, 0]); b.u64(base_media_decode_time);
b.finish()
}
fn build_trun_video(samples: &[CmafSample]) -> (Vec<u8>, usize) {
let mut b = BoxBuilder::new(b"trun");
b.u8(0); let flags: u32 = 0x000001 | 0x000004 | 0x000100 | 0x000200;
let flag_bytes = flags.to_be_bytes();
b.extend(&flag_bytes[1..]);
b.u32(samples.len() as u32);
let data_offset_pos_within_trun = b.current_len();
b.u32(0);
if let Some(first) = samples.first() {
b.u32(first.flags.pack());
} else {
b.u32(0);
}
for s in samples {
b.u32(s.duration);
b.u32(s.size);
}
let bytes = b.finish();
(bytes, data_offset_pos_within_trun)
}
fn build_trun_audio(samples: &[CmafSample]) -> (Vec<u8>, usize) {
let mut b = BoxBuilder::new(b"trun");
b.u8(0); let flags: u32 = 0x000001 | 0x000100 | 0x000200;
let flag_bytes = flags.to_be_bytes();
b.extend(&flag_bytes[1..]);
b.u32(samples.len() as u32);
let data_offset_pos_within_trun = b.current_len();
b.u32(0);
for s in samples {
b.u32(s.duration);
b.u32(s.size);
}
let bytes = b.finish();
(bytes, data_offset_pos_within_trun)
}
fn build_traf(tfhd: &[u8], tfdt: &[u8], trun: &[u8]) -> Vec<u8> {
let mut b = BoxBuilder::new(b"traf");
b.extend(tfhd);
b.extend(tfdt);
b.extend(trun);
b.finish()
}
pub struct MoofData {
pub bytes: Vec<u8>,
pub data_offset_pos: usize,
}
impl MoofData {
pub fn patch_data_offset(&mut self, data_offset: u32) {
self.bytes[self.data_offset_pos..self.data_offset_pos + 4]
.copy_from_slice(&data_offset.to_be_bytes());
}
pub fn patch_default_no_gap(&mut self) {
let off = (self.bytes.len() + 8) as u32;
self.patch_data_offset(off);
}
}
pub fn build_moof_video(
sequence_number: u32,
track_id: u32,
base_media_decode_time: u64,
samples: &[CmafSample],
) -> MoofData {
let mfhd = build_mfhd(sequence_number);
let tfhd = build_tfhd(
track_id,
None,
None,
Some(SampleFlags::delta_frame().pack()),
);
let tfdt = build_tfdt(base_media_decode_time);
let (trun, data_offset_pos_within_trun) = build_trun_video(samples);
let moof_header = 8usize;
let traf_header = 8usize;
let pos_in_moof = moof_header
+ mfhd.len()
+ traf_header
+ tfhd.len()
+ tfdt.len()
+ data_offset_pos_within_trun;
let traf = build_traf(&tfhd, &tfdt, &trun);
let mut b = BoxBuilder::new(b"moof");
b.extend(&mfhd);
b.extend(&traf);
let bytes = b.finish();
MoofData {
bytes,
data_offset_pos: pos_in_moof,
}
}
pub fn build_moof_audio(
sequence_number: u32,
track_id: u32,
base_media_decode_time: u64,
samples: &[CmafSample],
) -> MoofData {
let mfhd = build_mfhd(sequence_number);
let tfhd = build_tfhd(track_id, None, None, Some(SampleFlags::keyframe().pack()));
let tfdt = build_tfdt(base_media_decode_time);
let (trun, data_offset_pos_within_trun) = build_trun_audio(samples);
let moof_header = 8usize;
let traf_header = 8usize;
let pos_in_moof = moof_header
+ mfhd.len()
+ traf_header
+ tfhd.len()
+ tfdt.len()
+ data_offset_pos_within_trun;
let traf = build_traf(&tfhd, &tfdt, &trun);
let mut b = BoxBuilder::new(b"moof");
b.extend(&mfhd);
b.extend(&traf);
let bytes = b.finish();
MoofData {
bytes,
data_offset_pos: pos_in_moof,
}
}