use alloc::vec::Vec;
use crate::error::{Error, Result};
use broadcast_common::{Parse, Serialize};
pub const SYSTEM_HEADER_START_CODE: u32 = 0x0000_01BB;
const EXT_STREAM_ID: u8 = 0xB7;
pub(crate) const PREFIX_LEN: usize = 6;
const RESERVED_BITS_MASK: u8 = 0x7F;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct StdBufferBound {
pub stream_id: u8,
pub stream_id_extension: Option<u8>,
pub buffer_bound_scale: bool,
pub buffer_size_bound: u16,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SystemHeader {
pub rate_bound: u32,
pub audio_bound: u8,
pub fixed_flag: bool,
pub csps_flag: bool,
pub system_audio_lock_flag: bool,
pub system_video_lock_flag: bool,
pub video_bound: u8,
pub packet_rate_restriction_flag: bool,
pub reserved_bits: u8,
pub std_buffer_bounds: Vec<StdBufferBound>,
}
fn stream_loop_len(system_header: &SystemHeader) -> usize {
let mut len: usize = 0;
for b in &system_header.std_buffer_bounds {
if b.stream_id_extension.is_some() {
len += 6;
} else {
len += 3;
}
}
len
}
impl<'a> Parse<'a> for SystemHeader {
type Error = Error;
fn parse(b: &'a [u8]) -> Result<Self> {
if b.len() < PREFIX_LEN {
return Err(Error::BufferTooShort {
need: PREFIX_LEN,
have: b.len(),
what: "system_header prefix",
});
}
if u32::from_be_bytes([b[0], b[1], b[2], b[3]]) != SYSTEM_HEADER_START_CODE {
return Err(Error::BadSystemHeaderStartCode(u32::from_be_bytes([
b[0], b[1], b[2], b[3],
])));
}
let header_length = u16::from_be_bytes([b[4], b[5]]) as usize;
let body_end = PREFIX_LEN + header_length;
if b.len() < body_end {
return Err(Error::HeaderLengthOverflow {
header_length,
available: b.len().saturating_sub(PREFIX_LEN),
});
}
let body = &b[PREFIX_LEN..body_end];
if body.len() < 6 {
return Err(Error::BufferTooShort {
need: 6 + PREFIX_LEN,
have: b.len(),
what: "system_header fixed body",
});
}
if body[0] & 0x80 == 0 {
return Err(Error::BadMarker("system_header rate_bound marker 1"));
}
if body[2] & 0x01 == 0 {
return Err(Error::BadMarker("system_header rate_bound marker 2"));
}
if body[4] & 0x20 == 0 {
return Err(Error::BadMarker("system_header video_bound marker"));
}
let rate_bound = ((u32::from(body[0] & 0x7F) << 15)
| (u32::from(body[1]) << 7)
| u32::from(body[2] >> 1))
& 0x3F_FFFF;
let audio_bound = (body[3] >> 2) & 0x3F;
let fixed_flag = body[3] & 0x02 != 0;
let csps_flag = body[3] & 0x01 != 0;
let system_audio_lock_flag = body[4] & 0x80 != 0;
let system_video_lock_flag = body[4] & 0x40 != 0;
let video_bound = body[4] & 0x1F;
let packet_rate_restriction_flag = body[5] & 0x80 != 0;
let reserved_bits = body[5] & RESERVED_BITS_MASK;
let mut pos = 6;
let mut std_buffer_bounds = Vec::new();
while pos < body.len() && body[pos] & 0x80 != 0 {
let stream_id = body[pos];
if stream_id == EXT_STREAM_ID {
if pos + 6 > body.len() {
return Err(Error::BufferTooShort {
need: pos + 6,
have: body.len(),
what: "system_header extended stream entry",
});
}
if body[pos + 1] & 0xC0 != 0xC0 {
return Err(Error::BadStreamIdExtensionPrefix(body[pos + 1]));
}
let stream_id_extension = body[pos + 2] & 0x7F;
if body[pos + 3] != 0xB6 {
return Err(Error::BadStreamIdExtensionPrefix(body[pos + 3]));
}
if body[pos + 4] & 0xC0 != 0xC0 {
return Err(Error::BadMarker("P-STD_buffer_bound_scale prefix (ext)"));
}
let buffer_bound_scale = body[pos + 4] & 0x20 != 0;
let buffer_size_bound =
(u16::from(body[pos + 4] & 0x1F) << 8) | u16::from(body[pos + 5]);
std_buffer_bounds.push(StdBufferBound {
stream_id,
stream_id_extension: Some(stream_id_extension),
buffer_bound_scale,
buffer_size_bound,
});
pos += 6;
} else {
if pos + 3 > body.len() {
return Err(Error::BufferTooShort {
need: pos + 3,
have: body.len(),
what: "system_header stream entry",
});
}
if body[pos + 1] & 0xC0 != 0xC0 {
return Err(Error::BadMarker("P-STD_buffer_bound_scale prefix"));
}
let buffer_bound_scale = body[pos + 1] & 0x20 != 0;
let buffer_size_bound =
(u16::from(body[pos + 1] & 0x1F) << 8) | u16::from(body[pos + 2]);
std_buffer_bounds.push(StdBufferBound {
stream_id,
stream_id_extension: None,
buffer_bound_scale,
buffer_size_bound,
});
pos += 3;
}
}
Ok(SystemHeader {
rate_bound,
audio_bound,
fixed_flag,
csps_flag,
system_audio_lock_flag,
system_video_lock_flag,
video_bound,
packet_rate_restriction_flag,
reserved_bits,
std_buffer_bounds,
})
}
}
impl Serialize for SystemHeader {
type Error = Error;
fn serialized_len(&self) -> usize {
PREFIX_LEN + 6 + stream_loop_len(self)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let total = self.serialized_len();
if buf.len() < total {
return Err(Error::BufferTooShort {
need: total,
have: buf.len(),
what: "system_header serialize output",
});
}
buf[0..4].copy_from_slice(&SYSTEM_HEADER_START_CODE.to_be_bytes());
let header_length = broadcast_common::len::fit_u16(
6 + stream_loop_len(self),
"system_header.header_length",
)?;
buf[4..6].copy_from_slice(&header_length.to_be_bytes());
let rate_bound = self.rate_bound & 0x3F_FFFF;
buf[6] = 0x80 | ((rate_bound >> 15) & 0x7F) as u8;
buf[7] = ((rate_bound >> 7) & 0xFF) as u8;
buf[8] = (((rate_bound & 0x7F) as u8) << 1) | 0x01;
buf[9] = (self.audio_bound & 0x3F) << 2
| (u8::from(self.fixed_flag) << 1)
| u8::from(self.csps_flag);
buf[10] = (u8::from(self.system_audio_lock_flag) << 7)
| (u8::from(self.system_video_lock_flag) << 6)
| 0x20 | (self.video_bound & 0x1F);
buf[11] = (u8::from(self.packet_rate_restriction_flag) << 7)
| (self.reserved_bits & RESERVED_BITS_MASK);
let mut pos = 12;
for bound in &self.std_buffer_bounds {
if let Some(ext) = bound.stream_id_extension {
buf[pos] = bound.stream_id;
buf[pos + 1] = 0xC0;
buf[pos + 2] = ext & 0x7F;
buf[pos + 3] = 0xB6;
buf[pos + 4] = 0xC0
| (u8::from(bound.buffer_bound_scale) << 5)
| ((bound.buffer_size_bound >> 8) & 0x1F) as u8;
buf[pos + 5] = (bound.buffer_size_bound & 0xFF) as u8;
pos += 6;
} else {
buf[pos] = bound.stream_id;
buf[pos + 1] = 0xC0
| (u8::from(bound.buffer_bound_scale) << 5)
| ((bound.buffer_size_bound >> 8) & 0x1F) as u8;
buf[pos + 2] = (bound.buffer_size_bound & 0xFF) as u8;
pos += 3;
}
}
Ok(total)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn system_header_round_trip_no_streams() {
let bytes = vec![
0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF, ];
let h = SystemHeader::parse(&bytes).unwrap();
assert_eq!(h.rate_bound, 0);
assert_eq!(h.audio_bound, 1);
assert!(!h.fixed_flag);
assert!(!h.csps_flag);
assert!(!h.system_audio_lock_flag);
assert!(!h.system_video_lock_flag);
assert_eq!(h.video_bound, 0);
assert!(h.packet_rate_restriction_flag);
assert!(h.std_buffer_bounds.is_empty());
let mut out = vec![0u8; h.serialized_len()];
h.serialize_into(&mut out).unwrap();
assert_eq!(&out[..], &bytes[..]);
let h2 = SystemHeader::parse(&out).unwrap();
assert_eq!(h, h2);
}
#[test]
fn video_bound_bit_4_round_trips() {
let bytes = vec![
0x00,
0x00,
0x01,
0xBB, 0x00,
0x06, 0x80,
0x00,
0x01, 0x04, 0x20 | 0x1F, 0xFF,
];
let h = SystemHeader::parse(&bytes).unwrap();
assert_eq!(
h.video_bound, 31,
"bit 4 of video_bound must not be dropped"
);
let mut out = vec![0u8; h.serialized_len()];
h.serialize_into(&mut out).unwrap();
assert_eq!(&out[..], &bytes[..]);
}
#[test]
fn system_header_round_trip_with_streams() {
let bytes = vec![
0x00, 0x00, 0x01, 0xBB, 0x00,
0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
0xE0, 0xFF, 0xFF, 0xC0, 0xC1, 0x00,
];
let h = SystemHeader::parse(&bytes).unwrap();
assert_eq!(h.std_buffer_bounds.len(), 2);
assert_eq!(h.std_buffer_bounds[0].stream_id, 0xE0);
assert!(h.std_buffer_bounds[0].stream_id_extension.is_none());
assert!(h.std_buffer_bounds[0].buffer_bound_scale);
assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x1FFF);
assert_eq!(h.std_buffer_bounds[1].stream_id, 0xC0);
assert!(h.std_buffer_bounds[1].stream_id_extension.is_none());
assert!(!h.std_buffer_bounds[1].buffer_bound_scale);
assert_eq!(h.std_buffer_bounds[1].buffer_size_bound, 0x0100);
let mut out = vec![0u8; h.serialized_len()];
h.serialize_into(&mut out).unwrap();
assert_eq!(&out[..], &bytes[..]);
let h2 = SystemHeader::parse(&out).unwrap();
assert_eq!(h, h2);
let h_mut = SystemHeader {
rate_bound: 12345,
..h.clone()
};
let mut out2 = vec![0u8; h_mut.serialized_len()];
h_mut.serialize_into(&mut out2).unwrap();
assert_ne!(&out[..], &out2[..]);
}
#[test]
fn non_conventional_reserved_bits_round_trip() {
let bytes = vec![
0x00, 0x00, 0x01, 0xBB, 0x00, 0x06, 0x80, 0x00, 0x01, 0x04, 0x20, 0x2A, ];
let h = SystemHeader::parse(&bytes).unwrap();
assert_eq!(h.reserved_bits, 0x2A);
assert!(!h.packet_rate_restriction_flag);
let mut out = vec![0u8; h.serialized_len()];
h.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes);
}
#[test]
fn many_bounds_past_old_u16_overflow_point_errs_not_panics() {
let std_buffer_bounds = (0..11_000)
.map(|_| StdBufferBound {
stream_id: EXT_STREAM_ID,
stream_id_extension: Some(0x05),
buffer_bound_scale: false,
buffer_size_bound: 0,
})
.collect::<Vec<_>>();
let h = SystemHeader {
rate_bound: 0,
audio_bound: 0,
fixed_flag: false,
csps_flag: false,
system_audio_lock_flag: false,
system_video_lock_flag: false,
video_bound: 0,
packet_rate_restriction_flag: false,
reserved_bits: RESERVED_BITS_MASK,
std_buffer_bounds,
};
let mut buf = vec![0u8; h.serialized_len()];
assert!(h.serialize_into(&mut buf).is_err());
}
#[test]
fn header_length_over_u16_is_rejected_not_wrapped() {
let std_buffer_bounds = (0..25_000)
.map(|_| StdBufferBound {
stream_id: 0xC0,
stream_id_extension: None,
buffer_bound_scale: false,
buffer_size_bound: 0,
})
.collect::<Vec<_>>();
let h = SystemHeader {
rate_bound: 0,
audio_bound: 0,
fixed_flag: false,
csps_flag: false,
system_audio_lock_flag: false,
system_video_lock_flag: false,
video_bound: 0,
packet_rate_restriction_flag: false,
reserved_bits: RESERVED_BITS_MASK,
std_buffer_bounds,
};
let mut buf = vec![0u8; h.serialized_len()];
assert!(h.serialize_into(&mut buf).is_err());
}
#[test]
fn many_bounds_within_u16_header_length_round_trips() {
let std_buffer_bounds = (0..1000)
.map(|_| StdBufferBound {
stream_id: 0xC0,
stream_id_extension: None,
buffer_bound_scale: false,
buffer_size_bound: 0x0100,
})
.collect::<Vec<_>>();
let h = SystemHeader {
rate_bound: 0,
audio_bound: 0,
fixed_flag: false,
csps_flag: false,
system_audio_lock_flag: false,
system_video_lock_flag: false,
video_bound: 0,
packet_rate_restriction_flag: false,
reserved_bits: RESERVED_BITS_MASK,
std_buffer_bounds,
};
let mut buf = vec![0u8; h.serialized_len()];
h.serialize_into(&mut buf).unwrap();
let back = SystemHeader::parse(&buf).unwrap();
assert_eq!(back.std_buffer_bounds.len(), 1000);
}
#[test]
fn system_header_round_trip_extended_stream_id() {
let bytes = vec![
0x00, 0x00, 0x01, 0xBB, 0x00, 0x0C, 0x80, 0x00, 0x01, 0x04, 0x20, 0xFF,
0xB7, 0xC0, 0x05, 0xB6, 0xE1, 0x00,
];
let h = SystemHeader::parse(&bytes).unwrap();
assert_eq!(h.std_buffer_bounds.len(), 1);
assert_eq!(h.std_buffer_bounds[0].stream_id, 0xB7);
assert_eq!(h.std_buffer_bounds[0].stream_id_extension, Some(0x05));
assert!(h.std_buffer_bounds[0].buffer_bound_scale);
assert_eq!(h.std_buffer_bounds[0].buffer_size_bound, 0x100);
let mut out = vec![0u8; h.serialized_len()];
h.serialize_into(&mut out).unwrap();
assert_eq!(&out[..], &bytes[..]);
}
}