use crate::bitstream::{BitError, BitReader};
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum SpsExtensionError {
#[error("bitstream read failed: {0}")]
Bitstream(#[from] BitError),
#[error("seq_parameter_set_id out of range (got {0}, max 31)")]
SpsIdOutOfRange(u32),
#[error("aux_format_idc out of range (got {0}, max 3)")]
AuxFormatIdcOutOfRange(u32),
#[error("bit_depth_aux_minus8 out of range (got {0}, max 4)")]
BitDepthAuxOutOfRange(u32),
}
pub type SpsExtensionResult<T> = Result<T, SpsExtensionError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AuxFormat {
pub bit_depth_aux_minus8: u32,
pub alpha_incr_flag: bool,
pub alpha_opaque_value: u32,
pub alpha_transparent_value: u32,
}
impl AuxFormat {
pub fn bit_depth_aux(&self) -> u32 {
self.bit_depth_aux_minus8 + 8
}
pub fn alpha_value_bits(&self) -> u32 {
self.bit_depth_aux_minus8 + 9
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SeqParameterSetExtension {
pub seq_parameter_set_id: u32,
pub aux_format_idc: u32,
pub aux_format: Option<AuxFormat>,
pub additional_extension_flag: bool,
}
impl SeqParameterSetExtension {
pub fn parse(rbsp: &[u8]) -> SpsExtensionResult<Self> {
let mut r = BitReader::new(rbsp);
Self::parse_from(&mut r)
}
pub fn parse_from(r: &mut BitReader<'_>) -> SpsExtensionResult<Self> {
let seq_parameter_set_id = r.ue()?;
if seq_parameter_set_id > 31 {
return Err(SpsExtensionError::SpsIdOutOfRange(seq_parameter_set_id));
}
let aux_format_idc = r.ue()?;
if aux_format_idc > 3 {
return Err(SpsExtensionError::AuxFormatIdcOutOfRange(aux_format_idc));
}
let aux_format = if aux_format_idc != 0 {
let bit_depth_aux_minus8 = r.ue()?;
if bit_depth_aux_minus8 > 4 {
return Err(SpsExtensionError::BitDepthAuxOutOfRange(
bit_depth_aux_minus8,
));
}
let alpha_incr_flag = r.u(1)? == 1;
let alpha_value_bits = bit_depth_aux_minus8 + 9;
let alpha_opaque_value = r.u(alpha_value_bits)?;
let alpha_transparent_value = r.u(alpha_value_bits)?;
Some(AuxFormat {
bit_depth_aux_minus8,
alpha_incr_flag,
alpha_opaque_value,
alpha_transparent_value,
})
} else {
None
};
let additional_extension_flag = r.u(1)? == 1;
Ok(Self {
seq_parameter_set_id,
aux_format_idc,
aux_format,
additional_extension_flag,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
struct BitWriter {
bits: Vec<u8>,
}
impl BitWriter {
fn new() -> Self {
Self { bits: Vec::new() }
}
fn u(&mut self, value: u32, n: u32) {
for i in (0..n).rev() {
self.bits.push(((value >> i) & 1) as u8);
}
}
fn ue(&mut self, value: u32) {
let code_num = value + 1;
let n = 32 - code_num.leading_zeros();
let leading = n - 1;
for _ in 0..leading {
self.bits.push(0);
}
self.u(code_num, n);
}
fn into_bytes(mut self) -> Vec<u8> {
self.bits.push(1);
while self.bits.len() % 8 != 0 {
self.bits.push(0);
}
let mut out = vec![0u8; self.bits.len() / 8];
for (i, &b) in self.bits.iter().enumerate() {
if b == 1 {
out[i / 8] |= 1 << (7 - (i % 8));
}
}
out
}
}
#[test]
fn aux_format_idc_zero_skips_alpha_block() {
let mut w = BitWriter::new();
w.ue(7); w.ue(0); w.u(0, 1); let bytes = w.into_bytes();
let ext = SeqParameterSetExtension::parse(&bytes).unwrap();
assert_eq!(ext.seq_parameter_set_id, 7);
assert_eq!(ext.aux_format_idc, 0);
assert_eq!(ext.aux_format, None);
assert!(!ext.additional_extension_flag);
}
#[test]
fn aux_format_present_full_block() {
let mut w = BitWriter::new();
w.ue(0); w.ue(1); w.ue(2); w.u(1, 1); w.u(0x3FF, 11); w.u(0x000, 11); w.u(0, 1); let bytes = w.into_bytes();
let ext = SeqParameterSetExtension::parse(&bytes).unwrap();
assert_eq!(ext.seq_parameter_set_id, 0);
assert_eq!(ext.aux_format_idc, 1);
let aux = ext.aux_format.expect("aux block present");
assert_eq!(aux.bit_depth_aux_minus8, 2);
assert_eq!(aux.bit_depth_aux(), 10);
assert_eq!(aux.alpha_value_bits(), 11);
assert!(aux.alpha_incr_flag);
assert_eq!(aux.alpha_opaque_value, 0x3FF);
assert_eq!(aux.alpha_transparent_value, 0x000);
assert!(!ext.additional_extension_flag);
}
#[test]
fn aux_value_width_tracks_bit_depth_minus8_zero() {
let mut w = BitWriter::new();
w.ue(3); w.ue(3); w.ue(0); w.u(0, 1); w.u(0x1FF, 9); w.u(0x055, 9); w.u(0, 1); let bytes = w.into_bytes();
let ext = SeqParameterSetExtension::parse(&bytes).unwrap();
let aux = ext.aux_format.unwrap();
assert_eq!(aux.bit_depth_aux(), 8);
assert_eq!(aux.alpha_value_bits(), 9);
assert_eq!(aux.alpha_opaque_value, 0x1FF);
assert_eq!(aux.alpha_transparent_value, 0x055);
}
#[test]
fn additional_extension_flag_surfaced() {
let mut w = BitWriter::new();
w.ue(1);
w.ue(0);
w.u(1, 1); let bytes = w.into_bytes();
let ext = SeqParameterSetExtension::parse(&bytes).unwrap();
assert!(ext.additional_extension_flag);
}
#[test]
fn rejects_sps_id_out_of_range() {
let mut w = BitWriter::new();
w.ue(32); let bytes = w.into_bytes();
assert_eq!(
SeqParameterSetExtension::parse(&bytes),
Err(SpsExtensionError::SpsIdOutOfRange(32))
);
}
#[test]
fn rejects_aux_format_idc_out_of_range() {
let mut w = BitWriter::new();
w.ue(0);
w.ue(4); let bytes = w.into_bytes();
assert_eq!(
SeqParameterSetExtension::parse(&bytes),
Err(SpsExtensionError::AuxFormatIdcOutOfRange(4))
);
}
#[test]
fn rejects_bit_depth_aux_out_of_range() {
let mut w = BitWriter::new();
w.ue(0);
w.ue(1); w.ue(5); let bytes = w.into_bytes();
assert_eq!(
SeqParameterSetExtension::parse(&bytes),
Err(SpsExtensionError::BitDepthAuxOutOfRange(5))
);
}
#[test]
fn truncated_input_surfaces_eof() {
let bytes = [0b1010_1000u8];
let res = SeqParameterSetExtension::parse(&bytes);
assert!(matches!(
res,
Err(SpsExtensionError::Bitstream(BitError::Eof))
));
}
}