pub mod nal_unit_type {
pub const NON_IDR_SLICE: u8 = 1;
pub const IDR_SLICE: u8 = 5;
pub const SEI: u8 = 6;
pub const SPS: u8 = 7;
pub const PPS: u8 = 8;
}
#[derive(Debug, Clone)]
pub struct NalUnit {
pub data: Vec<u8>,
pub nal_type: u8,
}
impl NalUnit {
pub fn is_idr(&self) -> bool {
self.nal_type == nal_unit_type::IDR_SLICE
}
pub fn is_sps(&self) -> bool {
self.nal_type == nal_unit_type::SPS
}
pub fn is_pps(&self) -> bool {
self.nal_type == nal_unit_type::PPS
}
pub fn is_slice(&self) -> bool {
self.nal_type == nal_unit_type::IDR_SLICE || self.nal_type == nal_unit_type::NON_IDR_SLICE
}
pub fn to_annex_b(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(4 + self.data.len());
result.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
result.extend_from_slice(&self.data);
result
}
}
#[derive(Debug)]
pub struct ParsedFrame {
pub nals: Vec<NalUnit>,
pub sps: Option<Vec<u8>>,
pub pps: Option<Vec<u8>>,
pub is_keyframe: bool,
}
pub fn parse_annex_b(data: &[u8]) -> ParsedFrame {
let mut nals = Vec::new();
let mut sps = None;
let mut pps = None;
let mut is_keyframe = false;
let mut nal_starts = Vec::new();
let mut i = 0;
while i < data.len() {
if i + 3 < data.len()
&& data[i] == 0
&& data[i + 1] == 0
&& data[i + 2] == 0
&& data[i + 3] == 1
{
nal_starts.push(i + 4);
i += 4;
} else if i + 2 < data.len() && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 {
nal_starts.push(i + 3);
i += 3;
} else {
i += 1;
}
}
for (idx, &start) in nal_starts.iter().enumerate() {
if start >= data.len() {
continue;
}
let end = if idx + 1 < nal_starts.len() {
let next_start = nal_starts[idx + 1];
if next_start >= 4
&& data[next_start - 4] == 0
&& data[next_start - 3] == 0
&& data[next_start - 2] == 0
&& data[next_start - 1] == 1
{
next_start - 4
} else if next_start >= 3
&& data[next_start - 3] == 0
&& data[next_start - 2] == 0
&& data[next_start - 1] == 1
{
next_start - 3
} else {
next_start
}
} else {
data.len()
};
if start >= end {
continue;
}
let nal_data = &data[start..end];
let nal_type = nal_data[0] & 0x1F;
match nal_type {
nal_unit_type::SPS => {
sps = Some(nal_data.to_vec());
}
nal_unit_type::PPS => {
pps = Some(nal_data.to_vec());
}
nal_unit_type::IDR_SLICE => {
is_keyframe = true;
nals.push(NalUnit {
data: nal_data.to_vec(),
nal_type,
});
}
nal_unit_type::NON_IDR_SLICE => {
nals.push(NalUnit {
data: nal_data.to_vec(),
nal_type,
});
}
_ => {
nals.push(NalUnit {
data: nal_data.to_vec(),
nal_type,
});
}
}
}
ParsedFrame {
nals,
sps,
pps,
is_keyframe,
}
}
pub mod obu_type {
pub const SEQUENCE_HEADER: u8 = 1;
pub const TEMPORAL_DELIMITER: u8 = 2;
pub const FRAME_HEADER: u8 = 3;
pub const TILE_GROUP: u8 = 4;
pub const FRAME: u8 = 6;
}
#[derive(Debug, Clone)]
pub struct Obu {
pub obu_type: u8,
pub data: Vec<u8>,
}
fn read_leb128(data: &[u8], offset: usize) -> (u64, usize) {
let mut value: u64 = 0;
let mut bytes_read = 0;
for i in 0..8 {
if offset + i >= data.len() {
break;
}
let byte = data[offset + i];
value |= ((byte & 0x7F) as u64) << (i * 7);
bytes_read += 1;
if byte & 0x80 == 0 {
break;
}
}
(value, bytes_read)
}
pub fn parse_av1_obus(data: &[u8]) -> Vec<Obu> {
let mut obus = Vec::new();
let mut offset = 0;
while offset < data.len() {
let header_byte = data[offset];
let obu_type = (header_byte >> 3) & 0x0F;
let has_extension = (header_byte >> 2) & 1 == 1;
let has_size = (header_byte >> 1) & 1 == 1;
let mut header_size = 1;
if has_extension {
header_size += 1;
}
if !has_size {
obus.push(Obu {
obu_type,
data: data[offset..].to_vec(),
});
break;
}
let (obu_size, leb_bytes) = read_leb128(data, offset + header_size);
header_size += leb_bytes;
let total_size = header_size + obu_size as usize;
let end = (offset + total_size).min(data.len());
obus.push(Obu {
obu_type,
data: data[offset..end].to_vec(),
});
offset = end;
}
obus
}
pub fn extract_av1_sequence_header(data: &[u8]) -> Option<Vec<u8>> {
for obu in parse_av1_obus(data) {
if obu.obu_type == obu_type::SEQUENCE_HEADER {
return Some(obu.data);
}
}
None
}
#[derive(Debug)]
pub struct ParsedAv1Frame {
pub sequence_header: Option<Vec<u8>>,
pub is_keyframe: bool,
pub data: Vec<u8>,
}
pub fn parse_av1_frame(data: &[u8]) -> ParsedAv1Frame {
let obus = parse_av1_obus(data);
let sequence_header = obus
.iter()
.find(|o| o.obu_type == obu_type::SEQUENCE_HEADER)
.map(|o| o.data.clone());
let is_keyframe = sequence_header.is_some();
ParsedAv1Frame {
sequence_header,
is_keyframe,
data: data.to_vec(),
}
}
#[derive(Debug, Clone)]
pub struct CmafConfig {
pub fragment_duration_ms: u32,
pub timescale: u32,
}
impl Default for CmafConfig {
fn default() -> Self {
Self {
fragment_duration_ms: 2000,
timescale: 90000,
}
}
}
#[derive(Debug, Clone)]
struct PendingFrame {
data: Vec<u8>,
duration: u32,
is_sync: bool,
composition_offset: i32,
}
pub struct CmafMuxer {
config: CmafConfig,
initialized: bool,
width: u32,
height: u32,
sps: Vec<u8>,
pps: Vec<u8>,
pending_frames: Vec<PendingFrame>,
sequence_number: u32,
fragment_base_dts: i64,
last_dts: i64,
track_id: u32,
}
impl CmafMuxer {
pub fn new(config: CmafConfig) -> Self {
Self {
config,
initialized: false,
width: 0,
height: 0,
sps: Vec::new(),
pps: Vec::new(),
pending_frames: Vec::new(),
sequence_number: 1,
fragment_base_dts: 0,
last_dts: 0,
track_id: 1,
}
}
pub fn create_init_segment(
&mut self,
sps: &[u8],
pps: &[u8],
width: u32,
height: u32,
) -> Vec<u8> {
self.sps = sps.to_vec();
self.pps = pps.to_vec();
self.width = width;
self.height = height;
self.initialized = true;
let mut buf = Vec::new();
self.write_ftyp(&mut buf);
self.write_moov(&mut buf);
buf
}
pub fn add_frame(
&mut self,
nal_units: &[NalUnit],
pts: i64,
dts: i64,
duration: u32,
is_keyframe: bool,
) -> Option<Vec<u8>> {
if !self.initialized {
return None;
}
let should_flush = if self.pending_frames.is_empty() {
false
} else {
let fragment_duration =
(dts - self.fragment_base_dts) * 1000 / self.config.timescale as i64;
is_keyframe && fragment_duration >= self.config.fragment_duration_ms as i64
};
let segment = if should_flush {
Some(self.flush_fragment())
} else {
None
};
let data = self.nal_units_to_avcc(nal_units);
if self.pending_frames.is_empty() {
self.fragment_base_dts = dts;
}
let composition_offset = (pts - dts) as i32;
self.pending_frames.push(PendingFrame {
data,
duration,
is_sync: is_keyframe,
composition_offset,
});
self.last_dts = dts;
segment
}
pub fn flush(&mut self) -> Option<Vec<u8>> {
if self.pending_frames.is_empty() {
return None;
}
Some(self.flush_fragment())
}
fn nal_units_to_avcc(&self, nal_units: &[NalUnit]) -> Vec<u8> {
let total_size: usize = nal_units
.iter()
.filter(|n| n.is_slice()) .map(|n| 4 + n.data.len())
.sum();
let mut buf = Vec::with_capacity(total_size);
for nal in nal_units.iter().filter(|n| n.is_slice()) {
let len = nal.data.len() as u32;
buf.extend_from_slice(&len.to_be_bytes());
buf.extend_from_slice(&nal.data);
}
buf
}
fn flush_fragment(&mut self) -> Vec<u8> {
let mut buf = Vec::new();
self.write_styp(&mut buf);
self.write_moof(&mut buf);
self.write_mdat(&mut buf);
self.sequence_number += 1;
self.pending_frames.clear();
buf
}
fn write_ftyp(&self, buf: &mut Vec<u8>) {
let brands = [
b"isom", b"iso6", b"cmfc", b"cmfv", b"avc1", b"mp41", ];
let size = 8 + 4 + 4 + (brands.len() * 4);
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"ftyp");
buf.extend_from_slice(b"isom"); buf.extend_from_slice(&0u32.to_be_bytes()); for brand in &brands {
buf.extend_from_slice(*brand);
}
}
fn write_styp(&self, buf: &mut Vec<u8>) {
let brands = [
b"msdh", b"msix", b"cmfc", b"cmfv", ];
let size = 8 + 4 + 4 + (brands.len() * 4);
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"styp");
buf.extend_from_slice(b"cmfv"); buf.extend_from_slice(&0u32.to_be_bytes()); for brand in &brands {
buf.extend_from_slice(*brand);
}
}
fn write_moov(&self, buf: &mut Vec<u8>) {
let mut moov_content = Vec::new();
self.write_mvhd(&mut moov_content);
self.write_trak(&mut moov_content);
self.write_mvex(&mut moov_content);
let size = 8 + moov_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"moov");
buf.extend_from_slice(&moov_content);
}
fn write_mvhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]);
content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&self.config.timescale.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0x00010000u32.to_be_bytes()); content.extend_from_slice(&0x0100u16.to_be_bytes()); content.extend_from_slice(&[0; 2]); content.extend_from_slice(&[0; 8]);
let matrix: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
for m in &matrix {
content.extend_from_slice(&m.to_be_bytes());
}
content.extend_from_slice(&[0; 24]); content.extend_from_slice(&2u32.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mvhd");
buf.extend_from_slice(&content);
}
fn write_trak(&self, buf: &mut Vec<u8>) {
let mut trak_content = Vec::new();
self.write_tkhd(&mut trak_content);
self.write_mdia(&mut trak_content);
let size = 8 + trak_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"trak");
buf.extend_from_slice(&trak_content);
}
fn write_tkhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 3]);
content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&self.track_id.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&[0; 8]); content.extend_from_slice(&0i16.to_be_bytes()); content.extend_from_slice(&0i16.to_be_bytes()); content.extend_from_slice(&0i16.to_be_bytes()); content.extend_from_slice(&0u16.to_be_bytes());
let matrix: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
for m in &matrix {
content.extend_from_slice(&m.to_be_bytes());
}
content.extend_from_slice(&(self.width << 16).to_be_bytes());
content.extend_from_slice(&(self.height << 16).to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"tkhd");
buf.extend_from_slice(&content);
}
fn write_mdia(&self, buf: &mut Vec<u8>) {
let mut mdia_content = Vec::new();
self.write_mdhd(&mut mdia_content);
self.write_hdlr(&mut mdia_content);
self.write_minf(&mut mdia_content);
let size = 8 + mdia_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mdia");
buf.extend_from_slice(&mdia_content);
}
fn write_mdhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]);
content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&self.config.timescale.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0x55c4u16.to_be_bytes()); content.extend_from_slice(&0u16.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mdhd");
buf.extend_from_slice(&content);
}
fn write_hdlr(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(b"vide"); content.extend_from_slice(&[0; 12]); content.extend_from_slice(b"VideoHandler\0");
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"hdlr");
buf.extend_from_slice(&content);
}
fn write_minf(&self, buf: &mut Vec<u8>) {
let mut minf_content = Vec::new();
self.write_vmhd(&mut minf_content);
self.write_dinf(&mut minf_content);
self.write_stbl(&mut minf_content);
let size = 8 + minf_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"minf");
buf.extend_from_slice(&minf_content);
}
fn write_vmhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 1]); content.extend_from_slice(&0u16.to_be_bytes()); content.extend_from_slice(&[0; 6]);
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"vmhd");
buf.extend_from_slice(&content);
}
fn write_dinf(&self, buf: &mut Vec<u8>) {
let mut dinf_content = Vec::new();
let mut dref_content = Vec::new();
dref_content.push(0); dref_content.extend_from_slice(&[0, 0, 0]); dref_content.extend_from_slice(&1u32.to_be_bytes());
dref_content.extend_from_slice(&12u32.to_be_bytes()); dref_content.extend_from_slice(b"url ");
dref_content.push(0); dref_content.extend_from_slice(&[0, 0, 1]);
let dref_size = 8 + dref_content.len();
dinf_content.extend_from_slice(&(dref_size as u32).to_be_bytes());
dinf_content.extend_from_slice(b"dref");
dinf_content.extend_from_slice(&dref_content);
let size = 8 + dinf_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"dinf");
buf.extend_from_slice(&dinf_content);
}
fn write_stbl(&self, buf: &mut Vec<u8>) {
let mut stbl_content = Vec::new();
self.write_stsd(&mut stbl_content);
self.write_empty_stts(&mut stbl_content);
self.write_empty_stsc(&mut stbl_content);
self.write_empty_stsz(&mut stbl_content);
self.write_empty_stco(&mut stbl_content);
let size = 8 + stbl_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stbl");
buf.extend_from_slice(&stbl_content);
}
fn write_stsd(&self, buf: &mut Vec<u8>) {
let mut stsd_content = Vec::new();
stsd_content.push(0); stsd_content.extend_from_slice(&[0, 0, 0]); stsd_content.extend_from_slice(&1u32.to_be_bytes());
self.write_avc1(&mut stsd_content);
let size = 8 + stsd_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stsd");
buf.extend_from_slice(&stsd_content);
}
fn write_avc1(&self, buf: &mut Vec<u8>) {
let mut avc1_content = Vec::new();
avc1_content.extend_from_slice(&[0; 6]); avc1_content.extend_from_slice(&1u16.to_be_bytes());
avc1_content.extend_from_slice(&0u16.to_be_bytes()); avc1_content.extend_from_slice(&0u16.to_be_bytes()); avc1_content.extend_from_slice(&[0; 12]);
avc1_content.extend_from_slice(&(self.width as u16).to_be_bytes());
avc1_content.extend_from_slice(&(self.height as u16).to_be_bytes());
avc1_content.extend_from_slice(&0x00480000u32.to_be_bytes()); avc1_content.extend_from_slice(&0x00480000u32.to_be_bytes()); avc1_content.extend_from_slice(&0u32.to_be_bytes()); avc1_content.extend_from_slice(&1u16.to_be_bytes());
let mut compressor = [0u8; 32];
let name = b"xoq-cmaf";
compressor[0] = name.len() as u8;
compressor[1..1 + name.len()].copy_from_slice(name);
avc1_content.extend_from_slice(&compressor);
avc1_content.extend_from_slice(&0x0018u16.to_be_bytes()); avc1_content.extend_from_slice(&(-1i16).to_be_bytes());
self.write_avcc(&mut avc1_content);
let size = 8 + avc1_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"avc1");
buf.extend_from_slice(&avc1_content);
}
fn write_avcc(&self, buf: &mut Vec<u8>) {
let mut avcc_content = Vec::new();
avcc_content.push(1);
if self.sps.len() >= 4 {
avcc_content.push(self.sps[1]); avcc_content.push(self.sps[2]); avcc_content.push(self.sps[3]); } else {
avcc_content.extend_from_slice(&[0x64, 0x00, 0x1f]); }
avcc_content.push(0xFF);
avcc_content.push(0xE1); avcc_content.extend_from_slice(&(self.sps.len() as u16).to_be_bytes());
avcc_content.extend_from_slice(&self.sps);
avcc_content.push(1); avcc_content.extend_from_slice(&(self.pps.len() as u16).to_be_bytes());
avcc_content.extend_from_slice(&self.pps);
let size = 8 + avcc_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"avcC");
buf.extend_from_slice(&avcc_content);
}
fn write_empty_stts(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&0u32.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stts");
buf.extend_from_slice(&content);
}
fn write_empty_stsc(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&0u32.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stsc");
buf.extend_from_slice(&content);
}
fn write_empty_stsz(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stsz");
buf.extend_from_slice(&content);
}
fn write_empty_stco(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&0u32.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stco");
buf.extend_from_slice(&content);
}
fn write_mvex(&self, buf: &mut Vec<u8>) {
let mut mvex_content = Vec::new();
self.write_trex(&mut mvex_content);
let size = 8 + mvex_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mvex");
buf.extend_from_slice(&mvex_content);
}
fn write_trex(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&self.track_id.to_be_bytes()); content.extend_from_slice(&1u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes()); content.extend_from_slice(&0u32.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"trex");
buf.extend_from_slice(&content);
}
fn write_moof(&self, buf: &mut Vec<u8>) {
let mut moof_content = Vec::new();
self.write_mfhd(&mut moof_content);
self.write_traf(&mut moof_content);
let size = 8 + moof_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"moof");
buf.extend_from_slice(&moof_content);
}
fn write_mfhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&self.sequence_number.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mfhd");
buf.extend_from_slice(&content);
}
fn write_traf(&self, buf: &mut Vec<u8>) {
let mut traf_content = Vec::new();
self.write_tfhd(&mut traf_content);
self.write_tfdt(&mut traf_content);
self.write_trun(&mut traf_content, buf.len());
let size = 8 + traf_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"traf");
buf.extend_from_slice(&traf_content);
}
fn write_tfhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0x02, 0x00, 0x00]);
content.extend_from_slice(&self.track_id.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"tfhd");
buf.extend_from_slice(&content);
}
fn write_tfdt(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(1); content.extend_from_slice(&[0, 0, 0]); content.extend_from_slice(&(self.fragment_base_dts as u64).to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"tfdt");
buf.extend_from_slice(&content);
}
fn write_trun(&self, buf: &mut Vec<u8>, _moof_offset: usize) {
let sample_count = self.pending_frames.len() as u32;
let trun_content_size = 4 + 4 + 4 + (sample_count as usize * 16);
let trun_size = 8 + trun_content_size;
let tfhd_size = 8 + 8; let tfdt_size = 8 + 12; let traf_size = 8 + tfhd_size + tfdt_size + trun_size;
let mfhd_size = 8 + 8;
let moof_size = 8 + mfhd_size + traf_size;
let data_offset = moof_size + 8;
let mut content = Vec::new();
content.push(0); content.extend_from_slice(&[0x00, 0x0F, 0x01]); content.extend_from_slice(&sample_count.to_be_bytes());
content.extend_from_slice(&(data_offset as u32).to_be_bytes());
for frame in &self.pending_frames {
content.extend_from_slice(&frame.duration.to_be_bytes());
content.extend_from_slice(&(frame.data.len() as u32).to_be_bytes());
let flags = if frame.is_sync {
0x02000000u32 } else {
0x01010000u32 };
content.extend_from_slice(&flags.to_be_bytes());
content.extend_from_slice(&frame.composition_offset.to_be_bytes());
}
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"trun");
buf.extend_from_slice(&content);
}
fn write_mdat(&self, buf: &mut Vec<u8>) {
let total_data_size: usize = self.pending_frames.iter().map(|f| f.data.len()).sum();
let size = 8 + total_data_size;
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mdat");
for frame in &self.pending_frames {
buf.extend_from_slice(&frame.data);
}
}
pub fn sequence_number(&self) -> u32 {
self.sequence_number
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn pending_frame_count(&self) -> usize {
self.pending_frames.len()
}
}
pub struct Av1CmafMuxer {
config: CmafConfig,
initialized: bool,
width: u32,
height: u32,
sequence_header_obu: Vec<u8>,
high_bitdepth: bool,
pending_frames: Vec<PendingFrame>,
sequence_number: u32,
fragment_base_dts: i64,
last_dts: i64,
track_id: u32,
}
impl Av1CmafMuxer {
pub fn new(config: CmafConfig) -> Self {
Self {
config,
initialized: false,
width: 0,
height: 0,
sequence_header_obu: Vec::new(),
high_bitdepth: false,
pending_frames: Vec::new(),
sequence_number: 1,
fragment_base_dts: 0,
last_dts: 0,
track_id: 1,
}
}
pub fn set_high_bitdepth(&mut self, hbd: bool) {
self.high_bitdepth = hbd;
}
pub fn create_init_segment(
&mut self,
sequence_header_obu: &[u8],
width: u32,
height: u32,
) -> Vec<u8> {
self.sequence_header_obu = sequence_header_obu.to_vec();
self.width = width;
self.height = height;
self.initialized = true;
let mut buf = Vec::new();
self.write_ftyp(&mut buf);
self.write_moov(&mut buf);
buf
}
pub fn add_frame(
&mut self,
data: &[u8],
pts: i64,
dts: i64,
duration: u32,
is_keyframe: bool,
) -> Option<Vec<u8>> {
if !self.initialized {
return None;
}
let should_flush = if self.pending_frames.is_empty() {
false
} else {
let fragment_duration =
(dts - self.fragment_base_dts) * 1000 / self.config.timescale as i64;
is_keyframe && fragment_duration >= self.config.fragment_duration_ms as i64
};
let segment = if should_flush {
Some(self.flush_fragment())
} else {
None
};
if self.pending_frames.is_empty() {
self.fragment_base_dts = dts;
}
let composition_offset = (pts - dts) as i32;
self.pending_frames.push(PendingFrame {
data: data.to_vec(),
duration,
is_sync: is_keyframe,
composition_offset,
});
self.last_dts = dts;
segment
}
pub fn flush(&mut self) -> Option<Vec<u8>> {
if self.pending_frames.is_empty() {
return None;
}
Some(self.flush_fragment())
}
fn flush_fragment(&mut self) -> Vec<u8> {
let mut buf = Vec::new();
self.write_styp(&mut buf);
self.write_moof(&mut buf);
self.write_mdat(&mut buf);
self.sequence_number += 1;
self.pending_frames.clear();
buf
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn pending_frame_count(&self) -> usize {
self.pending_frames.len()
}
fn write_ftyp(&self, buf: &mut Vec<u8>) {
let brands = [b"isom", b"iso6", b"cmfc", b"cmfv", b"av01", b"mp41"];
let size = 8 + 4 + 4 + (brands.len() * 4);
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"ftyp");
buf.extend_from_slice(b"isom");
buf.extend_from_slice(&0u32.to_be_bytes());
for brand in &brands {
buf.extend_from_slice(*brand);
}
}
fn write_styp(&self, buf: &mut Vec<u8>) {
let brands = [b"msdh", b"msix", b"cmfc", b"cmfv"];
let size = 8 + 4 + 4 + (brands.len() * 4);
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"styp");
buf.extend_from_slice(b"cmfv");
buf.extend_from_slice(&0u32.to_be_bytes());
for brand in &brands {
buf.extend_from_slice(*brand);
}
}
fn write_moov(&self, buf: &mut Vec<u8>) {
let mut moov_content = Vec::new();
self.write_mvhd(&mut moov_content);
self.write_trak(&mut moov_content);
self.write_mvex(&mut moov_content);
let size = 8 + moov_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"moov");
buf.extend_from_slice(&moov_content);
}
fn write_mvhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0);
content.extend_from_slice(&[0, 0, 0]);
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&self.config.timescale.to_be_bytes());
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0x00010000u32.to_be_bytes());
content.extend_from_slice(&0x0100u16.to_be_bytes());
content.extend_from_slice(&[0; 2]);
content.extend_from_slice(&[0; 8]);
let matrix: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
for m in &matrix {
content.extend_from_slice(&m.to_be_bytes());
}
content.extend_from_slice(&[0; 24]);
content.extend_from_slice(&2u32.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mvhd");
buf.extend_from_slice(&content);
}
fn write_trak(&self, buf: &mut Vec<u8>) {
let mut trak_content = Vec::new();
self.write_tkhd(&mut trak_content);
self.write_mdia(&mut trak_content);
let size = 8 + trak_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"trak");
buf.extend_from_slice(&trak_content);
}
fn write_tkhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0);
content.extend_from_slice(&[0, 0, 3]);
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&self.track_id.to_be_bytes());
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&[0; 8]);
content.extend_from_slice(&0i16.to_be_bytes());
content.extend_from_slice(&0i16.to_be_bytes());
content.extend_from_slice(&0i16.to_be_bytes());
content.extend_from_slice(&0u16.to_be_bytes());
let matrix: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
for m in &matrix {
content.extend_from_slice(&m.to_be_bytes());
}
content.extend_from_slice(&(self.width << 16).to_be_bytes());
content.extend_from_slice(&(self.height << 16).to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"tkhd");
buf.extend_from_slice(&content);
}
fn write_mdia(&self, buf: &mut Vec<u8>) {
let mut mdia_content = Vec::new();
self.write_mdhd(&mut mdia_content);
self.write_hdlr(&mut mdia_content);
self.write_minf(&mut mdia_content);
let size = 8 + mdia_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mdia");
buf.extend_from_slice(&mdia_content);
}
fn write_mdhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0);
content.extend_from_slice(&[0, 0, 0]);
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&self.config.timescale.to_be_bytes());
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(&0x55c4u16.to_be_bytes());
content.extend_from_slice(&0u16.to_be_bytes());
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mdhd");
buf.extend_from_slice(&content);
}
fn write_hdlr(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0);
content.extend_from_slice(&[0, 0, 0]);
content.extend_from_slice(&0u32.to_be_bytes());
content.extend_from_slice(b"vide");
content.extend_from_slice(&[0; 12]);
content.extend_from_slice(b"VideoHandler\0");
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"hdlr");
buf.extend_from_slice(&content);
}
fn write_minf(&self, buf: &mut Vec<u8>) {
let mut minf_content = Vec::new();
self.write_vmhd(&mut minf_content);
self.write_dinf(&mut minf_content);
self.write_stbl(&mut minf_content);
let size = 8 + minf_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"minf");
buf.extend_from_slice(&minf_content);
}
fn write_vmhd(&self, buf: &mut Vec<u8>) {
let mut content = Vec::new();
content.push(0);
content.extend_from_slice(&[0, 0, 1]);
content.extend_from_slice(&0u16.to_be_bytes());
content.extend_from_slice(&[0; 6]);
let size = 8 + content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"vmhd");
buf.extend_from_slice(&content);
}
fn write_dinf(&self, buf: &mut Vec<u8>) {
let mut dinf_content = Vec::new();
let mut dref_content = Vec::new();
dref_content.push(0);
dref_content.extend_from_slice(&[0, 0, 0]);
dref_content.extend_from_slice(&1u32.to_be_bytes());
dref_content.extend_from_slice(&12u32.to_be_bytes());
dref_content.extend_from_slice(b"url ");
dref_content.push(0);
dref_content.extend_from_slice(&[0, 0, 1]);
let dref_size = 8 + dref_content.len();
dinf_content.extend_from_slice(&(dref_size as u32).to_be_bytes());
dinf_content.extend_from_slice(b"dref");
dinf_content.extend_from_slice(&dref_content);
let size = 8 + dinf_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"dinf");
buf.extend_from_slice(&dinf_content);
}
fn write_stbl(&self, buf: &mut Vec<u8>) {
let mut stbl_content = Vec::new();
self.write_stsd(&mut stbl_content);
for box_type in [b"stts", b"stsc", b"stsz", b"stco"] {
let mut c = Vec::new();
c.push(0);
c.extend_from_slice(&[0, 0, 0]);
c.extend_from_slice(&0u32.to_be_bytes());
if *box_type == *b"stsz" {
c.extend_from_slice(&0u32.to_be_bytes()); }
let s = 8 + c.len();
stbl_content.extend_from_slice(&(s as u32).to_be_bytes());
stbl_content.extend_from_slice(box_type);
stbl_content.extend_from_slice(&c);
}
let size = 8 + stbl_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stbl");
buf.extend_from_slice(&stbl_content);
}
fn write_stsd(&self, buf: &mut Vec<u8>) {
let mut stsd_content = Vec::new();
stsd_content.push(0);
stsd_content.extend_from_slice(&[0, 0, 0]);
stsd_content.extend_from_slice(&1u32.to_be_bytes());
self.write_av01(&mut stsd_content);
let size = 8 + stsd_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stsd");
buf.extend_from_slice(&stsd_content);
}
fn write_av01(&self, buf: &mut Vec<u8>) {
let mut av01_content = Vec::new();
av01_content.extend_from_slice(&[0; 6]); av01_content.extend_from_slice(&1u16.to_be_bytes()); av01_content.extend_from_slice(&0u16.to_be_bytes()); av01_content.extend_from_slice(&0u16.to_be_bytes()); av01_content.extend_from_slice(&[0; 12]); av01_content.extend_from_slice(&(self.width as u16).to_be_bytes());
av01_content.extend_from_slice(&(self.height as u16).to_be_bytes());
av01_content.extend_from_slice(&0x00480000u32.to_be_bytes()); av01_content.extend_from_slice(&0x00480000u32.to_be_bytes()); av01_content.extend_from_slice(&0u32.to_be_bytes()); av01_content.extend_from_slice(&1u16.to_be_bytes());
let mut compressor = [0u8; 32];
let name = b"xoq-av1";
compressor[0] = name.len() as u8;
compressor[1..1 + name.len()].copy_from_slice(name);
av01_content.extend_from_slice(&compressor);
av01_content.extend_from_slice(&0x0018u16.to_be_bytes()); av01_content.extend_from_slice(&(-1i16).to_be_bytes());
self.write_av1c(&mut av01_content);
let size = 8 + av01_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"av01");
buf.extend_from_slice(&av01_content);
}
fn write_av1c(&self, buf: &mut Vec<u8>) {
let mut av1c_content = Vec::new();
av1c_content.push(0x81);
let (seq_profile, seq_level_idx, high_bitdepth, twelve_bit, monochrome, chroma_x, chroma_y) =
self.parse_sequence_header_fields();
av1c_content.push((seq_profile << 5) | (seq_level_idx & 0x1F));
#[allow(clippy::identity_op)]
let byte2 = (0u8 << 7) | ((high_bitdepth & 1) << 6)
| ((twelve_bit & 1) << 5)
| ((monochrome & 1) << 4)
| ((chroma_x & 1) << 3)
| ((chroma_y & 1) << 2)
| 0; av1c_content.push(byte2);
av1c_content.push(0x00);
av1c_content.extend_from_slice(&self.sequence_header_obu);
let size = 8 + av1c_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"av1C");
buf.extend_from_slice(&av1c_content);
}
fn parse_sequence_header_fields(&self) -> (u8, u8, u8, u8, u8, u8, u8) {
let mut seq_profile = 0u8;
let mut seq_level_idx = 8u8; let high_bitdepth = if self.high_bitdepth { 1u8 } else { 0u8 };
let twelve_bit = 0u8;
let monochrome = 0u8;
let mut chroma_x = 1u8;
let mut chroma_y = 1u8;
if self.sequence_header_obu.is_empty() {
return (
seq_profile,
seq_level_idx,
high_bitdepth,
twelve_bit,
monochrome,
chroma_x,
chroma_y,
);
}
let header_byte = self.sequence_header_obu[0];
let has_extension = (header_byte >> 2) & 1 == 1;
let has_size = (header_byte >> 1) & 1 == 1;
let mut payload_offset = 1;
if has_extension {
payload_offset += 1;
}
if has_size {
let (_size, leb_bytes) = read_leb128(&self.sequence_header_obu, payload_offset);
payload_offset += leb_bytes;
}
if payload_offset >= self.sequence_header_obu.len() {
return (
seq_profile,
seq_level_idx,
high_bitdepth,
twelve_bit,
monochrome,
chroma_x,
chroma_y,
);
}
let payload = &self.sequence_header_obu[payload_offset..];
if payload.is_empty() {
return (
seq_profile,
seq_level_idx,
high_bitdepth,
twelve_bit,
monochrome,
chroma_x,
chroma_y,
);
}
seq_profile = (payload[0] >> 5) & 0x07;
let _still_picture = (payload[0] >> 4) & 1;
let reduced_still_picture_header = (payload[0] >> 3) & 1;
if reduced_still_picture_header == 1 && payload.len() > 1 {
seq_level_idx = ((payload[0] & 0x07) << 2) | (payload[1] >> 6);
} else if payload.len() > 2 {
let timing_info_present = (payload[0] >> 2) & 1;
if timing_info_present == 0 {
if payload.len() > 3 {
seq_level_idx = (payload[3] >> 3) & 0x1F;
}
}
}
if seq_profile == 0 {
chroma_x = 1;
chroma_y = 1;
}
(
seq_profile,
seq_level_idx,
high_bitdepth,
twelve_bit,
monochrome,
chroma_x,
chroma_y,
)
}
fn write_mvex(&self, buf: &mut Vec<u8>) {
let mut mvex_content = Vec::new();
let mut trex_content = Vec::new();
trex_content.push(0);
trex_content.extend_from_slice(&[0, 0, 0]);
trex_content.extend_from_slice(&self.track_id.to_be_bytes());
trex_content.extend_from_slice(&1u32.to_be_bytes());
trex_content.extend_from_slice(&0u32.to_be_bytes());
trex_content.extend_from_slice(&0u32.to_be_bytes());
trex_content.extend_from_slice(&0u32.to_be_bytes());
let trex_size = 8 + trex_content.len();
mvex_content.extend_from_slice(&(trex_size as u32).to_be_bytes());
mvex_content.extend_from_slice(b"trex");
mvex_content.extend_from_slice(&trex_content);
let size = 8 + mvex_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mvex");
buf.extend_from_slice(&mvex_content);
}
fn write_moof(&self, buf: &mut Vec<u8>) {
let mut moof_content = Vec::new();
let mut mfhd_content = Vec::new();
mfhd_content.push(0);
mfhd_content.extend_from_slice(&[0, 0, 0]);
mfhd_content.extend_from_slice(&self.sequence_number.to_be_bytes());
let mfhd_size = 8 + mfhd_content.len();
moof_content.extend_from_slice(&(mfhd_size as u32).to_be_bytes());
moof_content.extend_from_slice(b"mfhd");
moof_content.extend_from_slice(&mfhd_content);
self.write_traf(&mut moof_content, buf.len());
let size = 8 + moof_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"moof");
buf.extend_from_slice(&moof_content);
}
fn write_traf(&self, buf: &mut Vec<u8>, _outer_offset: usize) {
let mut traf_content = Vec::new();
let mut tfhd_content = Vec::new();
tfhd_content.push(0);
tfhd_content.extend_from_slice(&[0x02, 0x00, 0x00]); tfhd_content.extend_from_slice(&self.track_id.to_be_bytes());
let tfhd_size = 8 + tfhd_content.len();
traf_content.extend_from_slice(&(tfhd_size as u32).to_be_bytes());
traf_content.extend_from_slice(b"tfhd");
traf_content.extend_from_slice(&tfhd_content);
let mut tfdt_content = Vec::new();
tfdt_content.push(1); tfdt_content.extend_from_slice(&[0, 0, 0]);
tfdt_content.extend_from_slice(&(self.fragment_base_dts as u64).to_be_bytes());
let tfdt_size = 8 + tfdt_content.len();
traf_content.extend_from_slice(&(tfdt_size as u32).to_be_bytes());
traf_content.extend_from_slice(b"tfdt");
traf_content.extend_from_slice(&tfdt_content);
let sample_count = self.pending_frames.len() as u32;
let trun_content_size = 4 + 4 + 4 + (sample_count as usize * 16);
let trun_size = 8 + trun_content_size;
let traf_size = 8 + tfhd_size + tfdt_size + trun_size;
let mfhd_size = 8 + 8; let moof_size = 8 + mfhd_size + traf_size;
let data_offset = moof_size + 8;
let mut trun_content = Vec::new();
trun_content.push(0);
trun_content.extend_from_slice(&[0x00, 0x0F, 0x01]); trun_content.extend_from_slice(&sample_count.to_be_bytes());
trun_content.extend_from_slice(&(data_offset as u32).to_be_bytes());
for frame in &self.pending_frames {
trun_content.extend_from_slice(&frame.duration.to_be_bytes());
trun_content.extend_from_slice(&(frame.data.len() as u32).to_be_bytes());
let flags = if frame.is_sync {
0x02000000u32
} else {
0x01010000u32
};
trun_content.extend_from_slice(&flags.to_be_bytes());
trun_content.extend_from_slice(&frame.composition_offset.to_be_bytes());
}
traf_content.extend_from_slice(&(trun_size as u32).to_be_bytes());
traf_content.extend_from_slice(b"trun");
traf_content.extend_from_slice(&trun_content);
let size = 8 + traf_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"traf");
buf.extend_from_slice(&traf_content);
}
fn write_mdat(&self, buf: &mut Vec<u8>) {
let total_data_size: usize = self.pending_frames.iter().map(|f| f.data.len()).sum();
let size = 8 + total_data_size;
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mdat");
for frame in &self.pending_frames {
buf.extend_from_slice(&frame.data);
}
}
}
#[derive(Debug, Clone)]
pub struct BoxHeader {
pub box_type: [u8; 4],
pub offset: usize,
pub size: usize,
pub header_size: usize,
}
impl BoxHeader {
pub fn content<'a>(&self, data: &'a [u8]) -> &'a [u8] {
let start = self.offset + self.header_size;
let end = (self.offset + self.size).min(data.len());
if start >= end {
&[]
} else {
&data[start..end]
}
}
}
pub fn iter_boxes(data: &[u8]) -> Vec<BoxHeader> {
let mut boxes = Vec::new();
let mut offset = 0;
while offset + 8 <= data.len() {
let size = u32::from_be_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as usize;
let box_type: [u8; 4] = [
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
];
if size < 8 {
break;
}
boxes.push(BoxHeader {
box_type,
offset,
size: size.min(data.len() - offset),
header_size: 8,
});
offset += size;
}
boxes
}
pub fn find_box(data: &[u8], box_type: &[u8; 4]) -> Option<BoxHeader> {
iter_boxes(data)
.into_iter()
.find(|b| &b.box_type == box_type)
}
pub fn find_box_path<'a>(data: &'a [u8], path: &[&[u8; 4]]) -> Option<&'a [u8]> {
if path.is_empty() {
return Some(data);
}
let header = find_box(data, path[0])?;
let content = header.content(data);
if path.len() == 1 {
Some(content)
} else {
find_box_path(content, &path[1..])
}
}
#[derive(Debug, Clone)]
pub struct ParsedInitSegment {
pub av1c_config: Vec<u8>,
pub width: u32,
pub height: u32,
pub timescale: u32,
}
pub fn parse_cmaf_init(data: &[u8]) -> anyhow::Result<ParsedInitSegment> {
let stsd_content = find_box_path(
data,
&[b"moov", b"trak", b"mdia", b"minf", b"stbl", b"stsd"],
)
.ok_or_else(|| anyhow::anyhow!("Could not find stsd box in init segment"))?;
if stsd_content.len() < 8 {
anyhow::bail!("stsd content too short");
}
let sample_entry_data = &stsd_content[8..];
let av01_header = find_box(sample_entry_data, b"av01")
.ok_or_else(|| anyhow::anyhow!("Could not find av01 sample entry"))?;
let av01_content = av01_header.content(sample_entry_data);
if av01_content.len() < 78 {
anyhow::bail!("av01 content too short: {} bytes", av01_content.len());
}
let width = u16::from_be_bytes([av01_content[24], av01_content[25]]) as u32;
let height = u16::from_be_bytes([av01_content[26], av01_content[27]]) as u32;
let av1c_area = &av01_content[78..];
let av1c_header =
find_box(av1c_area, b"av1C").ok_or_else(|| anyhow::anyhow!("Could not find av1C box"))?;
let av1c_config = av1c_header.content(av1c_area).to_vec();
let mdhd_content = find_box_path(data, &[b"moov", b"trak", b"mdia", b"mdhd"])
.ok_or_else(|| anyhow::anyhow!("Could not find mdhd box"))?;
let timescale = if mdhd_content.len() >= 16 {
u32::from_be_bytes([
mdhd_content[12],
mdhd_content[13],
mdhd_content[14],
mdhd_content[15],
])
} else {
90000
};
Ok(ParsedInitSegment {
av1c_config,
width,
height,
timescale,
})
}
#[derive(Debug, Clone)]
pub struct SampleEntry {
pub duration: u32,
pub size: u32,
pub flags: u32,
pub composition_offset: i32,
}
#[derive(Debug, Clone)]
pub struct ParsedMediaSegment {
pub sequence_number: u32,
pub base_decode_time: u64,
pub samples: Vec<SampleEntry>,
pub mdat_payload: Vec<u8>,
}
pub fn parse_cmaf_media_segment(data: &[u8]) -> anyhow::Result<ParsedMediaSegment> {
let moof_header =
find_box(data, b"moof").ok_or_else(|| anyhow::anyhow!("Could not find moof box"))?;
let moof_content = moof_header.content(data);
let mfhd_content = find_box(moof_content, b"mfhd")
.ok_or_else(|| anyhow::anyhow!("Could not find mfhd box"))?;
let mfhd = mfhd_content.content(moof_content);
let sequence_number = if mfhd.len() >= 8 {
u32::from_be_bytes([mfhd[4], mfhd[5], mfhd[6], mfhd[7]])
} else {
0
};
let traf_header = find_box(moof_content, b"traf")
.ok_or_else(|| anyhow::anyhow!("Could not find traf box"))?;
let traf_content = traf_header.content(moof_content);
let tfdt_header = find_box(traf_content, b"tfdt")
.ok_or_else(|| anyhow::anyhow!("Could not find tfdt box"))?;
let tfdt = tfdt_header.content(traf_content);
let base_decode_time = if !tfdt.is_empty() && tfdt[0] == 1 {
if tfdt.len() >= 12 {
u64::from_be_bytes([
tfdt[4], tfdt[5], tfdt[6], tfdt[7], tfdt[8], tfdt[9], tfdt[10], tfdt[11],
])
} else {
0
}
} else {
if tfdt.len() >= 8 {
u32::from_be_bytes([tfdt[4], tfdt[5], tfdt[6], tfdt[7]]) as u64
} else {
0
}
};
let trun_header = find_box(traf_content, b"trun")
.ok_or_else(|| anyhow::anyhow!("Could not find trun box"))?;
let trun = trun_header.content(traf_content);
if trun.len() < 8 {
anyhow::bail!("trun content too short");
}
let trun_flags = u32::from_be_bytes([0, trun[1], trun[2], trun[3]]);
let sample_count = u32::from_be_bytes([trun[4], trun[5], trun[6], trun[7]]);
let mut offset = 8;
if trun_flags & 0x01 != 0 {
offset += 4; }
if trun_flags & 0x04 != 0 {
offset += 4; }
let has_duration = trun_flags & 0x100 != 0;
let has_size = trun_flags & 0x200 != 0;
let has_flags = trun_flags & 0x400 != 0;
let has_cts_offset = trun_flags & 0x800 != 0;
let mut samples = Vec::with_capacity(sample_count as usize);
for _ in 0..sample_count {
let duration = if has_duration && offset + 4 <= trun.len() {
let v = u32::from_be_bytes([
trun[offset],
trun[offset + 1],
trun[offset + 2],
trun[offset + 3],
]);
offset += 4;
v
} else {
0
};
let size = if has_size && offset + 4 <= trun.len() {
let v = u32::from_be_bytes([
trun[offset],
trun[offset + 1],
trun[offset + 2],
trun[offset + 3],
]);
offset += 4;
v
} else {
0
};
let flags = if has_flags && offset + 4 <= trun.len() {
let v = u32::from_be_bytes([
trun[offset],
trun[offset + 1],
trun[offset + 2],
trun[offset + 3],
]);
offset += 4;
v
} else {
0
};
let composition_offset = if has_cts_offset && offset + 4 <= trun.len() {
let v = i32::from_be_bytes([
trun[offset],
trun[offset + 1],
trun[offset + 2],
trun[offset + 3],
]);
offset += 4;
v
} else {
0
};
samples.push(SampleEntry {
duration,
size,
flags,
composition_offset,
});
}
let mdat_header =
find_box(data, b"mdat").ok_or_else(|| anyhow::anyhow!("Could not find mdat box"))?;
let mdat_payload = mdat_header.content(data).to_vec();
Ok(ParsedMediaSegment {
sequence_number,
base_decode_time,
samples,
mdat_payload,
})
}
#[derive(Debug, Clone)]
pub struct TrackConfig {
pub track_id: u32,
pub timescale: u32,
pub handler: [u8; 4],
pub codec_config: Vec<u8>,
pub width: u32,
pub height: u32,
pub high_bitdepth: bool,
}
#[derive(Debug, Clone)]
pub struct TrackFragment {
pub track_id: u32,
pub base_decode_time: u64,
pub samples: Vec<SampleEntry>,
pub data: Vec<u8>,
}
pub struct MultiTrackRecorder {
sequence_number: u32,
tracks: Vec<TrackConfig>,
}
impl MultiTrackRecorder {
pub fn new(tracks: Vec<TrackConfig>) -> Self {
Self {
sequence_number: 1,
tracks,
}
}
pub fn write_init_segment(&self) -> Vec<u8> {
let mut buf = Vec::new();
self.write_ftyp(&mut buf);
self.write_moov(&mut buf);
buf
}
pub fn write_fragment(&mut self, fragments: &[TrackFragment]) -> Vec<u8> {
if fragments.is_empty() {
return Vec::new();
}
let mut buf = Vec::new();
self.write_multi_moof_mdat(&mut buf, fragments);
self.sequence_number += 1;
buf
}
fn write_ftyp(&self, buf: &mut Vec<u8>) {
let brands: &[&[u8; 4]] = &[b"isom", b"iso6", b"cmfc", b"av01", b"mp41"];
let size = 8 + 4 + 4 + (brands.len() * 4);
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"ftyp");
buf.extend_from_slice(b"isom");
buf.extend_from_slice(&0u32.to_be_bytes());
for brand in brands {
buf.extend_from_slice(*brand);
}
}
fn write_moov(&self, buf: &mut Vec<u8>) {
let mut moov_content = Vec::new();
self.write_mvhd(&mut moov_content);
for track in &self.tracks {
self.write_trak(&mut moov_content, track);
}
self.write_mvex(&mut moov_content);
let size = 8 + moov_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"moov");
buf.extend_from_slice(&moov_content);
}
fn write_mvhd(&self, buf: &mut Vec<u8>) {
let mut c = Vec::new();
c.push(0); c.extend_from_slice(&[0, 0, 0]); c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&0u32.to_be_bytes()); let ts = self.tracks.first().map(|t| t.timescale).unwrap_or(90000);
c.extend_from_slice(&ts.to_be_bytes());
c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&0x00010000u32.to_be_bytes()); c.extend_from_slice(&0x0100u16.to_be_bytes()); c.extend_from_slice(&[0; 2]); c.extend_from_slice(&[0; 8]); let matrix: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
for m in &matrix {
c.extend_from_slice(&m.to_be_bytes());
}
c.extend_from_slice(&[0; 24]); let next_track_id = self.tracks.len() as u32 + 1;
c.extend_from_slice(&next_track_id.to_be_bytes());
let size = 8 + c.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mvhd");
buf.extend_from_slice(&c);
}
fn write_trak(&self, buf: &mut Vec<u8>, track: &TrackConfig) {
let mut trak_content = Vec::new();
self.write_tkhd(&mut trak_content, track);
self.write_mdia(&mut trak_content, track);
let size = 8 + trak_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"trak");
buf.extend_from_slice(&trak_content);
}
fn write_tkhd(&self, buf: &mut Vec<u8>, track: &TrackConfig) {
let mut c = Vec::new();
c.push(0); c.extend_from_slice(&[0, 0, 3]); c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&track.track_id.to_be_bytes());
c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&[0; 8]); c.extend_from_slice(&0i16.to_be_bytes()); c.extend_from_slice(&0i16.to_be_bytes()); c.extend_from_slice(&0i16.to_be_bytes());
c.extend_from_slice(&0u16.to_be_bytes()); let matrix: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
for m in &matrix {
c.extend_from_slice(&m.to_be_bytes());
}
c.extend_from_slice(&(track.width << 16).to_be_bytes());
c.extend_from_slice(&(track.height << 16).to_be_bytes());
let size = 8 + c.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"tkhd");
buf.extend_from_slice(&c);
}
fn write_mdia(&self, buf: &mut Vec<u8>, track: &TrackConfig) {
let mut mdia_content = Vec::new();
{
let mut c = Vec::new();
c.push(0); c.extend_from_slice(&[0, 0, 0]);
c.extend_from_slice(&0u32.to_be_bytes());
c.extend_from_slice(&0u32.to_be_bytes());
c.extend_from_slice(&track.timescale.to_be_bytes());
c.extend_from_slice(&0u32.to_be_bytes());
c.extend_from_slice(&0x55c4u16.to_be_bytes()); c.extend_from_slice(&0u16.to_be_bytes());
let s = 8 + c.len();
mdia_content.extend_from_slice(&(s as u32).to_be_bytes());
mdia_content.extend_from_slice(b"mdhd");
mdia_content.extend_from_slice(&c);
}
{
let mut c = Vec::new();
c.push(0);
c.extend_from_slice(&[0, 0, 0]);
c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&track.handler);
c.extend_from_slice(&[0; 12]); let name = if &track.handler == b"vide" {
b"VideoHandler\0"
} else {
b"MetaHandler\0\0"
};
c.extend_from_slice(name);
let s = 8 + c.len();
mdia_content.extend_from_slice(&(s as u32).to_be_bytes());
mdia_content.extend_from_slice(b"hdlr");
mdia_content.extend_from_slice(&c);
}
{
let mut minf_content = Vec::new();
if &track.handler == b"vide" {
let mut c = Vec::new();
c.push(0);
c.extend_from_slice(&[0, 0, 1]);
c.extend_from_slice(&0u16.to_be_bytes());
c.extend_from_slice(&[0; 6]);
let s = 8 + c.len();
minf_content.extend_from_slice(&(s as u32).to_be_bytes());
minf_content.extend_from_slice(b"vmhd");
minf_content.extend_from_slice(&c);
} else {
let mut c = Vec::new();
c.push(0);
c.extend_from_slice(&[0, 0, 0]);
let s = 8 + c.len();
minf_content.extend_from_slice(&(s as u32).to_be_bytes());
minf_content.extend_from_slice(b"nmhd");
minf_content.extend_from_slice(&c);
}
{
let mut dinf_content = Vec::new();
let mut dref_content = Vec::new();
dref_content.push(0);
dref_content.extend_from_slice(&[0, 0, 0]);
dref_content.extend_from_slice(&1u32.to_be_bytes());
dref_content.extend_from_slice(&12u32.to_be_bytes());
dref_content.extend_from_slice(b"url ");
dref_content.push(0);
dref_content.extend_from_slice(&[0, 0, 1]);
let dref_s = 8 + dref_content.len();
dinf_content.extend_from_slice(&(dref_s as u32).to_be_bytes());
dinf_content.extend_from_slice(b"dref");
dinf_content.extend_from_slice(&dref_content);
let s = 8 + dinf_content.len();
minf_content.extend_from_slice(&(s as u32).to_be_bytes());
minf_content.extend_from_slice(b"dinf");
minf_content.extend_from_slice(&dinf_content);
}
{
let mut stbl_content = Vec::new();
self.write_stsd(&mut stbl_content, track);
for box_type in [b"stts", b"stsc", b"stsz", b"stco"] {
let mut c = Vec::new();
c.push(0);
c.extend_from_slice(&[0, 0, 0]);
c.extend_from_slice(&0u32.to_be_bytes());
if *box_type == *b"stsz" {
c.extend_from_slice(&0u32.to_be_bytes());
}
let s = 8 + c.len();
stbl_content.extend_from_slice(&(s as u32).to_be_bytes());
stbl_content.extend_from_slice(box_type);
stbl_content.extend_from_slice(&c);
}
let s = 8 + stbl_content.len();
minf_content.extend_from_slice(&(s as u32).to_be_bytes());
minf_content.extend_from_slice(b"stbl");
minf_content.extend_from_slice(&stbl_content);
}
let s = 8 + minf_content.len();
mdia_content.extend_from_slice(&(s as u32).to_be_bytes());
mdia_content.extend_from_slice(b"minf");
mdia_content.extend_from_slice(&minf_content);
}
let size = 8 + mdia_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mdia");
buf.extend_from_slice(&mdia_content);
}
fn write_stsd(&self, buf: &mut Vec<u8>, track: &TrackConfig) {
let mut stsd_content = Vec::new();
stsd_content.push(0); stsd_content.extend_from_slice(&[0, 0, 0]); stsd_content.extend_from_slice(&1u32.to_be_bytes());
if &track.handler == b"vide" {
self.write_av01_entry(&mut stsd_content, track);
} else {
self.write_mett_entry(&mut stsd_content, track);
}
let size = 8 + stsd_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"stsd");
buf.extend_from_slice(&stsd_content);
}
fn write_av01_entry(&self, buf: &mut Vec<u8>, track: &TrackConfig) {
let mut av01_content = Vec::new();
av01_content.extend_from_slice(&[0; 6]); av01_content.extend_from_slice(&1u16.to_be_bytes()); av01_content.extend_from_slice(&0u16.to_be_bytes()); av01_content.extend_from_slice(&0u16.to_be_bytes()); av01_content.extend_from_slice(&[0; 12]); av01_content.extend_from_slice(&(track.width as u16).to_be_bytes());
av01_content.extend_from_slice(&(track.height as u16).to_be_bytes());
av01_content.extend_from_slice(&0x00480000u32.to_be_bytes()); av01_content.extend_from_slice(&0x00480000u32.to_be_bytes()); av01_content.extend_from_slice(&0u32.to_be_bytes()); av01_content.extend_from_slice(&1u16.to_be_bytes()); let mut compressor = [0u8; 32];
let name = b"xoq-rec";
compressor[0] = name.len() as u8;
compressor[1..1 + name.len()].copy_from_slice(name);
av01_content.extend_from_slice(&compressor);
av01_content.extend_from_slice(&0x0018u16.to_be_bytes()); av01_content.extend_from_slice(&(-1i16).to_be_bytes());
let mut av1c_box = Vec::new();
av1c_box.extend_from_slice(&track.codec_config);
let av1c_size = 8 + av1c_box.len();
av01_content.extend_from_slice(&(av1c_size as u32).to_be_bytes());
av01_content.extend_from_slice(b"av1C");
av01_content.extend_from_slice(&av1c_box);
let size = 8 + av01_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"av01");
buf.extend_from_slice(&av01_content);
}
fn write_mett_entry(&self, buf: &mut Vec<u8>, _track: &TrackConfig) {
let mut mett_content = Vec::new();
mett_content.extend_from_slice(&[0; 6]); mett_content.extend_from_slice(&1u16.to_be_bytes()); mett_content.extend_from_slice(b"application/octet-stream\0");
let size = 8 + mett_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mett");
buf.extend_from_slice(&mett_content);
}
fn write_mvex(&self, buf: &mut Vec<u8>) {
let mut mvex_content = Vec::new();
for track in &self.tracks {
let mut c = Vec::new();
c.push(0); c.extend_from_slice(&[0, 0, 0]); c.extend_from_slice(&track.track_id.to_be_bytes());
c.extend_from_slice(&1u32.to_be_bytes()); c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&0u32.to_be_bytes()); c.extend_from_slice(&0u32.to_be_bytes()); let s = 8 + c.len();
mvex_content.extend_from_slice(&(s as u32).to_be_bytes());
mvex_content.extend_from_slice(b"trex");
mvex_content.extend_from_slice(&c);
}
let size = 8 + mvex_content.len();
buf.extend_from_slice(&(size as u32).to_be_bytes());
buf.extend_from_slice(b"mvex");
buf.extend_from_slice(&mvex_content);
}
fn write_multi_moof_mdat(&self, buf: &mut Vec<u8>, fragments: &[TrackFragment]) {
let mut traf_bufs: Vec<Vec<u8>> = Vec::new();
let mfhd_size = 8 + 8;
let total_mdat_data: usize = fragments.iter().map(|f| f.data.len()).sum();
let mdat_header_size = 8;
let mut data_offsets: Vec<usize> = Vec::new();
let mut running_data_offset = 0usize;
for frag in fragments {
data_offsets.push(running_data_offset);
running_data_offset += frag.data.len();
}
for frag in fragments {
let mut traf_content = Vec::new();
{
let mut c = Vec::new();
c.push(0); c.extend_from_slice(&[0x02, 0x00, 0x00]); c.extend_from_slice(&frag.track_id.to_be_bytes());
let s = 8 + c.len();
traf_content.extend_from_slice(&(s as u32).to_be_bytes());
traf_content.extend_from_slice(b"tfhd");
traf_content.extend_from_slice(&c);
}
{
let mut c = Vec::new();
c.push(1); c.extend_from_slice(&[0, 0, 0]);
c.extend_from_slice(&frag.base_decode_time.to_be_bytes());
let s = 8 + c.len();
traf_content.extend_from_slice(&(s as u32).to_be_bytes());
traf_content.extend_from_slice(b"tfdt");
traf_content.extend_from_slice(&c);
}
{
let sample_count = frag.samples.len() as u32;
let mut c = Vec::new();
c.push(0); c.extend_from_slice(&[0x00, 0x0F, 0x01]); c.extend_from_slice(&sample_count.to_be_bytes());
c.extend_from_slice(&0u32.to_be_bytes());
for sample in &frag.samples {
c.extend_from_slice(&sample.duration.to_be_bytes());
c.extend_from_slice(&sample.size.to_be_bytes());
c.extend_from_slice(&sample.flags.to_be_bytes());
c.extend_from_slice(&sample.composition_offset.to_be_bytes());
}
let s = 8 + c.len();
traf_content.extend_from_slice(&(s as u32).to_be_bytes());
traf_content.extend_from_slice(b"trun");
traf_content.extend_from_slice(&c);
}
let mut traf_buf = Vec::new();
let s = 8 + traf_content.len();
traf_buf.extend_from_slice(&(s as u32).to_be_bytes());
traf_buf.extend_from_slice(b"traf");
traf_buf.extend_from_slice(&traf_content);
traf_bufs.push(traf_buf);
}
let total_traf_size: usize = traf_bufs.iter().map(|t| t.len()).sum();
let moof_size = 8 + mfhd_size + total_traf_size;
for (i, traf_buf) in traf_bufs.iter_mut().enumerate() {
let data_offset = (moof_size + mdat_header_size + data_offsets[i]) as u32;
let mut pos = 8; while pos + 8 <= traf_buf.len() {
let box_size = u32::from_be_bytes([
traf_buf[pos],
traf_buf[pos + 1],
traf_buf[pos + 2],
traf_buf[pos + 3],
]) as usize;
let box_type = &traf_buf[pos + 4..pos + 8];
if box_type == b"trun" {
let do_pos = pos + 16;
traf_buf[do_pos..do_pos + 4].copy_from_slice(&data_offset.to_be_bytes());
break;
}
pos += box_size;
}
}
buf.extend_from_slice(&(moof_size as u32).to_be_bytes());
buf.extend_from_slice(b"moof");
{
let mut c = Vec::new();
c.push(0);
c.extend_from_slice(&[0, 0, 0]);
c.extend_from_slice(&self.sequence_number.to_be_bytes());
let s = 8 + c.len();
buf.extend_from_slice(&(s as u32).to_be_bytes());
buf.extend_from_slice(b"mfhd");
buf.extend_from_slice(&c);
}
for traf_buf in &traf_bufs {
buf.extend_from_slice(traf_buf);
}
let mdat_size = mdat_header_size + total_mdat_data;
buf.extend_from_slice(&(mdat_size as u32).to_be_bytes());
buf.extend_from_slice(b"mdat");
for frag in fragments {
buf.extend_from_slice(&frag.data);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nal_unit_types() {
let idr = NalUnit {
data: vec![0x65, 0x00],
nal_type: nal_unit_type::IDR_SLICE,
};
assert!(idr.is_idr());
assert!(idr.is_slice());
assert!(!idr.is_sps());
assert!(!idr.is_pps());
let non_idr = NalUnit {
data: vec![0x41, 0x00],
nal_type: nal_unit_type::NON_IDR_SLICE,
};
assert!(!non_idr.is_idr());
assert!(non_idr.is_slice());
let sps = NalUnit {
data: vec![0x67, 0x64, 0x00, 0x1f],
nal_type: nal_unit_type::SPS,
};
assert!(sps.is_sps());
assert!(!sps.is_slice());
let pps = NalUnit {
data: vec![0x68, 0xee, 0x3c],
nal_type: nal_unit_type::PPS,
};
assert!(pps.is_pps());
assert!(!pps.is_slice());
}
#[test]
fn test_nal_unit_to_annex_b() {
let nal = NalUnit {
data: vec![0x65, 0xAA, 0xBB],
nal_type: nal_unit_type::IDR_SLICE,
};
let annex_b = nal.to_annex_b();
assert_eq!(&annex_b[..4], &[0x00, 0x00, 0x00, 0x01]);
assert_eq!(&annex_b[4..], &[0x65, 0xAA, 0xBB]);
}
#[test]
fn test_parse_annex_b_keyframe() {
let mut data = Vec::new();
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
data.extend_from_slice(&[0x67, 0x64, 0x00, 0x1f, 0xAC]);
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
data.extend_from_slice(&[0x68, 0xEE, 0x3C, 0x80]);
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
data.extend_from_slice(&[0x65, 0x88, 0x80, 0x40, 0x00]);
let parsed = parse_annex_b(&data);
assert!(parsed.is_keyframe);
assert!(parsed.sps.is_some());
assert!(parsed.pps.is_some());
assert_eq!(parsed.sps.unwrap(), vec![0x67, 0x64, 0x00, 0x1f, 0xAC]);
assert_eq!(parsed.pps.unwrap(), vec![0x68, 0xEE, 0x3C, 0x80]);
assert_eq!(parsed.nals.len(), 1);
assert!(parsed.nals[0].is_idr());
}
#[test]
fn test_parse_annex_b_non_keyframe() {
let mut data = Vec::new();
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]);
data.extend_from_slice(&[0x41, 0x9A, 0x00, 0x10]);
let parsed = parse_annex_b(&data);
assert!(!parsed.is_keyframe);
assert!(parsed.sps.is_none());
assert!(parsed.pps.is_none());
assert_eq!(parsed.nals.len(), 1);
assert!(!parsed.nals[0].is_idr());
}
#[test]
fn test_parse_annex_b_3byte_start_codes() {
let mut data = Vec::new();
data.extend_from_slice(&[0x00, 0x00, 0x01]);
data.extend_from_slice(&[0x41, 0x9A, 0x00]);
let parsed = parse_annex_b(&data);
assert_eq!(parsed.nals.len(), 1);
assert_eq!(parsed.nals[0].nal_type, nal_unit_type::NON_IDR_SLICE);
}
#[test]
fn test_parse_annex_b_empty() {
let parsed = parse_annex_b(&[]);
assert!(!parsed.is_keyframe);
assert!(parsed.sps.is_none());
assert!(parsed.pps.is_none());
assert!(parsed.nals.is_empty());
}
#[test]
fn test_default_config() {
let config = CmafConfig::default();
assert_eq!(config.fragment_duration_ms, 2000);
assert_eq!(config.timescale, 90000);
}
#[test]
fn test_muxer_initialization() {
let mut muxer = CmafMuxer::new(CmafConfig::default());
assert!(!muxer.is_initialized());
let sps = vec![0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50];
let pps = vec![0x68, 0xee, 0x3c, 0x80];
let init = muxer.create_init_segment(&sps, &pps, 1920, 1080);
assert!(muxer.is_initialized());
assert!(!init.is_empty());
assert_eq!(&init[4..8], b"ftyp");
assert!(init.windows(4).any(|w| w == b"moov"));
}
#[test]
fn test_ftyp_box() {
let muxer = CmafMuxer::new(CmafConfig::default());
let mut buf = Vec::new();
muxer.write_ftyp(&mut buf);
let size = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(&buf[4..8], b"ftyp");
assert_eq!(size as usize, buf.len());
}
#[test]
fn test_add_frame_before_init() {
let mut muxer = CmafMuxer::new(CmafConfig::default());
let nals = vec![NalUnit {
data: vec![0x65, 0x88],
nal_type: nal_unit_type::IDR_SLICE,
}];
let result = muxer.add_frame(&nals, 0, 0, 3000, true);
assert!(result.is_none());
}
#[test]
fn test_muxer_add_frame_and_flush() {
let mut muxer = CmafMuxer::new(CmafConfig {
fragment_duration_ms: 33,
timescale: 90000,
});
let sps = vec![0x67, 0x64, 0x00, 0x1f];
let pps = vec![0x68, 0xee, 0x3c, 0x80];
muxer.create_init_segment(&sps, &pps, 640, 480);
let nals = vec![NalUnit {
data: vec![0x65, 0x88, 0x80],
nal_type: nal_unit_type::IDR_SLICE,
}];
let seg = muxer.add_frame(&nals, 0, 0, 3000, true);
assert!(seg.is_none());
assert_eq!(muxer.pending_frame_count(), 1);
let nals = vec![NalUnit {
data: vec![0x41, 0x9A, 0x00],
nal_type: nal_unit_type::NON_IDR_SLICE,
}];
let seg = muxer.add_frame(&nals, 3000, 3000, 3000, false);
assert!(seg.is_none());
assert_eq!(muxer.pending_frame_count(), 2);
let seg = muxer.flush();
assert!(seg.is_some());
let seg = seg.unwrap();
assert!(seg.windows(4).any(|w| w == b"styp"));
assert!(seg.windows(4).any(|w| w == b"moof"));
assert!(seg.windows(4).any(|w| w == b"mdat"));
}
#[test]
fn test_parse_av1_obus() {
let mut data = Vec::new();
data.push(0x0A); data.push(3); data.extend_from_slice(&[0xAA, 0xBB, 0xCC]); data.push(0x32); data.push(2); data.extend_from_slice(&[0xDD, 0xEE]);
let obus = parse_av1_obus(&data);
assert_eq!(obus.len(), 2);
assert_eq!(obus[0].obu_type, obu_type::SEQUENCE_HEADER);
assert_eq!(obus[0].data.len(), 5); assert_eq!(obus[1].obu_type, obu_type::FRAME);
assert_eq!(obus[1].data.len(), 4); }
#[test]
fn test_extract_av1_sequence_header() {
let mut data = Vec::new();
data.push(0x12); data.push(0); data.push(0x0A); data.push(2);
data.extend_from_slice(&[0x11, 0x22]);
let seq_hdr = extract_av1_sequence_header(&data);
assert!(seq_hdr.is_some());
let seq_hdr = seq_hdr.unwrap();
assert_eq!(seq_hdr[0], 0x0A); assert_eq!(seq_hdr.len(), 4); }
#[test]
fn test_av1_cmaf_init_segment() {
let mut muxer = Av1CmafMuxer::new(CmafConfig::default());
assert!(!muxer.is_initialized());
let seq_hdr = vec![0x0A, 0x02, 0x00, 0x00];
let init = muxer.create_init_segment(&seq_hdr, 640, 480);
assert!(muxer.is_initialized());
assert!(!init.is_empty());
assert_eq!(&init[4..8], b"ftyp");
assert!(init.windows(4).any(|w| w == b"moov"));
assert!(init.windows(4).any(|w| w == b"av01"));
assert!(init.windows(4).any(|w| w == b"av1C"));
}
#[test]
fn test_av1_cmaf_add_frame_and_flush() {
let mut muxer = Av1CmafMuxer::new(CmafConfig {
fragment_duration_ms: 33,
timescale: 90000,
});
let seq_hdr = vec![0x0A, 0x02, 0x00, 0x00];
muxer.create_init_segment(&seq_hdr, 640, 480);
let seg = muxer.add_frame(
&[0x32, 0x05, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE],
0,
0,
3000,
true,
);
assert!(seg.is_none());
assert_eq!(muxer.pending_frame_count(), 1);
let seg = muxer.add_frame(&[0x32, 0x03, 0x11, 0x22, 0x33], 3000, 3000, 3000, false);
assert!(seg.is_none());
let seg = muxer.flush();
assert!(seg.is_some());
let seg = seg.unwrap();
assert!(seg.windows(4).any(|w| w == b"styp"));
assert!(seg.windows(4).any(|w| w == b"moof"));
assert!(seg.windows(4).any(|w| w == b"mdat"));
}
}