use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::{Parse, Serialize};
const BOX_HEADER_SIZE: usize = 8;
const FULLBOX_EXTRA_SIZE: usize = 4;
const STTS_TYPE: u32 = u32::from_be_bytes(*b"stts");
const CTTS_TYPE: u32 = u32::from_be_bytes(*b"ctts");
const CSLG_TYPE: u32 = u32::from_be_bytes(*b"cslg");
const ELST_TYPE: u32 = u32::from_be_bytes(*b"elst");
const SIDX_TYPE: u32 = u32::from_be_bytes(*b"sidx");
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SttsEntry {
pub sample_count: u32,
pub sample_delta: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct TimeToSampleBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<SttsEntry>,
}
impl TimeToSampleBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + 4,
have: body.len(),
what: "stts body",
});
}
let version = body[0];
let flags = u32::from_be_bytes([0, body[1], body[2], body[3]]);
let entry_count = u32::from_be_bytes([body[4], body[5], body[6], body[7]]) as usize;
let mut c = FULLBOX_EXTRA_SIZE + 4;
let mut entries = Vec::with_capacity(entry_count);
for _ in 0..entry_count {
if body.len() < c + 8 {
return Err(Error::BufferTooShort {
need: c + 8,
have: body.len(),
what: "stts entry",
});
}
let sample_count = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
let sample_delta =
u32::from_be_bytes([body[c + 4], body[c + 5], body[c + 6], body[c + 7]]);
entries.push(SttsEntry {
sample_count,
sample_delta,
});
c += 8;
}
Ok(Self {
version,
flags,
entries,
})
}
}
impl<'a> Parse<'a> for TimeToSampleBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4,
have: bytes.len(),
what: "stts box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != STTS_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected stts",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for TimeToSampleBox {
type Error = Error;
fn serialized_len(&self) -> usize {
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + self.entries.len() * 8
}
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 c = 0;
buf[c..c + 4].copy_from_slice(&(need as u32).to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(b"stts");
c += 4;
buf[c] = self.version;
let fb = self.flags.to_be_bytes();
buf[c + 1] = fb[1];
buf[c + 2] = fb[2];
buf[c + 3] = fb[3];
c += 4;
buf[c..c + 4].copy_from_slice(&(self.entries.len() as u32).to_be_bytes());
c += 4;
for entry in &self.entries {
buf[c..c + 4].copy_from_slice(&entry.sample_count.to_be_bytes());
buf[c + 4..c + 8].copy_from_slice(&entry.sample_delta.to_be_bytes());
c += 8;
}
Ok(c)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CttsEntry {
pub sample_count: u32,
pub sample_offset: i32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CompositionOffsetBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<CttsEntry>,
}
impl CompositionOffsetBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + 4,
have: body.len(),
what: "ctts body",
});
}
let version = body[0];
let flags = u32::from_be_bytes([0, body[1], body[2], body[3]]);
let entry_count = u32::from_be_bytes([body[4], body[5], body[6], body[7]]) as usize;
let entry_size: usize = 8; let mut c = FULLBOX_EXTRA_SIZE + 4;
let mut entries = Vec::with_capacity(entry_count);
for _ in 0..entry_count {
if body.len() < c + entry_size {
return Err(Error::BufferTooShort {
need: c + entry_size,
have: body.len(),
what: "ctts entry",
});
}
let sample_count = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
let raw_offset =
u32::from_be_bytes([body[c + 4], body[c + 5], body[c + 6], body[c + 7]]);
let sample_offset = raw_offset as i32; entries.push(CttsEntry {
sample_count,
sample_offset,
});
c += entry_size;
}
Ok(Self {
version,
flags,
entries,
})
}
}
impl<'a> Parse<'a> for CompositionOffsetBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4,
have: bytes.len(),
what: "ctts box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != CTTS_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected ctts",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for CompositionOffsetBox {
type Error = Error;
fn serialized_len(&self) -> usize {
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + self.entries.len() * 8
}
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 c = 0;
buf[c..c + 4].copy_from_slice(&(need as u32).to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(b"ctts");
c += 4;
buf[c] = self.version;
let fb = self.flags.to_be_bytes();
buf[c + 1] = fb[1];
buf[c + 2] = fb[2];
buf[c + 3] = fb[3];
c += 4;
buf[c..c + 4].copy_from_slice(&(self.entries.len() as u32).to_be_bytes());
c += 4;
for entry in &self.entries {
buf[c..c + 4].copy_from_slice(&entry.sample_count.to_be_bytes());
buf[c + 4..c + 8].copy_from_slice(&(entry.sample_offset as u32).to_be_bytes());
c += 8;
}
Ok(c)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CompositionToDecodeBox {
pub version: u8,
pub flags: u32,
pub composition_to_dts_shift: i64,
pub least_decode_to_display_delta: i64,
pub greatest_decode_to_display_delta: i64,
pub composition_start_time: i64,
pub composition_end_time: i64,
}
impl CompositionToDecodeBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + 4,
have: body.len(),
what: "cslg body",
});
}
let version = body[0];
let flags = u32::from_be_bytes([0, body[1], body[2], body[3]]);
let payload = &body[FULLBOX_EXTRA_SIZE..];
let (fld_size, have): (usize, &str) = if version == 0 {
(4, "cslg v0 field")
} else {
(8, "cslg v1 field")
};
if payload.len() < fld_size * 5 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + fld_size * 5,
have: body.len(),
what: have,
});
}
let mut c = 0usize;
let read_i64 = |buf: &[u8], off: usize, sz: usize| -> i64 {
if sz == 4 {
i32::from_be_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]) as i64
} else {
i64::from_be_bytes([
buf[off],
buf[off + 1],
buf[off + 2],
buf[off + 3],
buf[off + 4],
buf[off + 5],
buf[off + 6],
buf[off + 7],
])
}
};
let composition_to_dts_shift = read_i64(payload, c, fld_size);
c += fld_size;
let least_decode_to_display_delta = read_i64(payload, c, fld_size);
c += fld_size;
let greatest_decode_to_display_delta = read_i64(payload, c, fld_size);
c += fld_size;
let composition_start_time = read_i64(payload, c, fld_size);
c += fld_size;
let composition_end_time = read_i64(payload, c, fld_size);
Ok(Self {
version,
flags,
composition_to_dts_shift,
least_decode_to_display_delta,
greatest_decode_to_display_delta,
composition_start_time,
composition_end_time,
})
}
}
impl<'a> Parse<'a> for CompositionToDecodeBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4,
have: bytes.len(),
what: "cslg box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != CSLG_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected cslg",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for CompositionToDecodeBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let fld = if self.version == 0 { 4 } else { 8 };
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + fld * 5
}
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 c = 0;
buf[c..c + 4].copy_from_slice(&(need as u32).to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(b"cslg");
c += 4;
buf[c] = self.version;
let fb = self.flags.to_be_bytes();
buf[c + 1] = fb[1];
buf[c + 2] = fb[2];
buf[c + 3] = fb[3];
c += 4;
let write_i64 = |buf: &mut [u8], off: usize, sz: usize, v: i64| {
if sz == 4 {
buf[off..off + 4].copy_from_slice(&(v as i32).to_be_bytes());
} else {
buf[off..off + 8].copy_from_slice(&v.to_be_bytes());
}
};
let fld = if self.version == 0 { 4 } else { 8 };
write_i64(buf, c, fld, self.composition_to_dts_shift);
c += fld;
write_i64(buf, c, fld, self.least_decode_to_display_delta);
c += fld;
write_i64(buf, c, fld, self.greatest_decode_to_display_delta);
c += fld;
write_i64(buf, c, fld, self.composition_start_time);
c += fld;
write_i64(buf, c, fld, self.composition_end_time);
Ok(c + fld)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EditListEntry {
pub segment_duration: u64,
pub media_time: i64,
pub media_rate_integer: i16,
pub media_rate_fraction: i16,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EditListBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<EditListEntry>,
}
impl EditListBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + 4,
have: body.len(),
what: "elst body",
});
}
let version = body[0];
let flags = u32::from_be_bytes([0, body[1], body[2], body[3]]);
let entry_count = u32::from_be_bytes([body[4], body[5], body[6], body[7]]) as usize;
let entry_size: usize = if version == 0 { 4 + 4 + 4 } else { 8 + 8 + 4 }; let mut c = FULLBOX_EXTRA_SIZE + 4;
let mut entries = Vec::with_capacity(entry_count);
for _ in 0..entry_count {
if body.len() < c + entry_size {
return Err(Error::BufferTooShort {
need: c + entry_size,
have: body.len(),
what: "elst entry",
});
}
let (segment_duration, media_time) = if version == 0 {
let sd =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as u64;
let mt_raw =
u32::from_be_bytes([body[c + 4], body[c + 5], body[c + 6], body[c + 7]]);
let mt = mt_raw as i32 as i64;
c += 8;
(sd, mt)
} else {
let sd = u64::from_be_bytes([
body[c],
body[c + 1],
body[c + 2],
body[c + 3],
body[c + 4],
body[c + 5],
body[c + 6],
body[c + 7],
]);
let mt = i64::from_be_bytes([
body[c + 8],
body[c + 9],
body[c + 10],
body[c + 11],
body[c + 12],
body[c + 13],
body[c + 14],
body[c + 15],
]);
c += 16;
(sd, mt)
};
let mr_int = i16::from_be_bytes([body[c], body[c + 1]]);
let mr_frac = i16::from_be_bytes([body[c + 2], body[c + 3]]);
c += 4;
entries.push(EditListEntry {
segment_duration,
media_time,
media_rate_integer: mr_int,
media_rate_fraction: mr_frac,
});
}
Ok(Self {
version,
flags,
entries,
})
}
}
impl<'a> Parse<'a> for EditListBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4,
have: bytes.len(),
what: "elst box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != ELST_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected elst",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for EditListBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let entry_wire = if self.version == 0 { 12 } else { 20 };
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + self.entries.len() * entry_wire
}
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 c = 0;
buf[c..c + 4].copy_from_slice(&(need as u32).to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(b"elst");
c += 4;
buf[c] = self.version;
let fb = self.flags.to_be_bytes();
buf[c + 1] = fb[1];
buf[c + 2] = fb[2];
buf[c + 3] = fb[3];
c += 4;
buf[c..c + 4].copy_from_slice(&(self.entries.len() as u32).to_be_bytes());
c += 4;
for entry in &self.entries {
if self.version == 0 {
buf[c..c + 4].copy_from_slice(&(entry.segment_duration as u32).to_be_bytes());
buf[c + 4..c + 8].copy_from_slice(&(entry.media_time as u32).to_be_bytes());
c += 8;
} else {
buf[c..c + 8].copy_from_slice(&entry.segment_duration.to_be_bytes());
buf[c + 8..c + 16].copy_from_slice(&entry.media_time.to_be_bytes());
c += 16;
}
buf[c..c + 2].copy_from_slice(&entry.media_rate_integer.to_be_bytes());
buf[c + 2..c + 4].copy_from_slice(&entry.media_rate_fraction.to_be_bytes());
c += 4;
}
Ok(c)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SidxReference {
pub reference_type: u8,
pub referenced_size: u32,
pub subsegment_duration: u32,
pub starts_with_sap: u8,
pub sap_type: u8,
pub sap_delta_time: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SegmentIndexBox {
pub version: u8,
pub flags: u32,
pub reference_id: u32,
pub timescale: u32,
pub earliest_presentation_time: u64,
pub first_offset: u64,
pub references: Vec<SidxReference>,
}
impl SegmentIndexBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA_SIZE + 4 + 4 + 4 + 2 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + 4 + 4 + 4 + 2,
have: body.len(),
what: "sidx body",
});
}
let version = body[0];
let flags = u32::from_be_bytes([0, body[1], body[2], body[3]]);
let mut c = FULLBOX_EXTRA_SIZE; let reference_id = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
let timescale = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
let (ept, first_offset) = if version == 0 {
let ept = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as u64;
c += 4;
let fo = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as u64;
c += 4;
(ept, fo)
} else {
let ept = u64::from_be_bytes([
body[c],
body[c + 1],
body[c + 2],
body[c + 3],
body[c + 4],
body[c + 5],
body[c + 6],
body[c + 7],
]);
c += 8;
let fo = u64::from_be_bytes([
body[c],
body[c + 1],
body[c + 2],
body[c + 3],
body[c + 4],
body[c + 5],
body[c + 6],
body[c + 7],
]);
c += 8;
(ept, fo)
};
if body.len() < c + 2 {
return Err(Error::BufferTooShort {
need: c + 2,
have: body.len(),
what: "sidx reserved+count",
});
}
let _reserved = body[c] >> 4; let reference_count = u16::from_be_bytes([body[c], body[c + 1]]) as usize;
c += 2;
let mut references = Vec::with_capacity(reference_count);
for _ in 0..reference_count {
if body.len() < c + 12 {
return Err(Error::BufferTooShort {
need: c + 12,
have: body.len(),
what: "sidx reference entry",
});
}
let raw_ref = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
let reference_type = ((raw_ref >> 31) & 1) as u8;
let referenced_size = raw_ref & 0x7FFF_FFFF;
c += 4;
let subsegment_duration =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
let raw_sap = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
let starts_with_sap = ((raw_sap >> 31) & 1) as u8;
let sap_type = ((raw_sap >> 28) & 0x7) as u8;
let sap_delta_time = raw_sap & 0x0FFF_FFFF;
c += 4;
references.push(SidxReference {
reference_type,
referenced_size,
subsegment_duration,
starts_with_sap,
sap_type,
sap_delta_time,
});
}
Ok(Self {
version,
flags,
reference_id,
timescale,
earliest_presentation_time: ept,
first_offset,
references,
})
}
}
impl<'a> Parse<'a> for SegmentIndexBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 {
return Err(Error::BufferTooShort {
need: BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4,
have: bytes.len(),
what: "sidx box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != SIDX_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected sidx",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for SegmentIndexBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let time_size: usize = if self.version == 0 { 4 } else { 8 };
BOX_HEADER_SIZE
+ FULLBOX_EXTRA_SIZE
+ 4 + 4 + time_size + time_size + 2 + self.references.len() * 12 }
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 c = 0;
buf[c..c + 4].copy_from_slice(&(need as u32).to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(b"sidx");
c += 4;
buf[c] = self.version;
let fb = self.flags.to_be_bytes();
buf[c + 1] = fb[1];
buf[c + 2] = fb[2];
buf[c + 3] = fb[3];
c += 4;
buf[c..c + 4].copy_from_slice(&self.reference_id.to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(&self.timescale.to_be_bytes());
c += 4;
if self.version == 0 {
buf[c..c + 4].copy_from_slice(&(self.earliest_presentation_time as u32).to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(&(self.first_offset as u32).to_be_bytes());
c += 4;
} else {
buf[c..c + 8].copy_from_slice(&self.earliest_presentation_time.to_be_bytes());
c += 8;
buf[c..c + 8].copy_from_slice(&self.first_offset.to_be_bytes());
c += 8;
}
buf[c..c + 2].copy_from_slice(&(self.references.len() as u16).to_be_bytes());
c += 2;
for r in &self.references {
let raw_ref = ((r.reference_type as u32) << 31) | (r.referenced_size & 0x7FFF_FFFF);
buf[c..c + 4].copy_from_slice(&raw_ref.to_be_bytes());
c += 4;
buf[c..c + 4].copy_from_slice(&r.subsegment_duration.to_be_bytes());
c += 4;
let raw_sap = ((r.starts_with_sap as u32) << 31)
| ((r.sap_type as u32) << 28)
| (r.sap_delta_time & 0x0FFF_FFFF);
buf[c..c + 4].copy_from_slice(&raw_sap.to_be_bytes());
c += 4;
}
Ok(c)
}
}
#[cfg(test)]
mod tests {
use super::*;
use broadcast_common::Serialize;
#[test]
fn stts_round_trip_empty() {
let b = TimeToSampleBox {
version: 0,
flags: 0,
entries: vec![],
};
let bytes = b.to_bytes();
let parsed = TimeToSampleBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn stts_round_trip_single_entry() {
let b = TimeToSampleBox {
version: 0,
flags: 0,
entries: vec![SttsEntry {
sample_count: 50,
sample_delta: 512,
}],
};
let bytes = b.to_bytes();
assert_eq!(bytes.len(), 8 + 4 + 4 + 8);
let parsed = TimeToSampleBox::parse(&bytes).unwrap();
assert_eq!(parsed.entries.len(), 1);
assert_eq!(parsed.entries[0].sample_count, 50);
assert_eq!(parsed.entries[0].sample_delta, 512);
}
#[test]
fn stts_parse_body_api() {
let b = TimeToSampleBox {
version: 0,
flags: 0,
entries: vec![SttsEntry {
sample_count: 10,
sample_delta: 300,
}],
};
let bytes = b.to_bytes();
let parsed = TimeToSampleBox::parse_body(&bytes[8..]).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn ctts_round_trip_v0() {
let b = CompositionOffsetBox {
version: 0,
flags: 0,
entries: vec![CttsEntry {
sample_count: 1,
sample_offset: 1024,
}],
};
let bytes = b.to_bytes();
let parsed = CompositionOffsetBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn ctts_parse_body_v0_multi() {
let b = CompositionOffsetBox {
version: 0,
flags: 0,
entries: vec![
CttsEntry {
sample_count: 1,
sample_offset: 1024,
},
CttsEntry {
sample_count: 2,
sample_offset: 512,
},
],
};
let bytes = b.to_bytes();
let parsed = CompositionOffsetBox::parse_body(&bytes[8..]).unwrap();
assert_eq!(parsed.entries.len(), 2);
assert_eq!(parsed.entries[1].sample_offset, 512);
}
#[test]
fn ctts_parse_wrong_type() {
let bytes = [0, 0, 0, 16, b'x', b'x', b'x', b'x', 0, 0, 0, 0, 0, 0, 0, 0];
let result = CompositionOffsetBox::parse(&bytes);
assert!(result.is_err());
}
#[test]
fn cslg_round_trip_v0() {
let b = CompositionToDecodeBox {
version: 0,
flags: 0,
composition_to_dts_shift: 0,
least_decode_to_display_delta: 0,
greatest_decode_to_display_delta: 1024,
composition_start_time: 0,
composition_end_time: 25500,
};
let bytes = b.to_bytes();
assert_eq!(bytes.len(), 8 + 4 + 4 * 5);
let parsed = CompositionToDecodeBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn cslg_round_trip_v1() {
let b = CompositionToDecodeBox {
version: 1,
flags: 0,
composition_to_dts_shift: 0,
least_decode_to_display_delta: -100,
greatest_decode_to_display_delta: 100500,
composition_start_time: 0,
composition_end_time: 9999999999,
};
let bytes = b.to_bytes();
assert_eq!(bytes.len(), 8 + 4 + 8 * 5);
let parsed = CompositionToDecodeBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn elst_round_trip_v0_single() {
let b = EditListBox {
version: 0,
flags: 0,
entries: vec![EditListEntry {
segment_duration: 2000,
media_time: 1024,
media_rate_integer: 1,
media_rate_fraction: 0,
}],
};
let bytes = b.to_bytes();
assert_eq!(bytes.len(), 8 + 4 + 4 + 12); let parsed = EditListBox::parse(&bytes).unwrap();
assert_eq!(parsed.entries.len(), 1);
assert_eq!(parsed.entries[0].segment_duration, 2000);
assert_eq!(parsed.entries[0].media_time, 1024);
assert_eq!(parsed.entries[0].media_rate_integer, 1);
}
#[test]
fn elst_v0_empty_edit() {
let b = EditListBox {
version: 0,
flags: 0,
entries: vec![
EditListEntry {
segment_duration: 500,
media_time: -1,
media_rate_integer: 0,
media_rate_fraction: 0,
},
EditListEntry {
segment_duration: 2000,
media_time: 0,
media_rate_integer: 1,
media_rate_fraction: 0,
},
],
};
let bytes = b.to_bytes();
let parsed = EditListBox::parse(&bytes).unwrap();
assert_eq!(parsed.entries.len(), 2);
assert_eq!(parsed.entries[0].media_time, -1);
assert_eq!(bytes.len(), 40);
}
#[test]
fn elst_round_trip_v1() {
let b = EditListBox {
version: 1,
flags: 0,
entries: vec![EditListEntry {
segment_duration: 0x1_0000_0000,
media_time: 0x2_0000_0000,
media_rate_integer: 1,
media_rate_fraction: 0,
}],
};
let bytes = b.to_bytes();
assert_eq!(bytes.len(), 8 + 4 + 4 + 20); let parsed = EditListBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn elst_parse_body_api() {
let b = EditListBox {
version: 0,
flags: 0,
entries: vec![EditListEntry {
segment_duration: 100,
media_time: 50,
media_rate_integer: 1,
media_rate_fraction: 0,
}],
};
let bytes = b.to_bytes();
let parsed = EditListBox::parse_body(&bytes[8..]).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn sidx_round_trip_v0() {
let b = SegmentIndexBox {
version: 0,
flags: 0,
reference_id: 1,
timescale: 90000,
earliest_presentation_time: 0,
first_offset: 68,
references: vec![
SidxReference {
reference_type: 0,
referenced_size: 1000,
subsegment_duration: 180000,
starts_with_sap: 1,
sap_type: 1,
sap_delta_time: 0,
},
SidxReference {
reference_type: 0,
referenced_size: 1200,
subsegment_duration: 180000,
starts_with_sap: 1,
sap_type: 1,
sap_delta_time: 0,
},
],
};
let bytes = b.to_bytes();
let parsed = SegmentIndexBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn sidx_v1_round_trip() {
let b = SegmentIndexBox {
version: 1,
flags: 0,
reference_id: 3,
timescale: 48000,
earliest_presentation_time: 0x1_0000_0000,
first_offset: 0x2_0000_0000,
references: vec![SidxReference {
reference_type: 1,
referenced_size: 500,
subsegment_duration: 96000,
starts_with_sap: 1,
sap_type: 1,
sap_delta_time: 0,
}],
};
let bytes = b.to_bytes();
let parsed = SegmentIndexBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn sidx_field_bit_boundaries() {
let b = SegmentIndexBox {
version: 0,
flags: 0,
reference_id: 1,
timescale: 90000,
earliest_presentation_time: 0,
first_offset: 0,
references: vec![SidxReference {
reference_type: 1, referenced_size: 0x7FFF_FFFF, subsegment_duration: 0xFFFF_FFFF,
starts_with_sap: 1, sap_type: 7, sap_delta_time: 0x0FFF_FFFF, }],
};
let bytes = b.to_bytes();
let parsed = SegmentIndexBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn sidx_parse_body_api() {
let b = SegmentIndexBox {
version: 0,
flags: 0,
reference_id: 1,
timescale: 90000,
earliest_presentation_time: 0,
first_offset: 0,
references: vec![],
};
let bytes = b.to_bytes();
let parsed = SegmentIndexBox::parse_body(&bytes[8..]).unwrap();
assert_eq!(parsed, b);
}
}