use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObuType {
Reserved(u8),
SequenceHeader,
TemporalDelimiter,
FrameHeader,
TileGroup,
Metadata,
Frame,
RedundantFrameHeader,
TileList,
Padding,
}
impl ObuType {
pub fn from_raw(raw: u8) -> Self {
match raw {
1 => Self::SequenceHeader,
2 => Self::TemporalDelimiter,
3 => Self::FrameHeader,
4 => Self::TileGroup,
5 => Self::Metadata,
6 => Self::Frame,
7 => Self::RedundantFrameHeader,
8 => Self::TileList,
15 => Self::Padding,
other => Self::Reserved(other),
}
}
pub fn as_raw(&self) -> u8 {
match self {
Self::Reserved(v) => *v,
Self::SequenceHeader => 1,
Self::TemporalDelimiter => 2,
Self::FrameHeader => 3,
Self::TileGroup => 4,
Self::Metadata => 5,
Self::Frame => 6,
Self::RedundantFrameHeader => 7,
Self::TileList => 8,
Self::Padding => 15,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ObuDescriptor<'a> {
pub obu_type: ObuType,
pub extension_flag: bool,
pub has_size_field: bool,
pub temporal_id: u8,
pub spatial_id: u8,
pub payload_len: usize,
pub header_len: usize,
pub payload: &'a [u8],
}
#[derive(Debug)]
pub struct ObuIter<'a> {
rest: &'a [u8],
offset: usize,
done: bool,
}
impl<'a> ObuIter<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self {
rest: bytes,
offset: 0,
done: false,
}
}
pub fn position(&self) -> usize {
self.offset
}
}
pub fn parse_obu(bytes: &[u8]) -> Result<(ObuDescriptor<'_>, usize), Error> {
let first = *bytes.first().ok_or(Error::UnexpectedEnd)?;
let mut cursor = 1usize;
let forbidden_bit = (first >> 7) & 0x1;
if forbidden_bit != 0 {
return Err(Error::ForbiddenBitSet);
}
let obu_type_raw = (first >> 3) & 0x0f;
let extension_flag = ((first >> 2) & 0x1) != 0;
let has_size_field = ((first >> 1) & 0x1) != 0;
let (temporal_id, spatial_id) = if extension_flag {
let ext = *bytes.get(cursor).ok_or(Error::UnexpectedEnd)?;
cursor += 1;
let temporal = (ext >> 5) & 0x7; let spatial = (ext >> 3) & 0x3; (temporal, spatial)
} else {
(0u8, 0u8)
};
let payload_len = if has_size_field {
let (size, size_bytes) = parse_leb128(&bytes[cursor..])?;
cursor += size_bytes;
usize::try_from(size).map_err(|_| Error::SizeOverflow)?
} else {
return Err(Error::MissingSizeField);
};
let payload_end = cursor.checked_add(payload_len).ok_or(Error::SizeOverflow)?;
if payload_end > bytes.len() {
return Err(Error::UnexpectedEnd);
}
let payload = &bytes[cursor..payload_end];
let descriptor = ObuDescriptor {
obu_type: ObuType::from_raw(obu_type_raw),
extension_flag,
has_size_field,
temporal_id,
spatial_id,
payload_len,
header_len: cursor,
payload,
};
Ok((descriptor, payload_end))
}
pub fn parse_leb128(bytes: &[u8]) -> Result<(u64, usize), Error> {
let mut value: u64 = 0;
let mut leb128_bytes: usize = 0;
for i in 0..8 {
let byte = *bytes.get(i).ok_or(Error::UnexpectedEnd)?;
let chunk = u64::from(byte & 0x7f) << (i * 7);
value |= chunk;
leb128_bytes += 1;
if (byte & 0x80) == 0 {
if value > u64::from(u32::MAX) {
return Err(Error::Leb128Overflow);
}
return Ok((value, leb128_bytes));
}
}
Err(Error::Leb128TooLong)
}
impl<'a> Iterator for ObuIter<'a> {
type Item = Result<ObuDescriptor<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.done || self.rest.is_empty() {
return None;
}
match parse_obu(self.rest) {
Ok((descriptor, consumed)) => {
self.rest = &self.rest[consumed..];
self.offset += consumed;
Some(Ok(descriptor))
}
Err(e) => {
self.done = true;
Some(Err(e))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn leb128_decodes_single_byte_zero() {
let (v, n) = parse_leb128(&[0x00]).expect("zero is a valid leb128");
assert_eq!(v, 0);
assert_eq!(n, 1);
}
#[test]
fn leb128_decodes_multi_byte_value() {
let (v, n) = parse_leb128(&[0x81, 0x01]).expect("two-byte leb128 decodes");
assert_eq!(v, 129);
assert_eq!(n, 2);
}
#[test]
fn leb128_allows_redundant_zero_padding() {
let (v, n) = parse_leb128(&[0x80, 0x00]).expect("padded zero is valid leb128");
assert_eq!(v, 0);
assert_eq!(n, 2);
}
#[test]
fn leb128_rejects_value_above_u32_max() {
let err = parse_leb128(&[0x80, 0x80, 0x80, 0x80, 0x10])
.expect_err("2^32 exceeds the §4.10.5 cap");
assert_eq!(err, Error::Leb128Overflow);
}
#[test]
fn leb128_rejects_encoding_longer_than_eight_bytes() {
let nine_bytes = [0x80u8; 9];
let err = parse_leb128(&nine_bytes).expect_err("8 continuation bytes must be rejected");
assert_eq!(err, Error::Leb128TooLong);
}
fn obu_header_byte(obu_type: u8, ext_flag: bool, has_size_field: bool) -> u8 {
debug_assert!(obu_type <= 0x0f);
let ext = u8::from(ext_flag);
let sz = u8::from(has_size_field);
(obu_type << 3) | (ext << 2) | (sz << 1)
}
#[test]
fn parses_temporal_delimiter_no_extension_empty_payload() {
let header = obu_header_byte(2, false, true);
let bytes = [header, 0x00 ];
let (obu, consumed) = parse_obu(&bytes).expect("td OBU parses");
assert_eq!(obu.obu_type, ObuType::TemporalDelimiter);
assert!(!obu.extension_flag);
assert!(obu.has_size_field);
assert_eq!(obu.temporal_id, 0);
assert_eq!(obu.spatial_id, 0);
assert_eq!(obu.payload_len, 0);
assert_eq!(obu.header_len, 2);
assert!(obu.payload.is_empty());
assert_eq!(consumed, 2);
}
#[test]
fn parses_frame_obu_with_extension_header() {
let header = obu_header_byte(6, true, true);
let ext = (3 << 5) | (2 << 3);
let bytes = [header, ext, 0x05, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5];
let (obu, consumed) = parse_obu(&bytes).expect("frame OBU parses");
assert_eq!(obu.obu_type, ObuType::Frame);
assert!(obu.extension_flag);
assert!(obu.has_size_field);
assert_eq!(obu.temporal_id, 3);
assert_eq!(obu.spatial_id, 2);
assert_eq!(obu.payload_len, 5);
assert_eq!(obu.header_len, 3); assert_eq!(obu.payload, &[0xa1, 0xa2, 0xa3, 0xa4, 0xa5]);
assert_eq!(consumed, 8);
}
#[test]
fn iterator_walks_concatenated_obus() {
let mut stream = Vec::new();
stream.push(obu_header_byte(1, false, true));
stream.push(0x02);
stream.extend_from_slice(&[0x11, 0x22]);
stream.push(obu_header_byte(6, false, true));
stream.push(0x03);
stream.extend_from_slice(&[0x33, 0x44, 0x55]);
stream.push(obu_header_byte(2, false, true));
stream.push(0x00);
let mut iter = ObuIter::new(&stream);
let a = iter.next().unwrap().unwrap();
assert_eq!(a.obu_type, ObuType::SequenceHeader);
assert_eq!(a.payload, &[0x11, 0x22]);
let b = iter.next().unwrap().unwrap();
assert_eq!(b.obu_type, ObuType::Frame);
assert_eq!(b.payload, &[0x33, 0x44, 0x55]);
let c = iter.next().unwrap().unwrap();
assert_eq!(c.obu_type, ObuType::TemporalDelimiter);
assert!(c.payload.is_empty());
assert!(iter.next().is_none());
}
#[test]
fn rejects_forbidden_bit_set() {
let bad = obu_header_byte(2, false, true) | 0x80;
let err = parse_obu(&[bad, 0x00]).expect_err("obu_forbidden_bit=1 must be rejected");
assert_eq!(err, Error::ForbiddenBitSet);
}
#[test]
fn rejects_truncated_payload() {
let header = obu_header_byte(4, false, true);
let bytes = [header, 0x04, 0xaa, 0xbb];
let err = parse_obu(&bytes).expect_err("truncated payload must error");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn rejects_missing_size_field() {
let header = obu_header_byte(2, false, false);
let err = parse_obu(&[header]).expect_err("size-less OBU must be rejected");
assert_eq!(err, Error::MissingSizeField);
}
#[test]
fn reserved_obu_type_preserves_raw_value() {
let header = obu_header_byte(9, false, true);
let bytes = [header, 0x01, 0xde];
let (obu, _) = parse_obu(&bytes).expect("reserved OBU parses");
assert_eq!(obu.obu_type, ObuType::Reserved(9));
assert_eq!(obu.obu_type.as_raw(), 9);
assert_eq!(obu.payload, &[0xde]);
}
}