pub mod frame;
pub mod frame_header;
pub mod metadata;
pub mod sequence_header;
pub mod tile_group;
pub mod tile_list;
use self::{
frame::Frame,
frame_header::{FrameHeader, FrameType},
metadata::Metadata,
sequence_header::SequenceHeader,
tile_group::TileGroup,
tile_list::TileList,
};
use crate::buffer::Buffer;
pub const REFS_PER_FRAME: u8 = 7;
pub const TOTAL_REFS_PER_FRAME: u8 = 8;
pub const MAX_TILE_WIDTH: u16 = 4096;
pub const MAX_TILE_AREA: u32 = 4096 * 2304;
pub const MAX_TILE_ROWS: u8 = 64;
pub const MAX_TILE_COLS: u8 = 64;
pub const NUM_REF_FRAMES: u8 = 8;
pub const MAX_SEGMENTS: u8 = 8;
pub const GM_ABS_TRANS_BITS: u8 = 12;
pub const GM_ABS_TRANS_ONLY_BITS: u8 = 9;
pub const GM_ABS_ALPHA_BITS: u8 = 12;
pub const GM_ALPHA_PREC_BITS: u8 = 15;
pub const GM_TRANS_PREC_BITS: u8 = 6;
pub const GM_TRANS_ONLY_PREC_BITS: u8 = 3;
pub const SGRPROJ_PRJ_SUBEXP_K: u8 = 4;
pub const SELECT_SCREEN_CONTENT_TOOLS: u8 = 2;
pub const SELECT_INTEGER_MV: u8 = 2;
pub const SUPERRES_DENOM_MIN: u8 = 9;
pub const SUPERRES_DENOM_BITS: u8 = 3;
pub const SUPERRES_NUM: u8 = 8;
pub const WARPEDMODEL_PREC_BITS: u8 = 16;
pub const PRIMARY_REF_NONE: u8 = 7;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObuType {
Reserved(u8),
SequenceHeader,
TemporalDelimiter,
FrameHeader,
TileGroup,
Metadata,
Frame,
RedundantFrameHeader,
TileList,
Padding,
}
impl TryFrom<u8> for ObuType {
type Error = ObuError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0 | 9..=14 => Self::Reserved(value),
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,
_ => return Err(ObuError::Unknown(ObuUnknownError::ObuHeaderType)),
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct ObuHeaderExtension {
pub temporal_id: u8,
pub spatial_id: u8,
}
impl ObuHeaderExtension {
pub fn decode(buf: &mut Buffer<'_>) -> Result<Self, ObuError> {
let temporal_id = buf.get_bits(3) as u8;
let spatial_id = buf.get_bits(2) as u8;
buf.seek_bits(3);
Ok(Self {
temporal_id,
spatial_id,
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct ObuHeader {
pub r#type: ObuType,
pub has_size: bool,
pub extension: Option<ObuHeaderExtension>,
}
impl ObuHeader {
pub fn decode(buf: &mut Buffer<'_>) -> Result<Self, ObuError> {
buf.seek_bits(1);
let r#type = ObuType::try_from(buf.get_bits(4) as u8)?;
let obu_extension_flag = buf.get_bit();
let has_size = buf.get_bit();
buf.seek_bits(1);
let extension = if obu_extension_flag {
Some(ObuHeaderExtension::decode(buf.as_mut())?)
} else {
None
};
Ok(Self {
r#type,
has_size,
extension,
})
}
}
#[derive(Debug)]
pub enum Obu {
SequenceHeader(SequenceHeader),
Frame(Frame),
FrameHeader(FrameHeader),
TileGroup(TileGroup),
Metadata(Metadata),
TileList(TileList),
TemporalDelimiter,
RedundantFrameHeader,
Drop,
}
#[derive(Debug)]
pub struct ObuContext {
pub sequence_header: Option<SequenceHeader>,
pub obu_header_extension: Option<ObuHeaderExtension>,
pub num_planes: u8,
pub bit_depth: u8,
pub seen_frame_header: bool,
pub frame_is_intra: bool,
pub frame_width: u16,
pub frame_height: u16,
pub upscaled_width: u16,
pub superres_denom: u8,
pub mi_cols: u32,
pub mi_rows: u32,
pub render_width: u16,
pub render_height: u16,
pub order_hint: u32,
pub order_hint_bits: usize,
pub operating_point: usize,
pub operating_point_idc: u16,
pub ref_frame_type: Vec<FrameType>,
pub ref_frame_marking: Vec<bool>,
pub ref_order_hint: Vec<u32>,
pub ref_upscaled_width: Vec<u16>,
pub ref_frame_height: Vec<u16>,
pub ref_render_width: Vec<u16>,
pub ref_render_height: Vec<u16>,
pub ref_frame_idx: [u8; REFS_PER_FRAME as usize],
pub delta_frame_id: u32,
pub prev_gm_params: [[i32; 6]; REFS_PER_FRAME as usize],
pub tile_num: usize,
}
impl Default for ObuContext {
fn default() -> Self {
let ref_frame_type = vec![FrameType::KeyFrame; NUM_REF_FRAMES as usize];
let ref_frame_marking = vec![false; NUM_REF_FRAMES as usize];
let ref_order_hint = vec![0u32; NUM_REF_FRAMES as usize];
let ref_upscaled_width = vec![0u16; NUM_REF_FRAMES as usize];
let ref_frame_height = vec![0u16; NUM_REF_FRAMES as usize];
let ref_render_width = vec![0u16; NUM_REF_FRAMES as usize];
let ref_render_height = vec![0u16; NUM_REF_FRAMES as usize];
Self {
sequence_header: None,
obu_header_extension: None,
num_planes: 3,
bit_depth: 8,
seen_frame_header: false,
frame_is_intra: false,
frame_width: 0,
frame_height: 0,
upscaled_width: 0,
superres_denom: 8, mi_cols: 0,
mi_rows: 0,
render_width: 0,
render_height: 0,
order_hint: 0,
order_hint_bits: 0,
operating_point: 0,
operating_point_idc: 0,
ref_frame_type,
ref_frame_marking,
ref_order_hint,
ref_upscaled_width,
ref_frame_height,
ref_render_width,
ref_render_height,
ref_frame_idx: [0; REFS_PER_FRAME as usize],
delta_frame_id: 0,
prev_gm_params: [[0i32; 6]; REFS_PER_FRAME as usize],
tile_num: 0,
}
}
}
#[derive(Default)]
pub struct ObuParser {
pub ctx: ObuContext,
}
impl ObuParser {
pub fn parse(&mut self, buf: &mut Buffer) -> Result<Obu, ObuError> {
let header = ObuHeader::decode(buf.as_mut())?;
self.ctx.obu_header_extension = header.extension;
let _size = if header.has_size {
Some(buf.get_leb128() as usize)
} else {
None
};
if header.r#type != ObuType::SequenceHeader
&& header.r#type != ObuType::TemporalDelimiter
&& self.ctx.operating_point_idc != 0
{
if let Some(ext) = header.extension {
let in_temporal_layer = (self.ctx.operating_point_idc >> ext.temporal_id) & 1;
let in_spatial_layer = (self.ctx.operating_point_idc >> (ext.spatial_id + 8)) & 1;
if in_temporal_layer == 0 || in_spatial_layer == 0 {
return Ok(Obu::Drop);
}
}
}
let payload_start_bytes = buf.bytes_consumed();
let result = match header.r#type {
ObuType::SequenceHeader => {
let seq = SequenceHeader::decode(&mut self.ctx, buf)?;
self.ctx.sequence_header = Some(seq.clone());
Obu::SequenceHeader(seq)
}
ObuType::TemporalDelimiter => {
self.ctx.seen_frame_header = false;
Obu::TemporalDelimiter
}
ObuType::FrameHeader => Obu::FrameHeader(FrameHeader::decode(&mut self.ctx, buf)?),
ObuType::Frame => Obu::Frame(Frame::decode(&mut self.ctx, buf)?),
ObuType::TileGroup => {
Obu::TileGroup(TileGroup::empty())
}
ObuType::RedundantFrameHeader => {
Obu::RedundantFrameHeader
}
ObuType::Metadata => Obu::Metadata(Metadata::decode(buf)?),
ObuType::TileList => Obu::TileList(TileList::decode(buf)),
ObuType::Padding | ObuType::Reserved(_) => {
Obu::Drop
}
};
if let Some(size) = _size {
let bytes_consumed_in_payload =
buf.bytes_consumed().saturating_sub(payload_start_bytes);
if bytes_consumed_in_payload < size {
let remaining = size - bytes_consumed_in_payload;
buf.seek_bits(remaining * 8);
} else {
buf.byte_align();
}
} else {
buf.byte_align();
}
Ok(result)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObuUnknownError {
ObuHeaderType,
Profile,
ColorPrimaries,
TransferCharacteristics,
MatrixCoefficients,
ChromaSamplePosition,
MetadataType,
ScalabilityModeIdc,
FrameType,
InterpolationFilter,
FrameTypeRefIndex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObuError {
Unknown(ObuUnknownError),
NotFoundSequenceHeader,
}
impl std::error::Error for ObuError {}
impl std::fmt::Display for ObuError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ObuError::Unknown(e) => write!(f, "Unknown bitstream value: {:?}", e),
ObuError::NotFoundSequenceHeader => {
write!(f, "Frame header encountered before sequence header")
}
}
}
}