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 PRFT_TYPE: u32 = u32::from_be_bytes(*b"prft");
const SGPD_TYPE: u32 = u32::from_be_bytes(*b"sgpd");
const SBGP_TYPE: u32 = u32::from_be_bytes(*b"sbgp");
const SUBS_TYPE: u32 = u32::from_be_bytes(*b"subs");
pub const GROUPING_TYPE_ROLL: u32 = u32::from_be_bytes(*b"roll");
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ProducerReferenceTimeBox {
pub version: u8,
pub flags: u32,
pub reference_track_id: u32,
pub ntp_timestamp: u64,
pub media_time: u64,
}
impl ProducerReferenceTimeBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
let min = FULLBOX_EXTRA_SIZE + 4 + 8;
if body.len() < min {
return Err(Error::BufferTooShort {
need: min,
have: body.len(),
what: "prft 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_track_id =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
let ntp_timestamp = 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 media_time = if version == 0 {
if body.len() < c + 4 {
return Err(Error::BufferTooShort {
need: c + 4,
have: body.len(),
what: "prft media_time v0",
});
}
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as u64
} else {
if body.len() < c + 8 {
return Err(Error::BufferTooShort {
need: c + 8,
have: body.len(),
what: "prft media_time v1",
});
}
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],
])
};
Ok(Self {
version,
flags,
reference_track_id,
ntp_timestamp,
media_time,
})
}
}
impl<'a> Parse<'a> for ProducerReferenceTimeBox {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + 8 + 4 {
return Err(Error::BufferTooShort {
need: BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + 8 + 4,
have: bytes.len(),
what: "prft box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != PRFT_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected prft",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for ProducerReferenceTimeBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let mt_size = if self.version == 0 { 4 } else { 8 };
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + 8 + mt_size
}
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"prft");
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_track_id.to_be_bytes());
c += 4;
buf[c..c + 8].copy_from_slice(&self.ntp_timestamp.to_be_bytes());
c += 8;
if self.version == 0 {
buf[c..c + 4].copy_from_slice(&(self.media_time as u32).to_be_bytes());
c += 4;
} else {
buf[c..c + 8].copy_from_slice(&self.media_time.to_be_bytes());
c += 8;
}
Ok(c)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum SgpdEntry {
Roll {
roll_distance: i16,
},
Unknown(Vec<u8>),
}
impl SgpdEntry {
pub fn wire_len(&self) -> usize {
match self {
Self::Roll { .. } => 2,
Self::Unknown(v) => v.len(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SampleGroupDescriptionBox {
pub version: u8,
pub flags: u32,
pub grouping_type: u32,
pub default_length: u32,
pub entries: Vec<SgpdEntry>,
}
impl SampleGroupDescriptionBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA_SIZE + 4 + 4 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + 4 + 4,
have: body.len(),
what: "sgpd 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 grouping_type = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
let default_length = if version == 1 {
if body.len() < c + 4 {
return Err(Error::BufferTooShort {
need: c + 4,
have: body.len(),
what: "sgpd default_length",
});
}
let dl = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
dl
} else if version >= 2 {
if body.len() < c + 4 {
return Err(Error::BufferTooShort {
need: c + 4,
have: body.len(),
what: "sgpd default_sample_description_index",
});
}
c += 4; 0
} else {
0 };
if body.len() < c + 4 {
return Err(Error::BufferTooShort {
need: c + 4,
have: body.len(),
what: "sgpd entry_count",
});
}
let entry_count =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as usize;
c += 4;
let mut entries = Vec::with_capacity(entry_count);
for _ in 0..entry_count {
let entry_len: usize = if version == 1 && default_length == 0 {
if body.len() < c + 4 {
return Err(Error::BufferTooShort {
need: c + 4,
have: body.len(),
what: "sgpd description_length",
});
}
let dl =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as usize;
c += 4;
dl
} else if version == 1 {
default_length as usize
} else {
if grouping_type == GROUPING_TYPE_ROLL {
2
} else {
body.len() - c
}
};
if body.len() < c + entry_len {
return Err(Error::BufferTooShort {
need: c + entry_len,
have: body.len(),
what: "sgpd entry body",
});
}
let entry_bytes = &body[c..c + entry_len];
let entry = if grouping_type == GROUPING_TYPE_ROLL && entry_len >= 2 {
let rd = i16::from_be_bytes([entry_bytes[0], entry_bytes[1]]);
SgpdEntry::Roll { roll_distance: rd }
} else {
SgpdEntry::Unknown(entry_bytes.to_vec())
};
entries.push(entry);
c += entry_len;
}
Ok(Self {
version,
flags,
grouping_type,
default_length,
entries,
})
}
}
impl<'a> Parse<'a> for SampleGroupDescriptionBox {
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: "sgpd box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != SGPD_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected sgpd",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for SampleGroupDescriptionBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let (use_default_len, per_entry_prefix) = self.effective_default_length();
let entry_overhead = if per_entry_prefix { 4 } else { 0 };
let entries_size: usize = self
.entries
.iter()
.map(|e| entry_overhead + e.wire_len())
.sum();
let dl_field = if self.version == 1 { 4 } else { 0 };
let _ = use_default_len;
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + dl_field + 4 + entries_size
}
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 (effective_dl, per_entry_prefix) = self.effective_default_length();
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"sgpd");
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.grouping_type.to_be_bytes());
c += 4;
if self.version == 1 {
buf[c..c + 4].copy_from_slice(&effective_dl.to_be_bytes());
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 per_entry_prefix {
buf[c..c + 4].copy_from_slice(&(entry.wire_len() as u32).to_be_bytes());
c += 4;
}
match entry {
SgpdEntry::Roll { roll_distance } => {
buf[c..c + 2].copy_from_slice(&roll_distance.to_be_bytes());
c += 2;
}
SgpdEntry::Unknown(v) => {
buf[c..c + v.len()].copy_from_slice(v);
c += v.len();
}
}
}
Ok(c)
}
}
impl SampleGroupDescriptionBox {
fn effective_default_length(&self) -> (u32, bool) {
if self.version != 1 || self.entries.is_empty() {
return (0, false);
}
let first = self.entries[0].wire_len();
let uniform = self.entries.iter().all(|e| e.wire_len() == first);
if uniform {
(first as u32, false)
} else {
(0, true)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SbgpEntry {
pub sample_count: u32,
pub group_description_index: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SampleToGroupBox {
pub version: u8,
pub flags: u32,
pub grouping_type: u32,
pub grouping_type_parameter: Option<u32>,
pub entries: Vec<SbgpEntry>,
}
impl SampleToGroupBox {
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA_SIZE + 4 + 4 {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA_SIZE + 4 + 4,
have: body.len(),
what: "sbgp 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 grouping_type = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
let grouping_type_parameter = if version == 1 {
if body.len() < c + 4 {
return Err(Error::BufferTooShort {
need: c + 4,
have: body.len(),
what: "sbgp grouping_type_parameter",
});
}
let v = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
Some(v)
} else {
None
};
if body.len() < c + 4 {
return Err(Error::BufferTooShort {
need: c + 4,
have: body.len(),
what: "sbgp entry_count",
});
}
let entry_count =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as usize;
c += 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: "sbgp entry",
});
}
let sample_count = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
let group_description_index =
u32::from_be_bytes([body[c + 4], body[c + 5], body[c + 6], body[c + 7]]);
entries.push(SbgpEntry {
sample_count,
group_description_index,
});
c += 8;
}
Ok(Self {
version,
flags,
grouping_type,
grouping_type_parameter,
entries,
})
}
}
impl<'a> Parse<'a> for SampleToGroupBox {
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: "sbgp box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != SBGP_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected sbgp",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for SampleToGroupBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let gtp_size = if self.version == 1 { 4 } else { 0 };
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + gtp_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"sbgp");
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.grouping_type.to_be_bytes());
c += 4;
if self.version == 1 {
let gtp = self.grouping_type_parameter.unwrap_or(0);
buf[c..c + 4].copy_from_slice(>p.to_be_bytes());
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.group_description_index.to_be_bytes());
c += 8;
}
Ok(c)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SubSampleDescriptor {
pub subsample_size: u32,
pub subsample_priority: u8,
pub discardable: u8,
pub codec_specific_parameters: u32,
}
impl SubSampleDescriptor {
fn wire_len(version: u8) -> usize {
let size_field = if version == 1 { 4 } else { 2 };
size_field + 1 + 1 + 4
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SubsEntry {
pub sample_delta: u32,
pub subsamples: Vec<SubSampleDescriptor>,
}
impl SubsEntry {
fn wire_len(&self, version: u8) -> usize {
4 + 2 + self.subsamples.len() * SubSampleDescriptor::wire_len(version)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SubSampleInformationBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<SubsEntry>,
}
impl SubSampleInformationBox {
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: "subs 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 entry_count =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]) as usize;
c += 4;
let ss_size_field = if version == 1 { 4usize } else { 2usize };
let mut entries = Vec::with_capacity(entry_count);
for _ in 0..entry_count {
if body.len() < c + 4 + 2 {
return Err(Error::BufferTooShort {
need: c + 6,
have: body.len(),
what: "subs entry header",
});
}
let sample_delta = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
let subsample_count = u16::from_be_bytes([body[c], body[c + 1]]) as usize;
c += 2;
let ss_wire = ss_size_field + 1 + 1 + 4;
let mut subsamples = Vec::with_capacity(subsample_count);
for _ in 0..subsample_count {
if body.len() < c + ss_wire {
return Err(Error::BufferTooShort {
need: c + ss_wire,
have: body.len(),
what: "subs subsample",
});
}
let subsample_size = if version == 1 {
let v = u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
v
} else {
let v = u16::from_be_bytes([body[c], body[c + 1]]) as u32;
c += 2;
v
};
let subsample_priority = body[c];
let discardable = body[c + 1];
c += 2;
let codec_specific_parameters =
u32::from_be_bytes([body[c], body[c + 1], body[c + 2], body[c + 3]]);
c += 4;
subsamples.push(SubSampleDescriptor {
subsample_size,
subsample_priority,
discardable,
codec_specific_parameters,
});
}
entries.push(SubsEntry {
sample_delta,
subsamples,
});
}
Ok(Self {
version,
flags,
entries,
})
}
}
impl<'a> Parse<'a> for SubSampleInformationBox {
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: "subs box",
});
}
let ty = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
if ty != SUBS_TYPE {
return Err(Error::InvalidValue {
field: "box_type",
value: ty as u64,
reason: "expected subs",
});
}
Self::parse_body(&bytes[BOX_HEADER_SIZE..])
}
}
impl Serialize for SubSampleInformationBox {
type Error = Error;
fn serialized_len(&self) -> usize {
let entries_size: usize = self.entries.iter().map(|e| e.wire_len(self.version)).sum();
BOX_HEADER_SIZE + FULLBOX_EXTRA_SIZE + 4 + entries_size
}
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"subs");
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_delta.to_be_bytes());
c += 4;
buf[c..c + 2].copy_from_slice(&(entry.subsamples.len() as u16).to_be_bytes());
c += 2;
for ss in &entry.subsamples {
if self.version == 1 {
buf[c..c + 4].copy_from_slice(&ss.subsample_size.to_be_bytes());
c += 4;
} else {
buf[c..c + 2].copy_from_slice(&(ss.subsample_size as u16).to_be_bytes());
c += 2;
}
buf[c] = ss.subsample_priority;
buf[c + 1] = ss.discardable;
c += 2;
buf[c..c + 4].copy_from_slice(&ss.codec_specific_parameters.to_be_bytes());
c += 4;
}
}
Ok(c)
}
}
#[cfg(test)]
mod tests {
use super::*;
use broadcast_common::Serialize;
#[test]
fn prft_round_trip_v0() {
let b = ProducerReferenceTimeBox {
version: 0,
flags: 0,
reference_track_id: 1,
ntp_timestamp: 0x1234_5678_9abc_def0,
media_time: 0x0000_0000_0000_1234,
};
let bytes = b.to_bytes();
assert_eq!(bytes.len(), 8 + 4 + 4 + 8 + 4);
let parsed = ProducerReferenceTimeBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn prft_round_trip_v1() {
let b = ProducerReferenceTimeBox {
version: 1,
flags: 0x000018,
reference_track_id: 1,
ntp_timestamp: 0xedefe3e3_a7ae147a,
media_time: 0x0000_0000_0000_1c20,
};
let bytes = b.to_bytes();
assert_eq!(bytes.len(), 8 + 4 + 4 + 8 + 8);
let parsed = ProducerReferenceTimeBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn sgpd_round_trip_roll_v1() {
let b = SampleGroupDescriptionBox {
version: 1,
flags: 0,
grouping_type: GROUPING_TYPE_ROLL,
default_length: 2,
entries: vec![SgpdEntry::Roll { roll_distance: -1 }],
};
let bytes = b.to_bytes();
let parsed = SampleGroupDescriptionBox::parse(&bytes).unwrap();
assert_eq!(parsed.entries.len(), 1);
assert_eq!(parsed.entries[0], SgpdEntry::Roll { roll_distance: -1 });
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn sgpd_round_trip_two_roll_entries() {
let b = SampleGroupDescriptionBox {
version: 1,
flags: 0,
grouping_type: GROUPING_TYPE_ROLL,
default_length: 2,
entries: vec![
SgpdEntry::Roll { roll_distance: -4 },
SgpdEntry::Roll { roll_distance: -1 },
],
};
let bytes = b.to_bytes();
let parsed = SampleGroupDescriptionBox::parse(&bytes).unwrap();
assert_eq!(parsed.entries.len(), 2);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn sbgp_round_trip_v0() {
let b = SampleToGroupBox {
version: 0,
flags: 0,
grouping_type: GROUPING_TYPE_ROLL,
grouping_type_parameter: None,
entries: vec![
SbgpEntry {
sample_count: 1,
group_description_index: 1,
},
SbgpEntry {
sample_count: 10,
group_description_index: 0,
},
],
};
let bytes = b.to_bytes();
let parsed = SampleToGroupBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn sbgp_round_trip_v1() {
let b = SampleToGroupBox {
version: 1,
flags: 0,
grouping_type: GROUPING_TYPE_ROLL,
grouping_type_parameter: Some(0xDEAD_BEEF),
entries: vec![SbgpEntry {
sample_count: 5,
group_description_index: 1,
}],
};
let bytes = b.to_bytes();
let parsed = SampleToGroupBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
#[test]
fn subs_round_trip_v0() {
let b = SubSampleInformationBox {
version: 0,
flags: 0,
entries: vec![SubsEntry {
sample_delta: 1,
subsamples: vec![
SubSampleDescriptor {
subsample_size: 100,
subsample_priority: 255,
discardable: 0,
codec_specific_parameters: 0,
},
SubSampleDescriptor {
subsample_size: 200,
subsample_priority: 128,
discardable: 1,
codec_specific_parameters: 0,
},
],
}],
};
let bytes = b.to_bytes();
let parsed = SubSampleInformationBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn subs_round_trip_v1() {
let b = SubSampleInformationBox {
version: 1,
flags: 0,
entries: vec![SubsEntry {
sample_delta: 5,
subsamples: vec![SubSampleDescriptor {
subsample_size: 0x0001_2345,
subsample_priority: 200,
discardable: 0,
codec_specific_parameters: 0xABCD_EF01,
}],
}],
};
let bytes = b.to_bytes();
let parsed = SubSampleInformationBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
assert_eq!(parsed.to_bytes(), bytes);
}
#[test]
fn subs_empty_round_trip() {
let b = SubSampleInformationBox {
version: 0,
flags: 0,
entries: vec![],
};
let bytes = b.to_bytes();
let parsed = SubSampleInformationBox::parse(&bytes).unwrap();
assert_eq!(parsed, b);
}
}