use crate::avc_config::{AVCConfigurationBox, AVCDecoderConfigurationRecord};
use crate::error::{Error, Result};
use crate::hevc_config::{HEVCConfigurationBox, HEVCDecoderConfigurationRecord};
use alloc::vec::Vec;
use broadcast_common::Serialize;
const VISUAL_SAMPLE_ENTRY_SIZE: usize = 78;
const BARE_ENTRY_MIN_LEN: usize = 8 + VISUAL_SAMPLE_ENTRY_SIZE + 8;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct VisualSampleEntryFields {
pub data_reference_index: u16,
pub width: u16,
pub height: u16,
pub horizontal_resolution: u32,
pub vertical_resolution: u32,
pub data_size: u32,
pub frame_count: u16,
pub compressorname: [u8; 32],
pub depth: u16,
pub predefined: u16,
}
impl Default for VisualSampleEntryFields {
fn default() -> Self {
Self {
data_reference_index: 1,
width: 0,
height: 0,
horizontal_resolution: 0x00480000,
vertical_resolution: 0x00480000,
data_size: 0,
frame_count: 1,
compressorname: [0u8; 32],
depth: 0x0018,
predefined: 0xFFFF,
}
}
}
impl VisualSampleEntryFields {
pub const fn serialized_len() -> usize {
VISUAL_SAMPLE_ENTRY_SIZE
}
pub fn parse_body(bytes: &[u8], what: &'static str) -> Result<Self> {
let body = crate::box_types::box_body(bytes, VISUAL_SAMPLE_ENTRY_SIZE, what)?;
Ok(Self {
data_reference_index: u16::from_be_bytes([body[6], body[7]]),
width: u16::from_be_bytes([body[24], body[25]]),
height: u16::from_be_bytes([body[26], body[27]]),
horizontal_resolution: u32::from_be_bytes([body[28], body[29], body[30], body[31]]),
vertical_resolution: u32::from_be_bytes([body[32], body[33], body[34], body[35]]),
data_size: u32::from_be_bytes([body[36], body[37], body[38], body[39]]),
frame_count: u16::from_be_bytes([body[40], body[41]]),
compressorname: {
let mut c = [0u8; 32];
c.copy_from_slice(&body[42..74]);
c
},
depth: u16::from_be_bytes([body[74], body[75]]),
predefined: u16::from_be_bytes([body[76], body[77]]),
})
}
pub(crate) fn children_region<'a>(bytes: &'a [u8], what: &'static str) -> Result<&'a [u8]> {
Ok(
&crate::box_types::box_body(bytes, VISUAL_SAMPLE_ENTRY_SIZE, what)?
[VISUAL_SAMPLE_ENTRY_SIZE..],
)
}
pub fn serialize_body_into(&self, buf: &mut [u8]) -> Result<usize> {
self.serialize_into(buf)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if buf.len() < VISUAL_SAMPLE_ENTRY_SIZE {
return Err(Error::OutputBufferTooSmall {
need: VISUAL_SAMPLE_ENTRY_SIZE,
have: buf.len(),
});
}
let mut cursor = 0usize;
buf[cursor..cursor + 6].fill(0);
cursor += 6;
buf[cursor..cursor + 2].copy_from_slice(&self.data_reference_index.to_be_bytes());
cursor += 2;
buf[cursor..cursor + 16].fill(0);
cursor += 16;
buf[cursor..cursor + 2].copy_from_slice(&self.width.to_be_bytes());
cursor += 2;
buf[cursor..cursor + 2].copy_from_slice(&self.height.to_be_bytes());
cursor += 2;
buf[cursor..cursor + 4].copy_from_slice(&self.horizontal_resolution.to_be_bytes());
cursor += 4;
buf[cursor..cursor + 4].copy_from_slice(&self.vertical_resolution.to_be_bytes());
cursor += 4;
buf[cursor..cursor + 4].copy_from_slice(&self.data_size.to_be_bytes());
cursor += 4;
buf[cursor..cursor + 2].copy_from_slice(&self.frame_count.to_be_bytes());
cursor += 2;
buf[cursor..cursor + 32].copy_from_slice(&self.compressorname);
cursor += 32;
buf[cursor..cursor + 2].copy_from_slice(&self.depth.to_be_bytes());
cursor += 2;
buf[cursor..cursor + 2].copy_from_slice(&self.predefined.to_be_bytes());
cursor += 2;
Ok(cursor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OpaqueBox {
pub box_type: [u8; 4],
pub data: Vec<u8>,
}
impl OpaqueBox {
fn serialized_len(&self) -> usize {
8 + self.data.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
let size = broadcast_common::len::fit_u32(need, "opaque child size")?;
buf[..4].copy_from_slice(&size.to_be_bytes());
buf[4..8].copy_from_slice(&self.box_type);
buf[8..8 + self.data.len()].copy_from_slice(&self.data);
Ok(need)
}
}
pub(crate) fn find_config_box<'a>(region: &'a [u8], fourcc: &[u8; 4]) -> Option<&'a [u8]> {
let mut off = 0usize;
while off + 8 <= region.len() {
let Some(size) = crate::box_types::child_extent(®ion[off..]) else {
break;
};
let ty = ®ion[off + 4..off + 8];
let clamped = size.min(region.len() - off);
if ty == fourcc {
return Some(®ion[off..off + clamped]);
}
off += size;
}
None
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum SampleEntryChild {
Config,
Other(Vec<u8>),
}
pub(crate) fn entry_children(region: &[u8], fourcc: &[u8; 4]) -> Result<Vec<SampleEntryChild>> {
let mut out = Vec::new();
let mut off = 0usize;
while off < region.len() {
let Some(rest) = region.get(off..) else {
break;
};
let (bx, consumed) = crate::box_types::parse_box(rest)?;
if bx.header.box_type.is(fourcc) {
out.push(SampleEntryChild::Config);
} else {
out.push(SampleEntryChild::Other(rest[..consumed].to_vec()));
}
if bx.header.size == 0 || consumed == 0 {
break;
}
off += consumed;
}
Ok(out)
}
pub(crate) fn serialize_entry_children(
children: &[SampleEntryChild],
write_config: impl Fn(&mut [u8], &mut usize) -> Result<()>,
buf: &mut [u8],
c: &mut usize,
) -> Result<()> {
for child in children {
match child {
SampleEntryChild::Config => write_config(buf, c)?,
SampleEntryChild::Other(bytes) => {
buf[*c..*c + bytes.len()].copy_from_slice(bytes);
*c += bytes.len();
}
}
}
Ok(())
}
pub(crate) fn entry_children_len(children: &[SampleEntryChild], config_len: usize) -> usize {
children
.iter()
.map(|c| match c {
SampleEntryChild::Config => config_len,
SampleEntryChild::Other(bytes) => bytes.len(),
})
.sum()
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct AVCSampleEntry {
pub codec_type: [u8; 4],
pub visual: VisualSampleEntryFields,
pub config: AVCConfigurationBox,
pub extra_boxes: Vec<OpaqueBox>,
}
impl AVCSampleEntry {
pub fn bare_parse(bytes: &[u8]) -> Result<Self> {
use crate::avc_config::AVCConfigurationBox;
if bytes.len() < BARE_ENTRY_MIN_LEN {
return Err(Error::BufferTooShort {
need: BARE_ENTRY_MIN_LEN,
have: bytes.len(),
what: "avc1 bare",
});
}
let codec_type = [bytes[4], bytes[5], bytes[6], bytes[7]];
let body = &bytes[crate::box_types::box_header_len(bytes)?..];
let visual = VisualSampleEntryFields::parse_body(bytes, "avc1 bare")?;
let config_region = &body[VISUAL_SAMPLE_ENTRY_SIZE..];
if let Some(avcc) = find_config_box(config_region, b"avcC") {
let config = if avcc.len() > 8 {
AVCConfigurationBox::parse_body(&avcc[crate::box_types::box_header_len(avcc)?..])?
} else {
return Err(Error::BufferTooShort {
need: 8,
have: avcc.len(),
what: "avcC body",
});
};
let extra_boxes = capture_extra_boxes(config_region, &[b"avcC"]);
Ok(Self {
codec_type,
visual,
config,
extra_boxes,
})
} else {
Err(Error::BufferTooShort {
need: 0,
have: 0,
what: "avc1 missing avcC",
})
}
}
pub fn new_avc1(config: AVCDecoderConfigurationRecord) -> Self {
let compressorname = {
let mut c = [0u8; 32];
c[0] = 11;
c[1..11].copy_from_slice(b"AVC Coding");
c
};
Self {
codec_type: *b"avc1",
visual: VisualSampleEntryFields {
compressorname,
..VisualSampleEntryFields::default()
},
config: AVCConfigurationBox::new(config),
extra_boxes: Vec::new(),
}
}
pub fn new_avc3(config: AVCDecoderConfigurationRecord) -> Self {
let mut s = Self::new_avc1(config);
s.codec_type = *b"avc3";
s
}
}
impl Serialize for AVCSampleEntry {
type Error = Error;
fn serialized_len(&self) -> usize {
let mut n = 8 + VisualSampleEntryFields::serialized_len() + self.config.serialized_len();
for eb in &self.extra_boxes {
n += eb.serialized_len();
}
crate::box_types::box_len(n)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
let mut cursor = 0usize;
cursor += crate::box_types::write_box_header(buf, &self.codec_type, need)?;
cursor += self.visual.serialize_into(&mut buf[cursor..])?;
cursor += self.config.serialize_into(&mut buf[cursor..])?;
for eb in &self.extra_boxes {
cursor += eb.serialize_into(&mut buf[cursor..])?;
}
Ok(cursor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Mp4vSampleEntry {
pub visual: VisualSampleEntryFields,
pub config_boxes: Vec<crate::init_segment::OpaqueBox>,
}
impl Mp4vSampleEntry {
pub const FOURCC: [u8; 4] = *b"mp4v";
pub fn bare_parse(bytes: &[u8]) -> Result<Self> {
let compact = crate::box_types::compact_form(bytes)?;
let bytes: &[u8] = &compact;
let visual = VisualSampleEntryFields::parse_body(bytes, "mp4v")?;
let region = &bytes[8 + VISUAL_SAMPLE_ENTRY_SIZE..];
let config_boxes = parse_trailing_boxes(region);
Ok(Self {
visual,
config_boxes,
})
}
}
impl Serialize for Mp4vSampleEntry {
type Error = Error;
fn serialized_len(&self) -> usize {
let mut n = 8 + VisualSampleEntryFields::serialized_len();
for b in &self.config_boxes {
n += b.serialized_len();
}
crate::box_types::box_len(n)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
let mut cursor = 0usize;
cursor += crate::box_types::write_box_header(buf, &Self::FOURCC, need)?;
cursor += self.visual.serialize_into(&mut buf[cursor..])?;
for b in &self.config_boxes {
cursor += b.serialize_into(&mut buf[cursor..])?;
}
Ok(cursor)
}
}
fn capture_extra_boxes(region: &[u8], skip: &[&[u8; 4]]) -> Vec<OpaqueBox> {
let mut out = Vec::new();
let mut off = 0usize;
while off + 8 <= region.len() {
let Some(sz) = crate::box_types::child_extent(®ion[off..]) else {
break;
};
if sz > region.len() - off {
break;
}
let hdr = crate::box_types::box_header_len(®ion[off..]).unwrap_or(8);
let bt = [
region[off + 4],
region[off + 5],
region[off + 6],
region[off + 7],
];
if !skip.iter().any(|k| **k == bt) {
out.push(OpaqueBox {
box_type: bt,
data: region[off + hdr..off + sz].to_vec(),
});
}
off += sz;
}
out
}
fn parse_trailing_boxes(region: &[u8]) -> Vec<crate::init_segment::OpaqueBox> {
let mut out = Vec::new();
let mut off = 0usize;
while off + 8 <= region.len() {
let Some(sz) = crate::box_types::child_extent(®ion[off..]) else {
break;
};
if sz > region.len() - off {
break;
}
let hdr = crate::box_types::box_header_len(®ion[off..]).unwrap_or(8);
let bt = [
region[off + 4],
region[off + 5],
region[off + 6],
region[off + 7],
];
let mut child =
crate::init_segment::OpaqueBox::new(bt, region[off + hdr..off + sz].to_vec());
child.largesize = hdr != 8;
out.push(child);
off += sz;
}
out
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct HEVCSampleEntry {
pub codec_type: [u8; 4],
pub visual: VisualSampleEntryFields,
pub config: HEVCConfigurationBox,
pub extra_boxes: Vec<OpaqueBox>,
}
impl HEVCSampleEntry {
pub fn bare_parse(bytes: &[u8]) -> Result<Self> {
use crate::hevc_config::HEVCConfigurationBox;
if bytes.len() < BARE_ENTRY_MIN_LEN {
return Err(Error::BufferTooShort {
need: BARE_ENTRY_MIN_LEN,
have: bytes.len(),
what: "hvc1 bare",
});
}
let codec_type = [bytes[4], bytes[5], bytes[6], bytes[7]];
let body = &bytes[crate::box_types::box_header_len(bytes)?..];
let visual = VisualSampleEntryFields::parse_body(bytes, "hvc1 bare")?;
let config_region = &body[VISUAL_SAMPLE_ENTRY_SIZE..];
if let Some(hvcc) = find_config_box(config_region, b"hvcC") {
let config = if hvcc.len() > 8 {
HEVCConfigurationBox::parse_body(&hvcc[crate::box_types::box_header_len(hvcc)?..])?
} else {
return Err(Error::BufferTooShort {
need: 8,
have: hvcc.len(),
what: "hvcC body",
});
};
Ok(Self {
codec_type,
visual,
config,
extra_boxes: capture_extra_boxes(config_region, &[b"hvcC"]),
})
} else {
Err(Error::BufferTooShort {
need: 0,
have: 0,
what: "hvc1 missing hvcC",
})
}
}
pub fn new_hvc1(config: HEVCDecoderConfigurationRecord) -> Self {
let compressorname = {
let mut c = [0u8; 32];
c[0] = 12;
c[1..12].copy_from_slice(b"HEVC Coding");
c
};
Self {
codec_type: *b"hvc1",
visual: VisualSampleEntryFields {
compressorname,
..VisualSampleEntryFields::default()
},
config: HEVCConfigurationBox::new(config),
extra_boxes: Vec::new(),
}
}
pub fn new_hev1(config: HEVCDecoderConfigurationRecord) -> Self {
let mut s = Self::new_hvc1(config);
s.codec_type = *b"hev1";
s
}
}
impl Serialize for HEVCSampleEntry {
type Error = Error;
fn serialized_len(&self) -> usize {
let mut n = 8 + VisualSampleEntryFields::serialized_len() + self.config.serialized_len();
for eb in &self.extra_boxes {
n += eb.serialized_len();
}
crate::box_types::box_len(n)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
let mut cursor = 0usize;
cursor += crate::box_types::write_box_header(buf, &self.codec_type, need)?;
cursor += self.visual.serialize_into(&mut buf[cursor..])?;
cursor += self.config.serialize_into(&mut buf[cursor..])?;
for eb in &self.extra_boxes {
cursor += eb.serialize_into(&mut buf[cursor..])?;
}
Ok(cursor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct VVCSampleEntry {
pub codec_type: [u8; 4],
pub visual: VisualSampleEntryFields,
pub config: crate::vvc_config::VvcConfigurationBox,
pub extra_boxes: Vec<OpaqueBox>,
}
impl VVCSampleEntry {
pub fn bare_parse(bytes: &[u8]) -> Result<Self> {
let compact = crate::box_types::compact_form(bytes)?;
let bytes: &[u8] = &compact;
use crate::vvc_config::VvcConfigurationBox;
if bytes.len() < 8 + 78 + 8 {
return Err(Error::BufferTooShort {
need: 8 + 80 + 8,
have: bytes.len(),
what: "vvc1 bare",
});
}
let codec_type = [bytes[4], bytes[5], bytes[6], bytes[7]];
let visual = VisualSampleEntryFields::parse_body(bytes, "vvc1")?;
let config_region = &bytes[8 + VISUAL_SAMPLE_ENTRY_SIZE..];
if let Some(vvcc) = find_config_box(config_region, b"vvcC") {
let config = if vvcc.len() > 8 {
VvcConfigurationBox::parse_body(&vvcc[crate::box_types::box_header_len(vvcc)?..])?
} else {
return Err(Error::BufferTooShort {
need: 8,
have: vvcc.len(),
what: "vvcC body",
});
};
Ok(Self {
codec_type,
visual,
config,
extra_boxes: capture_extra_boxes(config_region, &[b"vvcC"]),
})
} else {
Err(Error::BufferTooShort {
need: 0,
have: 0,
what: "vvc1 missing vvcC",
})
}
}
pub fn new_vvc1(config: crate::vvc_config::VvcConfigurationBox) -> Self {
let compressorname = {
let mut c = [0u8; 32];
c[0] = 10;
c[1..11].copy_from_slice(b"VVC Coding");
c
};
Self {
codec_type: *b"vvc1",
visual: VisualSampleEntryFields {
compressorname,
..VisualSampleEntryFields::default()
},
config,
extra_boxes: Vec::new(),
}
}
pub fn new_vvi1(config: crate::vvc_config::VvcConfigurationBox) -> Self {
let mut s = Self::new_vvc1(config);
s.codec_type = *b"vvi1";
s
}
}
impl Serialize for VVCSampleEntry {
type Error = Error;
fn serialized_len(&self) -> usize {
let mut n = 8 + VisualSampleEntryFields::serialized_len() + self.config.serialized_len();
for eb in &self.extra_boxes {
n += eb.serialized_len();
}
crate::box_types::box_len(n)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
let mut cursor = 0usize;
cursor += crate::box_types::write_box_header(buf, &self.codec_type, need)?;
cursor += self.visual.serialize_into(&mut buf[cursor..])?;
cursor += self.config.serialize_into(&mut buf[cursor..])?;
for eb in &self.extra_boxes {
cursor += eb.serialize_into(&mut buf[cursor..])?;
}
Ok(cursor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::avc_config::AVCDecoderConfigurationRecord;
use crate::hevc_config::HEVCDecoderConfigurationRecord;
use alloc::vec;
use broadcast_common::Serialize;
fn make_avc_config() -> AVCDecoderConfigurationRecord {
AVCDecoderConfigurationRecord {
configuration_version: 1,
profile_indication: 100,
profile_compatibility: 0,
level_indication: 30,
length_size_minus_one: 3,
sps: alloc::vec![crate::nalu_types::AvcSps(vec![0x67, 0x64, 0x00, 0x1E])],
pps: alloc::vec![crate::nalu_types::AvcPps(vec![0x68, 0xEE, 0x3C])],
chroma_format: Some(1),
bit_depth_luma_minus8: Some(0),
bit_depth_chroma_minus8: Some(0),
sps_ext: alloc::vec![],
}
}
fn make_hevc_config() -> HEVCDecoderConfigurationRecord {
HEVCDecoderConfigurationRecord {
configuration_version: 1,
general_profile_space: 0,
general_tier_flag: false,
general_profile_idc: 1,
general_profile_compatibility_flags: 0,
general_constraint_indicator_flags: 0,
general_level_idc: 93,
min_spatial_segmentation_idc: 0,
parallelism_type: 0,
chroma_format_idc: 1,
bit_depth_luma_minus8: 0,
bit_depth_chroma_minus8: 0,
avg_frame_rate: 0,
constant_frame_rate: 0,
num_temporal_layers: 1,
temporal_id_nested: false,
length_size_minus_one: 3,
arrays: alloc::vec![crate::nalu_types::HevcNalArray {
array_completeness: true,
nal_unit_type: 32,
nalus: alloc::vec![crate::nalu_types::HevcNalUnit(vec![
0x40, 0x01, 0x0C, 0x01, 0xFF
])],
}],
}
}
#[test]
fn hevc_entry_keeps_every_sibling_box() {
let entry = HEVCSampleEntry::new_hvc1(make_hevc_config());
let mut bytes = entry.try_to_bytes().unwrap();
let colr: [u8; 19] = [
0x00, 0x00, 0x00, 0x13, b'c', b'o', b'l', b'r', b'n', b'c', b'l', b'x', 0x00, 0x09,
0x00, 0x10, 0x00, 0x09, 0x80,
];
let dvc_cfg: [u8; 12] = [
0x00, 0x00, 0x00, 0x0C, b'd', b'v', b'c', b'C', 0x01, 0x00, 0x00, 0x00,
];
bytes.extend_from_slice(&colr);
bytes.extend_from_slice(&dvc_cfg);
let total = bytes.len() as u32;
bytes[0..4].copy_from_slice(&total.to_be_bytes());
let parsed = HEVCSampleEntry::bare_parse(&bytes).unwrap();
let types: alloc::vec::Vec<[u8; 4]> =
parsed.extra_boxes.iter().map(|b| b.box_type).collect();
assert!(
types.contains(b"colr") && types.contains(b"dvcC"),
"colr and dvcC must be captured, got {types:?}"
);
assert_eq!(parsed.try_to_bytes().unwrap(), bytes);
}
#[test]
fn vvc_entry_keeps_every_sibling_box() {
use crate::vvc_config::VvcConfigurationBox;
use crate::vvc_config::VvcDecoderConfigurationRecord;
let entry =
VVCSampleEntry::new_vvc1(VvcConfigurationBox::new(VvcDecoderConfigurationRecord {
length_size_minus_one: 3,
ptl_present: false,
ols_idx: 0,
num_sublayers: 1,
constant_frame_rate: 0,
chroma_format_idc: 1,
bit_depth_minus8: 0,
ptl: None,
max_picture_width: 0,
max_picture_height: 0,
avg_frame_rate: 0,
arrays: alloc::vec![],
}));
let mut bytes = entry.try_to_bytes().unwrap();
let colr: [u8; 19] = [
0x00, 0x00, 0x00, 0x13, b'c', b'o', b'l', b'r', b'n', b'c', b'l', b'x', 0x00, 0x09,
0x00, 0x10, 0x00, 0x09, 0x80,
];
bytes.extend_from_slice(&colr);
let total = bytes.len() as u32;
bytes[0..4].copy_from_slice(&total.to_be_bytes());
let parsed = VVCSampleEntry::bare_parse(&bytes).unwrap();
assert!(
parsed.extra_boxes.iter().any(|b| b.box_type == *b"colr"),
"vvc1 must capture colr"
);
assert_eq!(parsed.try_to_bytes().unwrap(), bytes);
}
#[test]
fn capture_extra_boxes_stops_at_a_truncated_tail() {
let mut region = alloc::vec::Vec::new();
region.extend_from_slice(&[
0x00, 0x00, 0x00, 0x0C, b'c', b'o', b'l', b'r', 0x01, 0x02, 0x03, 0x04,
]);
let after = region.len();
region.extend_from_slice(&[0x00, 0x00, 0xFF, 0xFF, b'b', b't', b'r', b't']);
region.extend_from_slice(&[0x00, 0x00, 0x00, 0x0C, b'c', b'l', b'l', b'i', 9, 9, 9, 9]);
let boxes = capture_extra_boxes(®ion, &[]);
assert_eq!(boxes.len(), 1, "only the box before the damage is kept");
assert_eq!(boxes[0].box_type, *b"colr");
assert_eq!(boxes[0].data, vec![0x01, 0x02, 0x03, 0x04]);
assert_eq!(after, 12);
let mut under = alloc::vec::Vec::new();
under.extend_from_slice(&[0x00, 0x00, 0x00, 0x04, b'z', b'z', b'z', b'z']);
assert!(capture_extra_boxes(&under, &[]).is_empty());
let mut exact = alloc::vec::Vec::new();
exact.extend_from_slice(&[0x00, 0x00, 0x00, 0x08, b'p', b'a', b's', b'p']);
assert_eq!(capture_extra_boxes(&exact, &[]).len(), 1);
}
#[test]
fn avc_entry_keeps_boxes_before_the_config() {
let entry = AVCSampleEntry::new_avc1(make_avc_config());
let bytes = entry.try_to_bytes().unwrap();
let avcc_at = bytes.windows(4).position(|w| w == b"avcC").unwrap() - 4;
let pasp: [u8; 16] = [
0x00, 0x00, 0x00, 0x10, b'p', b'a', b's', b'p', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x01,
];
let mut with_pasp = alloc::vec::Vec::new();
with_pasp.extend_from_slice(&bytes[..avcc_at]);
with_pasp.extend_from_slice(&pasp);
with_pasp.extend_from_slice(&bytes[avcc_at..]);
let total = with_pasp.len() as u32;
with_pasp[0..4].copy_from_slice(&total.to_be_bytes());
let parsed = AVCSampleEntry::bare_parse(&with_pasp).unwrap();
assert!(
parsed.extra_boxes.iter().any(|b| b.box_type == *b"pasp"),
"pasp before avcC must be captured"
);
let re = parsed.try_to_bytes().unwrap();
let pasp_at = re.windows(4).position(|w| w == b"pasp").unwrap();
assert_eq!(&re[pasp_at..pasp_at + 12], &pasp[4..]);
assert_eq!(AVCSampleEntry::bare_parse(&re).unwrap(), parsed);
assert_eq!(re.len(), with_pasp.len(), "same total length");
}
#[test]
fn find_config_box_hostile_size_does_not_panic() {
let mut region = alloc::vec![0u8; 8];
region[0..4].copy_from_slice(&0xFFFF_FFFFu32.to_be_bytes());
region[4..8].copy_from_slice(b"avcC");
let found = find_config_box(®ion, b"avcC").expect("fourcc matches at offset 0");
assert_eq!(
found.len(),
region.len(),
"hostile size must clamp to the region's actual remaining length"
);
}
#[test]
fn find_config_box_hostile_size_non_matching_fourcc_does_not_panic() {
let mut region = alloc::vec![0u8; 8];
region[0..4].copy_from_slice(&0xFFFF_FFFFu32.to_be_bytes());
region[4..8].copy_from_slice(b"zzzz");
assert!(find_config_box(®ion, b"avcC").is_none());
}
#[test]
fn test_avc1_serialize() {
let config = make_avc_config();
let entry = AVCSampleEntry::new_avc1(config);
let bytes = entry.try_to_bytes().unwrap();
assert!(bytes.len() > 8);
assert_eq!(&bytes[4..8], b"avc1");
assert!(bytes.windows(4).any(|w| w == b"avcC"));
}
#[test]
fn test_avc3_serialize() {
let config = make_avc_config();
let entry = AVCSampleEntry::new_avc3(config);
let bytes = entry.try_to_bytes().unwrap();
assert_eq!(&bytes[4..8], b"avc3");
}
#[test]
fn test_hvc1_serialize() {
let config = make_hevc_config();
let entry = HEVCSampleEntry::new_hvc1(config);
let bytes = entry.try_to_bytes().unwrap();
assert_eq!(&bytes[4..8], b"hvc1");
assert!(bytes.windows(4).any(|w| w == b"hvcC"));
}
#[test]
fn test_hev1_serialize() {
let config = make_hevc_config();
let entry = HEVCSampleEntry::new_hev1(config);
let bytes = entry.try_to_bytes().unwrap();
assert_eq!(&bytes[4..8], b"hev1");
}
#[test]
fn reserved_fields_written_as_zeros_into_a_dirty_buffer() {
let entry = AVCSampleEntry::new_avc1(make_avc_config());
let clean = entry.try_to_bytes().unwrap();
let mut dirty = vec![0xFFu8; clean.len()];
let written = entry
.serialize_into(&mut dirty)
.expect("serialize into a correctly-sized buffer");
assert_eq!(written, clean.len());
assert_eq!(
dirty, clean,
"a 0xFF-filled buffer must yield the same bytes as to_bytes()"
);
assert_eq!(
&clean[8..14],
&[0u8; 6],
"6 reserved bytes after the header"
);
assert_eq!(
&clean[16..32],
&[0u8; 16],
"pre_defined(16)+reserved(16) after data_reference_index"
);
}
}