use crate::box_types::BoxHeader;
use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::{Parse, Serialize};
use core::fmt;
const BOX_HEADER_SIZE: usize = 8;
const FULLBOX_EXTRA: usize = 4;
const TAG_ES_DESCRIPTOR: u8 = 0x03;
const TAG_DECODER_CONFIG: u8 = 0x04;
const TAG_DECODER_SPECIFIC_INFO: u8 = 0x05;
const TAG_SL_CONFIG: u8 = 0x06;
const MAX_DESCRIPTOR_SIZE: usize = 268_435_455;
const MAX_VARINT_BYTES: usize = 4;
const VARINT_WIDTH_FIXED: usize = 4;
const DECODER_CONFIG_FIXED: usize = 13;
fn read_u24_be(bytes: &[u8], cursor: &mut usize, what: &'static str) -> Result<u32> {
if *cursor + 3 > bytes.len() {
return Err(Error::BufferTooShort {
need: *cursor + 3,
have: bytes.len(),
what,
});
}
let v = u32::from_be_bytes([0, bytes[*cursor], bytes[*cursor + 1], bytes[*cursor + 2]]);
*cursor += 3;
Ok(v)
}
fn read_u32_be(bytes: &[u8], cursor: &mut usize, what: &'static str) -> Result<u32> {
if *cursor + 4 > bytes.len() {
return Err(Error::BufferTooShort {
need: *cursor + 4,
have: bytes.len(),
what,
});
}
let v = u32::from_be_bytes([
bytes[*cursor],
bytes[*cursor + 1],
bytes[*cursor + 2],
bytes[*cursor + 3],
]);
*cursor += 4;
Ok(v)
}
fn parse_varint(bytes: &[u8], cursor: &mut usize) -> Result<(usize, usize)> {
let start = *cursor;
let mut value: usize = 0;
loop {
if *cursor >= bytes.len() {
return Err(Error::BufferTooShort {
need: *cursor + 1,
have: bytes.len(),
what: "descriptor size varint",
});
}
let b = bytes[*cursor];
*cursor += 1;
value = (value << 7) | (b & 0x7F) as usize;
let bytes_so_far = *cursor - start;
if bytes_so_far > MAX_VARINT_BYTES {
return Err(Error::InvalidValue {
field: "descriptor size varint",
value: bytes_so_far as u64,
reason: "varint longer than 4 bytes",
});
}
if (b & 0x80) == 0 {
break;
}
}
if value > MAX_DESCRIPTOR_SIZE {
return Err(Error::InvalidValue {
field: "descriptor size",
value: value as u64,
reason: "exceeds maximum descriptor size (2^28-1)",
});
}
Ok((value, *cursor - start))
}
fn varint_width(value: usize) -> usize {
let mut w = 1;
let mut v = value >> 7;
while v != 0 {
w += 1;
v >>= 7;
}
w
}
fn write_varint_width(
buf: &mut [u8],
cursor: &mut usize,
value: usize,
width: usize,
) -> Result<()> {
if !(1..=MAX_VARINT_BYTES).contains(&width) {
return Err(Error::InvalidValue {
field: "descriptor size varint width",
value: width as u64,
reason: "varint width must be 1..=4 bytes",
});
}
if value > MAX_DESCRIPTOR_SIZE {
return Err(Error::FieldOverflow(broadcast_common::len::FieldOverflow {
field: "descriptor size",
value: value as u64,
max: MAX_DESCRIPTOR_SIZE as u64,
}));
}
if varint_width(value) > width {
return Err(Error::InvalidValue {
field: "descriptor size varint width",
value: width as u64,
reason: "value does not fit the recorded varint width",
});
}
if *cursor + width > buf.len() {
return Err(Error::OutputBufferTooSmall {
need: *cursor + width,
have: buf.len(),
});
}
for i in 0..width {
let shift = 7 * (width - 1 - i);
let mut b = ((value >> shift) & 0x7F) as u8;
if i + 1 < width {
b |= 0x80;
}
buf[*cursor + i] = b;
}
*cursor += width;
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct UnknownDescriptor {
pub tag: u8,
pub size_width: usize,
pub data: Vec<u8>,
pub position: usize,
}
impl UnknownDescriptor {
pub fn serialized_len(&self) -> usize {
1 + self.size_width + self.data.len()
}
pub 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;
buf[cursor] = self.tag;
cursor += 1;
write_varint_width(buf, &mut cursor, self.data.len(), self.size_width)?;
buf[cursor..cursor + self.data.len()].copy_from_slice(&self.data);
cursor += self.data.len();
Ok(cursor)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ObjectTypeIndication(pub u8);
impl ObjectTypeIndication {
pub fn name(&self) -> &str {
match self.0 {
0x20 => "MPEG-4 Visual",
0x21 => "AVC / H.264",
0x22 => "AVC parameter sets",
0x40 => "MPEG-4 Audio / AAC",
0x60 => "MPEG-2 Video Simple",
0x61 => "MPEG-2 Video Main",
0x62 => "MPEG-2 Video SNR",
0x63 => "MPEG-2 Video Spatial",
0x64 => "MPEG-2 Video High",
0x65 => "MPEG-2 Video 422",
0x66 => "MPEG-2 AAC LC",
0x67 => "MPEG-2 AAC Main",
0x68 => "MPEG-2 AAC SSR",
0x69 => "MPEG-2 Audio (13818-3)",
0x6A => "MPEG-1 Visual (11172-2)",
0x6B => "MPEG-1 Audio (11172-3)",
0x6C => "JPEG",
0x6E => "JPEG 2000",
0xFF => "no object type",
_ => "user-private",
}
}
}
impl fmt::Display for ObjectTypeIndication {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} (0x{:02X})", self.name(), self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct StreamType(pub u8);
impl StreamType {
pub fn name(&self) -> &str {
match self.0 {
0x01 => "ObjectDescriptorStream",
0x02 => "ClockReferenceStream",
0x03 => "SceneDescriptionStream",
0x04 => "VisualStream",
0x05 => "AudioStream",
0x06 => "MPEG7Stream",
0x07 => "IPMPStream",
0x08 => "ObjectContentInfoStream",
0x09 => "MPEGJStream",
0x0A..=0x1F => "reserved",
0x20..=0x3F => "user-private",
_ => "forbidden",
}
}
}
impl fmt::Display for StreamType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} (0x{:02X})", self.name(), self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DecoderSpecificInfo {
pub data: Vec<u8>,
pub size_width: usize,
}
impl DecoderSpecificInfo {
const TAG: u8 = TAG_DECODER_SPECIFIC_INFO;
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
size_width: VARINT_WIDTH_FIXED,
}
}
}
impl<'a> Parse<'a> for DecoderSpecificInfo {
type Error = Error;
fn parse(body: &'a [u8]) -> Result<Self> {
Ok(Self {
data: body.to_vec(),
size_width: VARINT_WIDTH_FIXED,
})
}
}
impl Serialize for DecoderSpecificInfo {
type Error = Error;
fn serialized_len(&self) -> usize {
1 + self.size_width + 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 mut cursor = 0usize;
buf[cursor] = Self::TAG;
cursor += 1;
write_varint_width(buf, &mut cursor, self.data.len(), self.size_width)?;
buf[cursor..cursor + self.data.len()].copy_from_slice(&self.data);
cursor += self.data.len();
Ok(cursor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SLConfigDescriptor {
pub body: Vec<u8>,
pub size_width: usize,
}
impl SLConfigDescriptor {
const TAG: u8 = TAG_SL_CONFIG;
pub fn new(body: Vec<u8>) -> Self {
Self {
body,
size_width: VARINT_WIDTH_FIXED,
}
}
pub fn predefined_two() -> Self {
Self::new(alloc::vec![2])
}
}
impl<'a> Parse<'a> for SLConfigDescriptor {
type Error = Error;
fn parse(body: &'a [u8]) -> Result<Self> {
Ok(Self {
body: body.to_vec(),
size_width: VARINT_WIDTH_FIXED,
})
}
}
impl Serialize for SLConfigDescriptor {
type Error = Error;
fn serialized_len(&self) -> usize {
1 + self.size_width + self.body.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 mut cursor = 0usize;
buf[cursor] = Self::TAG;
cursor += 1;
write_varint_width(buf, &mut cursor, self.body.len(), self.size_width)?;
buf[cursor..cursor + self.body.len()].copy_from_slice(&self.body);
cursor += self.body.len();
Ok(cursor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DecoderConfigDescriptor {
pub object_type_indication: ObjectTypeIndication,
pub stream_type: StreamType,
pub up_stream: bool,
pub buffer_size_db: u32,
pub max_bitrate: u32,
pub avg_bitrate: u32,
pub decoder_specific_info: Option<DecoderSpecificInfo>,
pub unknown_descriptors: Vec<UnknownDescriptor>,
pub size_width: usize,
}
impl DecoderConfigDescriptor {
const TAG: u8 = TAG_DECODER_CONFIG;
pub fn new(
object_type_indication: u8,
stream_type: u8,
up_stream: bool,
buffer_size_db: u32,
max_bitrate: u32,
avg_bitrate: u32,
decoder_specific_info: Option<DecoderSpecificInfo>,
) -> Self {
Self {
object_type_indication: ObjectTypeIndication(object_type_indication),
stream_type: StreamType(stream_type),
up_stream,
buffer_size_db,
max_bitrate,
avg_bitrate,
decoder_specific_info,
unknown_descriptors: Vec::new(),
size_width: VARINT_WIDTH_FIXED,
}
}
}
impl<'a> Parse<'a> for DecoderConfigDescriptor {
type Error = Error;
fn parse(body: &'a [u8]) -> Result<Self> {
let mut cursor = 0usize;
if cursor >= body.len() {
return Err(Error::BufferTooShort {
need: 1,
have: body.len(),
what: "objectTypeIndication",
});
}
let oti = ObjectTypeIndication(body[cursor]);
cursor += 1;
if cursor >= body.len() {
return Err(Error::BufferTooShort {
need: cursor + 1,
have: body.len(),
what: "streamType/upStream",
});
}
let st_byte = body[cursor];
let stream_type_val = (st_byte >> 2) & 0x3F;
let up_stream = ((st_byte >> 1) & 0x01) != 0;
let _reserved = (st_byte & 0x01) != 0;
cursor += 1;
let buffer_size_db = read_u24_be(body, &mut cursor, "bufferSizeDB")?;
let max_bitrate = read_u32_be(body, &mut cursor, "maxBitrate")?;
let avg_bitrate = read_u32_be(body, &mut cursor, "avgBitrate")?;
let mut decoder_specific_info = None;
let mut unknown_descriptors = Vec::new();
while cursor < body.len() {
let sub_tag = body[cursor];
cursor += 1;
let (sub_size, sub_width) = parse_varint(body, &mut cursor)?;
let sub_body = if cursor + sub_size <= body.len() {
&body[cursor..cursor + sub_size]
} else {
return Err(Error::BufferTooShort {
need: cursor + sub_size,
have: body.len(),
what: "DecoderConfigDescriptor sub_descriptor body",
});
};
match sub_tag {
TAG_DECODER_SPECIFIC_INFO => {
let mut dsi = DecoderSpecificInfo::parse(sub_body)?;
dsi.size_width = sub_width;
decoder_specific_info = Some(dsi);
}
_ => {
unknown_descriptors.push(UnknownDescriptor {
tag: sub_tag,
size_width: sub_width,
data: sub_body.to_vec(),
position: usize::from(decoder_specific_info.is_some()),
});
}
}
cursor += sub_size;
}
Ok(Self {
object_type_indication: oti,
stream_type: StreamType(stream_type_val),
up_stream,
buffer_size_db,
max_bitrate,
avg_bitrate,
decoder_specific_info,
unknown_descriptors,
size_width: VARINT_WIDTH_FIXED,
})
}
}
impl Serialize for DecoderConfigDescriptor {
type Error = Error;
fn serialized_len(&self) -> usize {
1 + self.size_width + DECODER_CONFIG_FIXED
+ self.decoder_specific_info.as_ref().map_or(0, |dsi| dsi.serialized_len())
+ self
.unknown_descriptors
.iter()
.map(UnknownDescriptor::serialized_len)
.sum::<usize>()
}
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;
buf[cursor] = Self::TAG;
cursor += 1;
let body_size = DECODER_CONFIG_FIXED
+ self
.decoder_specific_info
.as_ref()
.map_or(0, |dsi| dsi.serialized_len())
+ self
.unknown_descriptors
.iter()
.map(UnknownDescriptor::serialized_len)
.sum::<usize>();
write_varint_width(buf, &mut cursor, body_size, self.size_width)?;
buf[cursor] = self.object_type_indication.0;
cursor += 1;
buf[cursor] = ((self.stream_type.0 & 0x3F) << 2) | ((self.up_stream as u8) << 1) | 0x01;
cursor += 1;
broadcast_common::len::fit_bits(u64::from(self.buffer_size_db), 24, "bufferSizeDB")?;
buf[cursor..cursor + 3].copy_from_slice(&[
(self.buffer_size_db >> 16) as u8,
(self.buffer_size_db >> 8) as u8,
self.buffer_size_db as u8,
]);
cursor += 3;
buf[cursor..cursor + 4].copy_from_slice(&self.max_bitrate.to_be_bytes());
cursor += 4;
buf[cursor..cursor + 4].copy_from_slice(&self.avg_bitrate.to_be_bytes());
cursor += 4;
for ud in &self.unknown_descriptors {
if ud.position == 0 {
cursor += ud.serialize_into(&mut buf[cursor..])?;
}
}
if let Some(ref dsi) = self.decoder_specific_info {
cursor += dsi.serialize_into(&mut buf[cursor..])?;
}
for ud in &self.unknown_descriptors {
if ud.position > 0 {
cursor += ud.serialize_into(&mut buf[cursor..])?;
}
}
Ok(cursor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ESDescriptor {
pub es_id: u16,
pub stream_dependence_flag: bool,
pub url_flag: bool,
pub ocr_stream_flag: bool,
pub stream_priority: u8,
pub depends_on_es_id: Option<u16>,
pub url: Option<alloc::string::String>,
pub ocr_es_id: Option<u16>,
pub decoder_config: Option<DecoderConfigDescriptor>,
pub sl_config: Option<SLConfigDescriptor>,
pub unknown_descriptors: Vec<UnknownDescriptor>,
pub size_width: usize,
}
impl ESDescriptor {
const TAG: u8 = TAG_ES_DESCRIPTOR;
pub fn new(
es_id: u16,
stream_priority: u8,
decoder_config: Option<DecoderConfigDescriptor>,
sl_config: Option<SLConfigDescriptor>,
) -> Self {
Self {
es_id,
stream_dependence_flag: false,
url_flag: false,
ocr_stream_flag: false,
stream_priority,
depends_on_es_id: None,
url: None,
ocr_es_id: None,
decoder_config,
sl_config,
unknown_descriptors: Vec::new(),
size_width: VARINT_WIDTH_FIXED,
}
}
}
impl<'a> Parse<'a> for ESDescriptor {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let mut tag_cursor = 0usize;
if tag_cursor >= bytes.len() {
return Err(Error::BufferTooShort {
need: 1,
have: bytes.len(),
what: "ES_Descriptor tag",
});
}
let tag = bytes[tag_cursor];
tag_cursor += 1;
if tag != Self::TAG {
return Err(Error::InvalidValue {
field: "descriptor_tag",
value: tag as u64,
reason: "expected ES_DescrTag (0x03)",
});
}
let (size, size_width) = parse_varint(bytes, &mut tag_cursor)?;
let end = tag_cursor
.checked_add(size)
.filter(|&end| end <= bytes.len())
.ok_or(Error::BufferTooShort {
need: tag_cursor.saturating_add(size),
have: bytes.len(),
what: "ES_Descriptor body",
})?;
let body = &bytes[tag_cursor..end];
let mut cursor = 0usize;
let mut unknown_descriptors = Vec::new();
if cursor + 2 > body.len() {
return Err(Error::BufferTooShort {
need: cursor + 2,
have: body.len(),
what: "ES_ID",
});
}
let es_id = u16::from_be_bytes([body[cursor], body[cursor + 1]]);
cursor += 2;
if cursor >= body.len() {
return Err(Error::BufferTooShort {
need: cursor + 1,
have: body.len(),
what: "ES flags byte",
});
}
let flags = body[cursor];
cursor += 1;
let stream_dependence_flag = (flags & 0x80) != 0;
let url_flag = (flags & 0x40) != 0;
let ocr_stream_flag = (flags & 0x20) != 0;
let stream_priority = flags & 0x1F;
let depends_on_es_id = if stream_dependence_flag {
if cursor + 2 > body.len() {
return Err(Error::BufferTooShort {
need: cursor + 2,
have: body.len(),
what: "dependsOn_ES_ID",
});
}
let v = u16::from_be_bytes([body[cursor], body[cursor + 1]]);
cursor += 2;
Some(v)
} else {
None
};
let url = if url_flag {
if cursor >= body.len() {
return Err(Error::BufferTooShort {
need: cursor + 1,
have: body.len(),
what: "URLLength",
});
}
let url_len = body[cursor] as usize;
cursor += 1;
if cursor + url_len > body.len() {
return Err(Error::BufferTooShort {
need: cursor + url_len,
have: body.len(),
what: "URLstring",
});
}
let u = core::str::from_utf8(&body[cursor..cursor + url_len]).map_err(|eve| {
Error::InvalidValue {
field: "URLstring",
value: body[cursor + eve.valid_up_to()] as u64,
reason: "URLstring must be UTF-8 (ISO/IEC 14496-1 §7.2.6.5)",
}
})?;
cursor += url_len;
Some(alloc::string::String::from(u))
} else {
None
};
let ocr_es_id = if ocr_stream_flag {
if cursor + 2 > body.len() {
return Err(Error::BufferTooShort {
need: cursor + 2,
have: body.len(),
what: "OCR_ES_Id",
});
}
let v = u16::from_be_bytes([body[cursor], body[cursor + 1]]);
cursor += 2;
Some(v)
} else {
None
};
let mut decoder_config = None;
let mut sl_config = None;
while cursor < body.len() {
if cursor >= body.len() {
break;
}
let sub_tag = body[cursor];
cursor += 1;
let (sub_size, sub_width) = parse_varint(body, &mut cursor)?;
if cursor + sub_size > body.len() {
return Err(Error::BufferTooShort {
need: cursor + sub_size,
have: body.len(),
what: "ES_Descriptor sub-descriptor body",
});
}
let sub_body = &body[cursor..cursor + sub_size];
match sub_tag {
TAG_DECODER_CONFIG => {
let mut dc = DecoderConfigDescriptor::parse(sub_body)?;
dc.size_width = sub_width;
decoder_config = Some(dc);
}
TAG_SL_CONFIG => {
let mut sl = SLConfigDescriptor::parse(sub_body)?;
sl.size_width = sub_width;
sl_config = Some(sl);
}
_ => {
let position =
usize::from(decoder_config.is_some()) + usize::from(sl_config.is_some());
unknown_descriptors.push(UnknownDescriptor {
tag: sub_tag,
size_width: sub_width,
data: sub_body.to_vec(),
position,
});
}
}
cursor += sub_size;
}
Ok(Self {
es_id,
stream_dependence_flag,
url_flag,
ocr_stream_flag,
stream_priority,
depends_on_es_id,
url,
ocr_es_id,
decoder_config,
sl_config,
unknown_descriptors,
size_width,
})
}
}
impl Serialize for ESDescriptor {
type Error = Error;
fn serialized_len(&self) -> usize {
let body_size = 2 + 1 + if self.depends_on_es_id.is_some() { 2 } else { 0 }
+ self.url.as_ref().map_or(0, |u| 1 + u.len())
+ if self.ocr_es_id.is_some() { 2 } else { 0 };
1 + self.size_width
+ body_size
+ self.decoder_config.as_ref().map_or(0, |dc| dc.serialized_len())
+ self.sl_config.as_ref().map_or(0, |sl| sl.serialized_len())
+ self
.unknown_descriptors
.iter()
.map(UnknownDescriptor::serialized_len)
.sum::<usize>()
}
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;
buf[cursor] = Self::TAG;
cursor += 1;
if self.stream_dependence_flag != self.depends_on_es_id.is_some() {
return Err(Error::InvalidValue {
field: "ES_Descriptor.streamDependenceFlag",
value: self.stream_dependence_flag as u64,
reason: "streamDependenceFlag disagrees with depends_on_es_id",
});
}
if self.url_flag != self.url.is_some() {
return Err(Error::InvalidValue {
field: "ES_Descriptor.URL_Flag",
value: self.url_flag as u64,
reason: "URL_Flag disagrees with url",
});
}
if self.ocr_stream_flag != self.ocr_es_id.is_some() {
return Err(Error::InvalidValue {
field: "ES_Descriptor.OCRstreamFlag",
value: self.ocr_stream_flag as u64,
reason: "OCRstreamFlag disagrees with ocr_es_id",
});
}
let mut body_size = 2 + 1; if self.depends_on_es_id.is_some() {
body_size += 2;
}
if let Some(u) = &self.url {
body_size += 1 + u.len();
}
if self.ocr_es_id.is_some() {
body_size += 2;
}
body_size += self
.decoder_config
.as_ref()
.map_or(0, |dc| dc.serialized_len())
+ self.sl_config.as_ref().map_or(0, |sl| sl.serialized_len())
+ self
.unknown_descriptors
.iter()
.map(UnknownDescriptor::serialized_len)
.sum::<usize>();
write_varint_width(buf, &mut cursor, body_size, self.size_width)?;
buf[cursor..cursor + 2].copy_from_slice(&self.es_id.to_be_bytes());
cursor += 2;
let mut flags = self.stream_priority & 0x1F;
if self.depends_on_es_id.is_some() {
flags |= 0x80;
}
if self.url.is_some() {
flags |= 0x40;
}
if self.ocr_es_id.is_some() {
flags |= 0x20;
}
buf[cursor] = flags;
cursor += 1;
if let Some(dep) = self.depends_on_es_id {
buf[cursor..cursor + 2].copy_from_slice(&dep.to_be_bytes());
cursor += 2;
}
if let Some(u) = &self.url {
buf[cursor] = broadcast_common::len::fit_u8(u.len(), "URLstring length")?;
cursor += 1;
buf[cursor..cursor + u.len()].copy_from_slice(u.as_bytes());
cursor += u.len();
}
if let Some(ocr) = self.ocr_es_id {
buf[cursor..cursor + 2].copy_from_slice(&ocr.to_be_bytes());
cursor += 2;
}
for slot in 0..=1usize {
for ud in &self.unknown_descriptors {
if ud.position == slot {
cursor += ud.serialize_into(&mut buf[cursor..])?;
}
}
match slot {
0 => {
if let Some(ref d) = self.decoder_config {
cursor += d.serialize_into(&mut buf[cursor..])?;
}
}
_ => {
if let Some(ref d) = self.sl_config {
cursor += d.serialize_into(&mut buf[cursor..])?;
}
}
}
}
for ud in &self.unknown_descriptors {
if ud.position > 1 {
cursor += ud.serialize_into(&mut buf[cursor..])?;
}
}
Ok(cursor)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EsdsBox {
pub es_descriptor: ESDescriptor,
}
impl EsdsBox {
pub(crate) fn decoder_specific_info_data(&self) -> Option<&[u8]> {
self.es_descriptor
.decoder_config
.as_ref()
.and_then(|dc| dc.decoder_specific_info.as_ref())
.map(|dsi| dsi.data.as_slice())
}
pub(crate) fn require_decoder_specific_info(&self) -> Result<&[u8]> {
self.decoder_specific_info_data()
.ok_or(Error::UnexpectedBox {
expected: "DecoderSpecificInfo (AudioSpecificConfig) in esds",
})
}
pub(crate) fn audio_specific_config(&self) -> Result<crate::aac_asc::AudioSpecificConfig> {
crate::aac_asc::AudioSpecificConfig::parse(self.require_decoder_specific_info()?)
}
pub fn parse_box(data: &[u8]) -> Result<Self> {
let header = BoxHeader::parse(data)?;
if !header.box_type.is(b"esds") {
return Err(Error::InvalidValue {
field: "box_type",
value: header.box_type.to_u32() as u64,
reason: "expected 'esds'",
});
}
let end = match header.size {
0 => data.len(),
size => usize::try_from(size).unwrap_or(usize::MAX),
};
let body = data
.get(header.header_size()..end)
.ok_or(Error::BufferTooShort {
need: end,
have: data.len(),
what: "esds box body",
})?;
Self::parse_body(body)
}
pub fn parse_body(body: &[u8]) -> Result<Self> {
if body.len() < FULLBOX_EXTRA {
return Err(Error::BufferTooShort {
need: FULLBOX_EXTRA,
have: body.len(),
what: "esds FullBox header",
});
}
let payload = &body[FULLBOX_EXTRA..];
let es_descriptor = ESDescriptor::parse(payload)?;
Ok(Self { es_descriptor })
}
pub fn new(es_descriptor: ESDescriptor) -> Self {
Self { es_descriptor }
}
}
impl Serialize for EsdsBox {
type Error = Error;
fn serialized_len(&self) -> usize {
crate::box_types::box_len(
BOX_HEADER_SIZE + FULLBOX_EXTRA + self.es_descriptor.serialized_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 mut cursor = 0usize;
cursor += crate::box_types::write_box_header(buf, b"esds", need)?;
buf[cursor..cursor + 4].copy_from_slice(&[0, 0, 0, 0]);
cursor += 4;
cursor += self.es_descriptor.serialize_into(&mut buf[cursor..])?;
Ok(cursor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn buffer_size_db_over_24_bits_is_rejected() {
let mut d = DecoderConfigDescriptor::new(0x40, 5, false, 0x0100_0000, 0, 0, None);
let mut buf = vec![0u8; d.serialized_len()];
assert!(d.serialize_into(&mut buf).is_err());
d.buffer_size_db = 0x00FF_FFFF;
assert!(d.serialize_into(&mut buf).is_ok());
assert_eq!(&buf[7..10], &[0xFF, 0xFF, 0xFF][..], "full 24 bits kept");
}
#[test]
fn test_parse_varint_accepts_1_to_4_byte_forms() {
let cases: &[(&[u8], usize, usize)] = &[
(&[0x00], 0, 1),
(&[0x7F], 0x7F, 1),
(&[0x81, 0x00], 0x80, 2),
(&[0xFF, 0x7F], 0x3FFF, 2),
(&[0x81, 0x80, 0x00], 0x4000, 3),
(&[0x80, 0x80, 0x80, 0x25], 0x25, 4),
(&[0x80, 0x80, 0x80, 0x01], 0x01, 4),
(&[0xFF, 0xFF, 0xFF, 0x7F], 0x0FFF_FFFF, 4),
];
for &(bytes, val, consumed) in cases {
let mut c = 0usize;
let (decoded, n) = parse_varint(bytes, &mut c).unwrap();
assert_eq!(decoded, val, "parse {bytes:02x?}");
assert_eq!(c, consumed, "cursor for {bytes:02x?}");
assert_eq!(n, consumed, "consumed for {bytes:02x?}");
}
}
#[test]
fn test_write_varint_width_is_4_byte_expanded_and_round_trips() {
for &val in &[0usize, 1, 0x25, 0x17, 5, 0x4000, 0x0FFF_FFFF] {
let mut buf = [0u8; 4];
let mut c = 0usize;
write_varint_width(&mut buf, &mut c, val, 4).unwrap();
assert_eq!(c, 4, "width-4 varint is always 4 bytes for {val}");
assert_eq!(buf[0] & 0x80, 0x80);
assert_eq!(buf[3] & 0x80, 0x00);
let mut rc = 0usize;
let (decoded, _) = parse_varint(&buf, &mut rc).unwrap();
assert_eq!(decoded, val, "round-trip {val}");
}
}
#[test]
fn test_write_varint_width_preserves_minimal_width() {
for &(val, width) in &[
(0usize, 1usize),
(0x25, 1),
(0x80, 2),
(0x3FFF, 2),
(0x4000, 3),
] {
let mut buf = [0u8; 4];
let mut c = 0usize;
write_varint_width(&mut buf, &mut c, val, width).unwrap();
assert_eq!(c, width, "value {val} written in {width} byte(s)");
let mut rc = 0usize;
let (decoded, used) = parse_varint(&buf[..c], &mut rc).unwrap();
assert_eq!(decoded, val);
assert_eq!(used, width, "parsed width matches written width");
}
let mut buf = [0u8; 4];
let mut c = 0usize;
let err = write_varint_width(&mut buf, &mut c, 0x80, 1).unwrap_err();
assert!(
matches!(
err,
Error::InvalidValue {
field: "descriptor size varint width",
..
}
),
"{err:?}"
);
let err = write_varint_width(&mut buf, &mut c, 0, 0).unwrap_err();
assert!(matches!(err, Error::InvalidValue { .. }), "{err:?}");
let err = write_varint_width(&mut buf, &mut c, MAX_DESCRIPTOR_SIZE + 1, 4).unwrap_err();
assert!(matches!(err, Error::FieldOverflow(_)), "{err:?}");
}
#[rustfmt::skip]
const REAL_ESDS_BOX_AAC: &[u8] = &[
0x00, 0x00, 0x00, 0x33, 0x65, 0x73, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x80, 0x80, 0x22, 0x00, 0x01, 0x00, 0x04, 0x80, 0x80, 0x80, 0x14, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x7d, 0x00, 0x01, 0x77, 0x0d, 0x05, 0x80, 0x80, 0x80, 0x02, 0x12, 0x08, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02, ];
#[test]
fn real_esds_box_round_trips_byte_exact() {
let esds = EsdsBox::parse_box(REAL_ESDS_BOX_AAC).expect("parse real esds");
assert_eq!(esds.es_descriptor.es_id, 1);
let dc = esds
.es_descriptor
.decoder_config
.as_ref()
.expect("decoder config");
assert_eq!(dc.object_type_indication.0, 0x40, "AAC OTI");
assert_eq!(dc.stream_type.0, 5, "AudioStream");
assert_eq!(dc.max_bitrate, 0x0001_807d);
assert_eq!(dc.avg_bitrate, 0x0001_770d);
assert_eq!(
dc.decoder_specific_info.as_ref().expect("dsi").data,
&[0x12, 0x08],
"AudioSpecificConfig"
);
let mut buf = vec![0u8; esds.serialized_len()];
let n = esds.serialize_into(&mut buf).expect("serialize");
assert_eq!(&buf[..n], REAL_ESDS_BOX_AAC, "real esds must round-trip");
}
#[test]
fn minimal_width_esds_round_trips_byte_exact() {
#[rustfmt::skip]
let minimal: &[u8] = &[
0x00, 0x00, 0x00, 0x27, 0x65, 0x73, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x03, 0x19, 0x00, 0x01, 0x00, 0x04, 0x11, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x7d, 0x00, 0x01, 0x77, 0x0d, 0x05, 0x02, 0x12, 0x08, 0x06, 0x01, 0x02, ];
let esds = EsdsBox::parse_box(minimal).expect("parse minimal-width esds");
assert_eq!(esds.serialized_len(), minimal.len());
let mut out = vec![0u8; esds.serialized_len()];
let n = esds.serialize_into(&mut out).expect("serialize");
assert_eq!(n, minimal.len());
assert_eq!(
&out[..n],
minimal,
"minimal-width esds must round-trip exactly"
);
let dc = esds.es_descriptor.decoder_config.as_ref().unwrap();
assert_eq!(esds.es_descriptor.size_width, 1);
assert_eq!(dc.size_width, 1);
assert_eq!(dc.decoder_specific_info.as_ref().unwrap().size_width, 1);
assert_eq!(esds.es_descriptor.sl_config.as_ref().unwrap().size_width, 1);
}
#[test]
fn unknown_sub_descriptors_are_preserved() {
const TAG_PROFILE_LEVEL_INDEX: u8 = 0x14;
#[rustfmt::skip]
let bytes: &[u8] = &[
0x00, 0x00, 0x00, 0x37, 0x65, 0x73, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x80, 0x80, 0x26, 0x00, 0x01, 0x00, 0x04, 0x80, 0x80, 0x80, 0x18, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x7d, 0x00, 0x01, 0x77, 0x0d, 0x05, 0x80, 0x80, 0x80, 0x02, 0x12, 0x08, 0x14, 0x02, 0x01, 0x02, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02, ];
let esds = EsdsBox::parse_box(bytes).expect("parse esds with unknown descriptor");
let dc = esds.es_descriptor.decoder_config.as_ref().unwrap();
assert_eq!(dc.unknown_descriptors.len(), 1);
assert_eq!(dc.unknown_descriptors[0].tag, TAG_PROFILE_LEVEL_INDEX);
assert_eq!(dc.unknown_descriptors[0].data, vec![0x01, 0x02]);
assert_eq!(esds.serialized_len(), bytes.len());
let mut out = vec![0u8; esds.serialized_len()];
esds.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes, "unknown descriptor must survive the round trip");
}
#[test]
fn unknown_sub_descriptors_keep_their_position() {
const TAG_LANGUAGE: u8 = 0x0E;
#[rustfmt::skip]
let bytes: &[u8] = &[
0x00, 0x00, 0x00, 0x3B, 0x65, 0x73, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x80, 0x80, 0x2A, 0x00, 0x01, 0x00, 0x04, 0x80, 0x80, 0x80, 0x18, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x7d, 0x00, 0x01, 0x77, 0x0d, 0x05, 0x80, 0x80, 0x80, 0x02, 0x12, 0x08, 0x14, 0x02, 0x01, 0x02,
0x0E, 0x02, 0x65, 0x6E, 0x06, 0x80, 0x80, 0x80, 0x01, 0x02, ];
let esds = EsdsBox::parse_box(bytes).expect("parse interleaved esds");
let es = &esds.es_descriptor;
assert_eq!(es.unknown_descriptors.len(), 1);
assert_eq!(es.unknown_descriptors[0].tag, TAG_LANGUAGE);
assert_eq!(es.unknown_descriptors[0].position, 1, "after dc, before sl");
assert_eq!(
es.decoder_config.as_ref().unwrap().unknown_descriptors[0].position,
1
);
assert_eq!(esds.serialized_len(), bytes.len());
let mut out = vec![0u8; esds.serialized_len()];
esds.serialize_into(&mut out).unwrap();
assert_eq!(
out, bytes,
"an interleaved unknown descriptor must stay between dc and sl"
);
}
#[test]
fn flag_and_field_disagreement_errors() {
let base = ESDescriptor::new(
1,
0,
Some(DecoderConfigDescriptor::new(0x40, 5, false, 0, 0, 0, None)),
Some(SLConfigDescriptor::predefined_two()),
);
let mut sdf = base.clone();
sdf.stream_dependence_flag = true;
let err = EsdsBox::new(sdf).try_to_bytes().unwrap_err();
assert!(
matches!(
err,
Error::InvalidValue {
field: "ES_Descriptor.streamDependenceFlag",
..
}
),
"got {err:?}"
);
let mut url = base.clone();
url.url_flag = true;
let err = EsdsBox::new(url).try_to_bytes().unwrap_err();
assert!(
matches!(
err,
Error::InvalidValue {
field: "ES_Descriptor.URL_Flag",
..
}
),
"got {err:?}"
);
let mut ocr = base.clone();
ocr.ocr_stream_flag = true;
let err = EsdsBox::new(ocr).try_to_bytes().unwrap_err();
assert!(
matches!(
err,
Error::InvalidValue {
field: "ES_Descriptor.OCRstreamFlag",
..
}
),
"got {err:?}"
);
let mut reverse = base.clone();
reverse.depends_on_es_id = Some(7);
let err = EsdsBox::new(reverse).try_to_bytes().unwrap_err();
assert!(matches!(err, Error::InvalidValue { .. }), "got {err:?}");
let mut ok = base.clone();
ok.url_flag = true;
ok.url = Some(alloc::string::String::from("x"));
let bytes = EsdsBox::new(ok).try_to_bytes().unwrap();
assert_eq!(
EsdsBox::parse_box(&bytes)
.unwrap()
.es_descriptor
.url
.as_deref(),
Some("x")
);
}
#[test]
fn test_skip_unknown_descriptor() {
let bytes = [0x07, 0x04, 1, 2, 3, 4, TAG_SL_CONFIG, 0x01, 0x02];
let mut cursor = 0usize;
let tag1 = bytes[cursor];
cursor += 1;
let (size1, _) = parse_varint(&bytes, &mut cursor).unwrap();
assert_eq!(tag1, 0x07);
assert_eq!(size1, 4);
cursor += size1;
let tag2 = bytes[cursor];
cursor += 1;
assert_eq!(tag2, TAG_SL_CONFIG);
let (size2, _) = parse_varint(&bytes, &mut cursor).unwrap();
assert_eq!(size2, 1);
let body2 = &bytes[cursor..cursor + size2];
assert_eq!(body2, &[0x02]);
}
#[test]
fn test_esds_mutation_changes_bytes() {
let es = EsdsBox::new(ESDescriptor::new(
2,
0,
Some(DecoderConfigDescriptor::new(
0x40,
5,
false,
0,
24576000,
24576005,
Some(DecoderSpecificInfo::new(vec![0x12, 0x08, 0x56, 0xe5, 0x00])),
)),
Some(SLConfigDescriptor::predefined_two()),
));
let original = es.try_to_bytes().unwrap();
let mut es2 = es.clone();
let dc = es2.es_descriptor.decoder_config.as_mut().unwrap();
dc.object_type_indication = ObjectTypeIndication(0x21); let mutated = es2.try_to_bytes().unwrap();
assert_ne!(mutated, original, "mutating OTI must change bytes");
}
fn es_descriptor_with_url(url: alloc::string::String) -> ESDescriptor {
ESDescriptor {
url_flag: true,
url: Some(url),
..ESDescriptor::new(
2,
0,
Some(DecoderConfigDescriptor::new(
0x40,
5,
false,
0,
24576000,
24576005,
Some(DecoderSpecificInfo::new(vec![0x12, 0x08, 0x56, 0xe5, 0x00])),
)),
Some(SLConfigDescriptor::predefined_two()),
)
}
}
#[test]
fn oversized_url_length_errors() {
let es = es_descriptor_with_url("x".repeat(256));
let err = es.try_to_bytes().unwrap_err();
assert!(
matches!(
err,
Error::FieldOverflow(broadcast_common::len::FieldOverflow {
field: "URLstring length",
..
})
),
"expected FieldOverflow for URLstring length, got {err:?}"
);
}
#[test]
fn max_url_length_round_trips() {
let url = "x".repeat(255);
let es = es_descriptor_with_url(url.clone());
let bytes = es.try_to_bytes().unwrap();
let url_bytes = url.as_bytes();
let pos = bytes
.windows(url_bytes.len())
.position(|w| w == url_bytes)
.expect("URL bytes present in output");
assert_eq!(bytes[pos - 1], 255, "URLlength byte must be exactly 255");
}
#[test]
fn es_descriptor_url_lengths_round_trip() {
for &len in &[0usize, 1, 100, 127, 128, 200, 255] {
let url: alloc::string::String = "x".repeat(len);
let es = es_descriptor_with_url(url.clone());
let bytes = EsdsBox::new(es)
.try_to_bytes()
.unwrap_or_else(|e| panic!("serialize URL length {len} failed: {e:?}"));
let parsed = EsdsBox::parse_box(&bytes)
.unwrap_or_else(|e| panic!("parse URL length {len} failed: {e:?}"));
assert_eq!(
parsed.es_descriptor.url.as_deref(),
Some(url.as_str()),
"URL length {len} did not survive parse"
);
let mut out = vec![0u8; parsed.serialized_len()];
let n = parsed.serialize_into(&mut out).unwrap();
assert_eq!(&out[..n], &bytes[..], "URL length {len} not byte-identical");
assert_eq!(parsed, EsdsBox::parse_box(&out[..n]).unwrap());
}
}
#[test]
fn es_descriptor_parse_is_symmetric_with_serialize() {
for &len in &[0usize, 1, 100, 101, 127, 128, 200, 255] {
let url: alloc::string::String = "x".repeat(len);
let es = es_descriptor_with_url(url);
let bytes = es.try_to_bytes().unwrap();
let parsed = ESDescriptor::parse(&bytes)
.unwrap_or_else(|e| panic!("parse URL length {len} failed: {e:?}"));
assert_eq!(parsed, es, "URL length {len} did not survive parse");
let mut out = vec![0u8; parsed.serialized_len()];
let n = parsed.serialize_into(&mut out).unwrap();
assert_eq!(&out[..n], &bytes[..], "URL length {len} not byte-identical");
}
}
fn minimal_varint_width(size: usize) -> usize {
const W1_MAX: usize = 0x7F;
const W2_MAX: usize = 0x3FFF;
const W3_MAX: usize = 0x1F_FFFF;
if size <= W1_MAX {
1
} else if size <= W2_MAX {
2
} else if size <= W3_MAX {
3
} else {
4
}
}
fn matrix_descriptor(
stream_dependence_flag: bool,
url_flag: bool,
ocr_stream_flag: bool,
url_len: usize,
pad: usize,
) -> ESDescriptor {
const TAG_PROFILE_LEVEL_INDEX: u8 = 0x14;
let mut es = ESDescriptor::new(
0x0102,
7,
Some(DecoderConfigDescriptor::new(
0x40,
5,
false,
0x0001_0000,
0x0001_807d,
0x0001_770d,
Some(DecoderSpecificInfo::new(vec![0x12, 0x08])),
)),
Some(SLConfigDescriptor::predefined_two()),
);
es.stream_dependence_flag = stream_dependence_flag;
es.depends_on_es_id = stream_dependence_flag.then_some(0x0AAA);
es.url_flag = url_flag;
es.url = url_flag.then(|| "u".repeat(url_len));
es.ocr_stream_flag = ocr_stream_flag;
es.ocr_es_id = ocr_stream_flag.then_some(0x0BBB);
es.unknown_descriptors.push(UnknownDescriptor {
tag: TAG_PROFILE_LEVEL_INDEX,
size_width: minimal_varint_width(pad),
data: vec![0x55; pad],
position: 0,
});
es.size_width = VARINT_WIDTH_FIXED;
let wide_len = es.try_to_bytes().expect("matrix es serializes");
let body_size = wide_len.len() - 1 - VARINT_WIDTH_FIXED;
es.size_width = minimal_varint_width(body_size);
es
}
#[test]
fn es_descriptor_flag_matrix_round_trips() {
const PAD_3BYTE: usize = 16_500;
const PAD_4BYTE: usize = 2_100_000;
let mut seen_width = [false; 5];
let mut cases = Vec::new();
for combo in 0..8u8 {
let sdf = combo & 0b100 != 0;
let url_flag = combo & 0b010 != 0;
let ocr = combo & 0b001 != 0;
let url_lens: &[usize] = if url_flag {
&[0, 1, 100, 200, 255]
} else {
&[0]
};
for &url_len in url_lens {
cases.push((sdf, url_flag, ocr, url_len, 3usize, "matrix"));
}
}
cases.push((true, true, true, 100, PAD_3BYTE, "3-byte varint"));
cases.push((false, true, false, 100, PAD_4BYTE, "4-byte varint"));
for (sdf, url_flag, ocr, url_len, pad, what) in cases {
let es = matrix_descriptor(sdf, url_flag, ocr, url_len, pad);
let bytes = es
.try_to_bytes()
.unwrap_or_else(|e| panic!("serialize {what} failed: {e:?}"));
let parsed = ESDescriptor::parse(&bytes)
.unwrap_or_else(|e| panic!("parse {what} failed: {e:?}"));
assert_eq!(
parsed, es,
"{what} (url {url_len}, pad {pad}) must round-trip"
);
assert_eq!(parsed.size_width, es.size_width);
seen_width[es.size_width] = true;
assert_eq!(parsed.unknown_descriptors.len(), 1, "{what}");
assert_eq!(parsed.unknown_descriptors[0].position, 0, "{what}");
assert_eq!(parsed.stream_dependence_flag, sdf);
assert_eq!(parsed.url_flag, url_flag);
assert_eq!(
parsed.url.as_deref(),
url_flag.then(|| "u".repeat(url_len)).as_deref()
);
assert_eq!(parsed.ocr_stream_flag, ocr);
let mut out = vec![0u8; parsed.serialized_len()];
let n = parsed.serialize_into(&mut out).unwrap();
assert_eq!(&out[..n], &bytes[..], "{what} not byte-identical");
assert_eq!(ESDescriptor::parse(&out[..n]).unwrap(), parsed, "{what}");
}
for (width, produced) in seen_width.iter().enumerate().skip(1) {
assert!(*produced, "matrix never produced a {width}-byte varint");
}
}
#[test]
fn es_descriptor_over_range_is_error_not_panic() {
for &len in &[256usize, 300, 1000] {
let err = es_descriptor_with_url("x".repeat(len))
.try_to_bytes()
.unwrap_err();
assert!(
matches!(
err,
Error::FieldOverflow(broadcast_common::len::FieldOverflow {
field: "URLstring length",
..
})
),
"len {len}: expected FieldOverflow, got {err:?}"
);
}
#[rustfmt::skip]
let hostile: &[u8] = &[
TAG_ES_DESCRIPTOR, 0xFF, 0xFF, 0xFF, 0x7F,
0x00, 0x01, 0x00,
];
let err =
ESDescriptor::parse(hostile).expect_err("hostile size varint must fail, not panic");
assert!(matches!(err, Error::BufferTooShort { .. }), "got {err:?}");
}
#[test]
fn es_descriptor_non_utf8_url_is_rejected() {
#[rustfmt::skip]
let payload: &[u8] = &[
TAG_ES_DESCRIPTOR, 0x05,
0x00, 0x01, 0x40, 0x01, 0xFF, ];
let err = ESDescriptor::parse(payload)
.expect_err("non-UTF-8 URLstring must fail, not decode lossily");
assert!(
matches!(
err,
Error::InvalidValue {
field: "URLstring",
..
}
),
"expected InvalidValue for URLstring, got {err:?}"
);
#[rustfmt::skip]
let mut boxed = Vec::new();
boxed.extend_from_slice(&((8 + 4 + payload.len()) as u32).to_be_bytes());
boxed.extend_from_slice(b"esds");
boxed.extend_from_slice(&[0, 0, 0, 0]);
boxed.extend_from_slice(payload);
let e = EsdsBox::parse_box(&boxed).expect_err("box path must reject too");
assert!(matches!(e, Error::InvalidValue { .. }), "got {e:?}");
let ok = alloc::string::String::from("http://ex.test/\u{1F512}");
let framed = es_descriptor_with_url(ok.clone())
.try_to_bytes()
.expect("serialize valid utf-8 URL");
let desc = ESDescriptor::parse(&framed).expect("valid UTF-8 parses");
assert_eq!(desc.url.as_deref(), Some(ok.as_str()));
assert_eq!(desc.try_to_bytes().expect("re-serialize"), framed);
}
#[test]
fn minimal_width_esds_with_long_url_round_trips() {
for &url_len in &[100usize, 127, 128, 200, 255] {
let url = vec![b'x'; url_len];
let dsi = [0x12, 0x08, 0x56, 0xe5, 0x00];
let dc_body = 13 + 1 + 1 + dsi.len();
let sl_body = 1usize;
let es_body = 2 + 1 + 1 + url_len + (1 + 1 + dc_body) + (1 + 1 + sl_body);
assert!(es_body <= 0x3FFF, "minimal ES size must fit 2 bytes");
let mut wire = Vec::new();
let es_total = 1 + 2 + es_body;
let box_total = 8 + 4 + es_total;
wire.extend_from_slice(&(box_total as u32).to_be_bytes());
wire.extend_from_slice(b"esds");
wire.extend_from_slice(&[0, 0, 0, 0]);
wire.extend_from_slice(&[
TAG_ES_DESCRIPTOR,
0x80 | ((es_body >> 7) as u8),
(es_body & 0x7F) as u8,
]);
wire.extend_from_slice(&[0x00, 0x02]);
wire.push(0x40);
wire.push(url_len as u8);
wire.extend_from_slice(&url);
wire.extend_from_slice(&[TAG_DECODER_CONFIG, dc_body as u8]);
wire.extend_from_slice(&[0x40, 0x15, 0, 0, 0, 0, 1, 0x77, 0, 0, 1, 0x77, 0]);
wire.extend_from_slice(&[TAG_DECODER_SPECIFIC_INFO, dsi.len() as u8]);
wire.extend_from_slice(&dsi);
wire.extend_from_slice(&[TAG_SL_CONFIG, sl_body as u8, 0x02]);
let esds = EsdsBox::parse_box(&wire).unwrap_or_else(|e| {
panic!("parse minimal-width URL {url_len} failed: {e:?}");
});
assert_eq!(
esds.es_descriptor.url.as_deref().map(str::len),
Some(url_len)
);
assert_eq!(esds.serialized_len(), wire.len());
let mut out = vec![0u8; esds.serialized_len()];
let n = esds.serialize_into(&mut out).unwrap();
assert_eq!(&out[..n], &wire[..], "URL {url_len} not byte-identical");
}
}
#[test]
fn rejects_oversized_es_descriptor_size() {
#[rustfmt::skip]
let body: Vec<u8> = [
0x00, 0x00, 0x00, 0x00, TAG_ES_DESCRIPTOR, 0xFF, 0xFF, 0xFF, 0x7F,
]
.to_vec();
let err = EsdsBox::parse_body(&body).expect_err("oversized ES_Descriptor size must fail");
assert!(
matches!(err, Error::BufferTooShort { .. }),
"expected BufferTooShort, got {err:?}"
);
}
#[test]
fn parses_size_zero_box_as_rest_of_buffer() {
let explicit = EsdsBox::parse_box(REAL_ESDS_BOX_AAC).expect("explicit-size twin parses");
let mut zeroed = REAL_ESDS_BOX_AAC.to_vec();
zeroed[..4].copy_from_slice(&0u32.to_be_bytes());
let to_eof = EsdsBox::parse_box(&zeroed).expect("size-0 esds parses as rest-of-buffer");
assert_eq!(to_eof, explicit);
}
}