use super::error::VideoError;
use super::h264_bitstream::BitstreamReader;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Av1ObuType {
Reserved,
SequenceHeader,
TemporalDelimiter,
FrameHeader,
TileGroup,
Metadata,
Frame,
RedundantFrameHeader,
TileList,
Padding,
Unknown(u8),
}
impl Av1ObuType {
pub fn from_raw(raw: u8) -> Self {
match raw {
0 => Self::Reserved,
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::Unknown(other),
}
}
pub fn to_raw(self) -> u8 {
match self {
Self::Reserved => 0,
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,
Self::Unknown(v) => v,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Av1ObuExtension {
pub temporal_id: u8,
pub spatial_id: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Av1ObuHeader {
pub obu_type: Av1ObuType,
pub has_size: bool,
pub has_extension: bool,
pub extension: Av1ObuExtension,
pub header_len: usize,
}
pub fn parse_obu_header(data: &[u8]) -> Result<Av1ObuHeader, VideoError> {
if data.is_empty() {
return Err(VideoError::Codec("AV1: empty OBU data".into()));
}
let byte0 = data[0];
let forbidden = (byte0 >> 7) & 1;
if forbidden != 0 {
return Err(VideoError::Codec("AV1: obu_forbidden_bit is set".into()));
}
let obu_type_raw = (byte0 >> 3) & 0x0F;
let obu_type = Av1ObuType::from_raw(obu_type_raw);
let has_extension = ((byte0 >> 2) & 1) != 0;
let has_size = ((byte0 >> 1) & 1) != 0;
let mut header_len = 1usize;
let mut extension = Av1ObuExtension::default();
if has_extension {
if data.len() < 2 {
return Err(VideoError::Codec(
"AV1: truncated OBU extension header".into(),
));
}
let byte1 = data[1];
extension.temporal_id = (byte1 >> 5) & 0x07;
extension.spatial_id = (byte1 >> 3) & 0x03;
header_len = 2;
}
Ok(Av1ObuHeader {
obu_type,
has_size,
has_extension,
extension,
header_len,
})
}
pub fn read_leb128(data: &[u8]) -> Result<(u64, usize), VideoError> {
let mut value: u64 = 0;
let max_bytes = 8.min(data.len());
for i in 0..max_bytes {
let byte = data[i];
let payload = (byte & 0x7F) as u64;
value |= payload << (i * 7);
if byte & 0x80 == 0 {
return Ok((value, i + 1));
}
}
if max_bytes == 0 {
return Err(VideoError::Codec("AV1: empty LEB128 data".into()));
}
Err(VideoError::Codec("AV1: LEB128 overflow (> 8 bytes)".into()))
}
pub fn write_leb128(mut value: u64) -> Vec<u8> {
let mut out = Vec::with_capacity(8);
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
out.push(byte);
if value == 0 {
break;
}
}
out
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Av1ColorPrimaries {
Bt709,
Unspecified,
Bt470M,
Bt470Bg,
Bt601,
Smpte240,
GenericFilm,
Bt2020,
Xyz,
Smpte431,
Smpte432,
Ebu3213,
Other(u8),
}
impl Av1ColorPrimaries {
fn from_raw(v: u8) -> Self {
match v {
1 => Self::Bt709,
2 => Self::Unspecified,
4 => Self::Bt470M,
5 => Self::Bt470Bg,
6 => Self::Bt601,
7 => Self::Smpte240,
8 => Self::GenericFilm,
9 => Self::Bt2020,
10 => Self::Xyz,
11 => Self::Smpte431,
12 => Self::Smpte432,
22 => Self::Ebu3213,
other => Self::Other(other),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Av1ChromaSamplePosition {
#[default]
Unknown,
Vertical,
Colocated,
Reserved,
}
impl Av1ChromaSamplePosition {
fn from_raw(v: u8) -> Self {
match v {
0 => Self::Unknown,
1 => Self::Vertical,
2 => Self::Colocated,
_ => Self::Reserved,
}
}
}
#[derive(Debug, Clone)]
pub struct Av1SequenceHeader {
pub profile: u8,
pub still_picture: bool,
pub reduced_still_picture_header: bool,
pub operating_points_cnt: usize,
pub operating_point_idc: Vec<u16>,
pub max_frame_width: u32,
pub max_frame_height: u32,
pub frame_width_bits: u8,
pub frame_height_bits: u8,
pub frame_id_numbers_present: bool,
pub delta_frame_id_length: u8,
pub additional_frame_id_length: u8,
pub use_128x128_superblock: bool,
pub enable_filter_intra: bool,
pub enable_intra_edge_filter: bool,
pub enable_interintra_compound: bool,
pub enable_masked_compound: bool,
pub enable_warped_motion: bool,
pub enable_dual_filter: bool,
pub enable_order_hint: bool,
pub enable_jnt_comp: bool,
pub enable_ref_frame_mvs: bool,
pub seq_force_screen_content_tools: u8,
pub seq_force_integer_mv: u8,
pub order_hint_bits: u8,
pub enable_superres: bool,
pub enable_cdef: bool,
pub enable_restoration: bool,
pub bit_depth: u8,
pub monochrome: bool,
pub color_description_present: bool,
pub color_primaries: Av1ColorPrimaries,
pub subsampling_x: u8,
pub subsampling_y: u8,
pub chroma_sample_position: Av1ChromaSamplePosition,
pub separate_uv_delta_q: bool,
pub film_grain_params_present: bool,
}
impl Av1SequenceHeader {
pub fn sb_size(&self) -> usize {
if self.use_128x128_superblock { 128 } else { 64 }
}
pub fn num_planes(&self) -> usize {
if self.monochrome { 1 } else { 3 }
}
pub fn max_sample_value(&self) -> i32 {
(1i32 << self.bit_depth) - 1
}
}
pub fn parse_sequence_header(data: &[u8]) -> Result<Av1SequenceHeader, VideoError> {
let mut r = BitstreamReader::new(data);
let profile = r.read_bits(3)? as u8;
if profile > 2 {
return Err(VideoError::Codec(format!(
"AV1: invalid seq_profile {profile}"
)));
}
let still_picture = r.read_bit()? != 0;
let reduced_still_picture_header = r.read_bit()? != 0;
let mut operating_points_cnt = 1usize;
let mut operating_point_idc = vec![0u16];
if reduced_still_picture_header {
let _seq_level_idx = r.read_bits(5)?;
} else {
let timing_info_present = r.read_bit()? != 0;
if timing_info_present {
let _num_units_in_display_tick = r.read_bits(32)?;
let _time_scale = r.read_bits(32)?;
let equal_picture_interval = r.read_bit()? != 0;
if equal_picture_interval {
let _num_ticks_per_picture_minus1 = read_uvlc(&mut r)?;
}
let decoder_model_info_present = r.read_bit()? != 0;
if decoder_model_info_present {
let _buffer_delay_length_minus1 = r.read_bits(5)?;
let _num_units_in_decoding_tick = r.read_bits(32)?;
let _buffer_removal_time_length_minus1 = r.read_bits(5)?;
let _frame_presentation_time_length_minus1 = r.read_bits(5)?;
}
}
let initial_display_delay_present = r.read_bit()? != 0;
operating_points_cnt = (r.read_bits(5)? as usize) + 1;
operating_point_idc = Vec::with_capacity(operating_points_cnt);
for _i in 0..operating_points_cnt {
let idc = r.read_bits(12)? as u16;
operating_point_idc.push(idc);
let _seq_level_idx = r.read_bits(5)?;
if _seq_level_idx > 7 {
let _seq_tier = r.read_bit()?;
}
if timing_info_present {
let timing_info_present_flag = r.read_bit()? != 0;
if timing_info_present_flag {
}
}
if initial_display_delay_present {
let has_delay = r.read_bit()? != 0;
if has_delay {
let _initial_display_delay_minus1 = r.read_bits(4)?;
}
}
}
}
let frame_width_bits = (r.read_bits(4)? as u8) + 1;
let frame_height_bits = (r.read_bits(4)? as u8) + 1;
let max_frame_width = r.read_bits(frame_width_bits)? + 1;
let max_frame_height = r.read_bits(frame_height_bits)? + 1;
let mut frame_id_numbers_present = false;
let mut delta_frame_id_length = 0u8;
let mut additional_frame_id_length = 0u8;
if !reduced_still_picture_header {
frame_id_numbers_present = r.read_bit()? != 0;
if frame_id_numbers_present {
delta_frame_id_length = (r.read_bits(4)? as u8) + 2;
additional_frame_id_length = (r.read_bits(3)? as u8) + 1;
}
}
let use_128x128_superblock = r.read_bit()? != 0;
let enable_filter_intra = r.read_bit()? != 0;
let enable_intra_edge_filter = r.read_bit()? != 0;
let mut enable_interintra_compound = false;
let mut enable_masked_compound = false;
let mut enable_warped_motion = false;
let mut enable_dual_filter = false;
let mut enable_order_hint = false;
let mut enable_jnt_comp = false;
let mut enable_ref_frame_mvs = false;
let mut seq_force_screen_content_tools = 2u8; let mut seq_force_integer_mv = 2u8; let mut order_hint_bits = 0u8;
if !reduced_still_picture_header {
enable_interintra_compound = r.read_bit()? != 0;
enable_masked_compound = r.read_bit()? != 0;
enable_warped_motion = r.read_bit()? != 0;
enable_dual_filter = r.read_bit()? != 0;
enable_order_hint = r.read_bit()? != 0;
if enable_order_hint {
enable_jnt_comp = r.read_bit()? != 0;
enable_ref_frame_mvs = r.read_bit()? != 0;
}
let seq_choose_screen_content_tools = r.read_bit()? != 0;
if seq_choose_screen_content_tools {
seq_force_screen_content_tools = 2; } else {
seq_force_screen_content_tools = r.read_bit()?;
}
if seq_force_screen_content_tools > 0 {
let seq_choose_integer_mv = r.read_bit()? != 0;
if seq_choose_integer_mv {
seq_force_integer_mv = 2; } else {
seq_force_integer_mv = r.read_bit()?;
}
}
if enable_order_hint {
order_hint_bits = (r.read_bits(3)? as u8) + 1;
}
}
let enable_superres = r.read_bit()? != 0;
let enable_cdef = r.read_bit()? != 0;
let enable_restoration = r.read_bit()? != 0;
let high_bitdepth = r.read_bit()? != 0;
let mut bit_depth: u8 = if high_bitdepth { 10 } else { 8 };
if profile == 2 && high_bitdepth {
let twelve_bit = r.read_bit()? != 0;
if twelve_bit {
bit_depth = 12;
}
}
let monochrome = if profile != 1 {
r.read_bit()? != 0
} else {
false
};
let color_description_present = r.read_bit()? != 0;
let mut color_primaries = Av1ColorPrimaries::Unspecified;
let mut _transfer_characteristics = 2u8; let mut _matrix_coefficients = 2u8;
if color_description_present {
color_primaries = Av1ColorPrimaries::from_raw(r.read_bits(8)? as u8);
_transfer_characteristics = r.read_bits(8)? as u8;
_matrix_coefficients = r.read_bits(8)? as u8;
}
#[allow(unused_assignments)]
let mut subsampling_x = 0u8;
#[allow(unused_assignments)]
let mut subsampling_y = 0u8;
let mut chroma_sample_position = Av1ChromaSamplePosition::Unknown;
let mut separate_uv_delta_q = false;
if monochrome {
let _color_range = r.read_bit()?;
subsampling_x = 1;
subsampling_y = 1;
} else if matches!(color_primaries, Av1ColorPrimaries::Bt709)
&& _transfer_characteristics == 13
&& _matrix_coefficients == 0
{
subsampling_x = 0;
subsampling_y = 0;
} else {
let _color_range = r.read_bit()?;
if profile == 0 {
subsampling_x = 1;
subsampling_y = 1;
} else if profile == 1 {
subsampling_x = 0;
subsampling_y = 0;
} else {
if bit_depth == 12 {
subsampling_x = r.read_bit()?;
if subsampling_x != 0 {
subsampling_y = r.read_bit()?;
}
} else {
subsampling_x = 1;
subsampling_y = 0;
}
}
if subsampling_x != 0 && subsampling_y != 0 {
chroma_sample_position = Av1ChromaSamplePosition::from_raw(r.read_bits(2)? as u8);
}
}
if !monochrome {
separate_uv_delta_q = r.read_bit()? != 0;
}
let film_grain_params_present = r.read_bit()? != 0;
Ok(Av1SequenceHeader {
profile,
still_picture,
reduced_still_picture_header,
operating_points_cnt,
operating_point_idc,
max_frame_width,
max_frame_height,
frame_width_bits,
frame_height_bits,
frame_id_numbers_present,
delta_frame_id_length,
additional_frame_id_length,
use_128x128_superblock,
enable_filter_intra,
enable_intra_edge_filter,
enable_interintra_compound,
enable_masked_compound,
enable_warped_motion,
enable_dual_filter,
enable_order_hint,
enable_jnt_comp,
enable_ref_frame_mvs,
seq_force_screen_content_tools,
seq_force_integer_mv,
order_hint_bits,
enable_superres,
enable_cdef,
enable_restoration,
bit_depth,
monochrome,
color_description_present,
color_primaries,
subsampling_x,
subsampling_y,
chroma_sample_position,
separate_uv_delta_q,
film_grain_params_present,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Av1FrameType {
KeyFrame,
InterFrame,
IntraOnlyFrame,
SwitchFrame,
}
impl Av1FrameType {
fn from_raw(v: u32) -> Result<Self, VideoError> {
match v {
0 => Ok(Self::KeyFrame),
1 => Ok(Self::InterFrame),
2 => Ok(Self::IntraOnlyFrame),
3 => Ok(Self::SwitchFrame),
_ => Err(VideoError::Codec(format!("AV1: invalid frame_type {v}"))),
}
}
pub fn is_intra(self) -> bool {
matches!(self, Self::KeyFrame | Self::IntraOnlyFrame)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Av1QuantizationParams {
pub base_q_idx: u16,
pub delta_q_y_dc: i8,
pub delta_q_u_dc: i8,
pub delta_q_u_ac: i8,
pub delta_q_v_dc: i8,
pub delta_q_v_ac: i8,
pub using_qmatrix: bool,
}
#[derive(Debug, Clone, Default)]
pub struct Av1TileInfo {
pub tile_cols: u32,
pub tile_rows: u32,
pub tile_col_widths: Vec<u32>,
pub tile_row_heights: Vec<u32>,
pub tile_count: u32,
pub context_update_tile_id: u32,
pub tile_size_bytes: u8,
}
#[derive(Debug, Clone, Default)]
pub struct Av1SegmentationParams {
pub enabled: bool,
pub update_map: bool,
pub update_data: bool,
pub temporal_update: bool,
pub feature_enabled: [[bool; 8]; 8],
pub feature_data: [[i16; 8]; 8],
}
#[derive(Debug, Clone, Default)]
pub struct Av1LoopFilterParams {
pub level: [u8; 4],
pub sharpness: u8,
pub delta_enabled: bool,
pub delta_update: bool,
pub ref_deltas: [i8; 8],
pub mode_deltas: [i8; 2],
}
#[derive(Debug, Clone, Default)]
pub struct Av1CdefParams {
pub damping: u8,
pub bits: u8,
pub strengths: Vec<(u8, u8, u8, u8)>,
}
#[derive(Debug, Clone)]
pub struct Av1FrameHeader {
pub frame_type: Av1FrameType,
pub show_frame: bool,
pub showable_frame: bool,
pub error_resilient: bool,
pub disable_cdf_update: bool,
pub allow_screen_content_tools: bool,
pub force_integer_mv: bool,
pub current_frame_id: u32,
pub frame_width: u32,
pub frame_height: u32,
pub use_superres: bool,
pub superres_denom: u8,
pub primary_ref_frame: u8,
pub refresh_frame_flags: u8,
pub ref_frame_idx: [u8; 7],
pub order_hint: u32,
pub tile_info: Av1TileInfo,
pub quantization_params: Av1QuantizationParams,
pub segmentation_params: Av1SegmentationParams,
pub loop_filter_params: Av1LoopFilterParams,
pub cdef_params: Av1CdefParams,
pub reduced_tx_set: bool,
pub tx_mode: u8,
pub allow_ref_frame_mvs: bool,
pub show_existing_frame: bool,
pub frame_to_show_map_idx: u8,
}
pub fn parse_frame_header(
data: &[u8],
seq: &Av1SequenceHeader,
) -> Result<Av1FrameHeader, VideoError> {
let mut r = BitstreamReader::new(data);
#[allow(unused_assignments)]
let mut show_existing_frame = false;
let mut frame_to_show_map_idx = 0u8;
if seq.reduced_still_picture_header {
} else {
show_existing_frame = r.read_bit()? != 0;
if show_existing_frame {
frame_to_show_map_idx = r.read_bits(3)? as u8;
return Ok(Av1FrameHeader {
frame_type: Av1FrameType::InterFrame,
show_frame: true,
showable_frame: false,
error_resilient: false,
disable_cdf_update: false,
allow_screen_content_tools: false,
force_integer_mv: false,
current_frame_id: 0,
frame_width: seq.max_frame_width,
frame_height: seq.max_frame_height,
use_superres: false,
superres_denom: 8,
primary_ref_frame: 7,
refresh_frame_flags: 0,
ref_frame_idx: [0; 7],
order_hint: 0,
tile_info: Av1TileInfo::default(),
quantization_params: Av1QuantizationParams::default(),
segmentation_params: Av1SegmentationParams::default(),
loop_filter_params: Av1LoopFilterParams::default(),
cdef_params: Av1CdefParams::default(),
reduced_tx_set: false,
tx_mode: 0,
allow_ref_frame_mvs: false,
show_existing_frame: true,
frame_to_show_map_idx,
});
}
}
let frame_type = if seq.reduced_still_picture_header {
Av1FrameType::KeyFrame
} else {
Av1FrameType::from_raw(r.read_bits(2)?)?
};
let show_frame = if seq.reduced_still_picture_header {
true
} else {
r.read_bit()? != 0
};
let showable_frame = if show_frame
&& !matches!(frame_type, Av1FrameType::KeyFrame)
&& !seq.reduced_still_picture_header
{
r.read_bit()? != 0
} else {
false
};
let error_resilient = if matches!(frame_type, Av1FrameType::SwitchFrame)
|| (matches!(frame_type, Av1FrameType::KeyFrame) && show_frame)
{
true
} else if !seq.reduced_still_picture_header {
r.read_bit()? != 0
} else {
false
};
let disable_cdf_update = if !seq.reduced_still_picture_header {
r.read_bit()? != 0
} else {
false
};
let allow_screen_content_tools = if seq.seq_force_screen_content_tools == 2 {
if seq.reduced_still_picture_header {
false
} else {
r.read_bit()? != 0
}
} else {
seq.seq_force_screen_content_tools != 0
};
let force_integer_mv = if allow_screen_content_tools {
if seq.seq_force_integer_mv == 2 {
if seq.reduced_still_picture_header {
false
} else {
r.read_bit()? != 0
}
} else {
seq.seq_force_integer_mv != 0
}
} else {
false
};
let mut current_frame_id = 0u32;
if seq.frame_id_numbers_present {
let id_len = seq.delta_frame_id_length + seq.additional_frame_id_length;
current_frame_id = r.read_bits(id_len)?;
}
let frame_size_override = if matches!(frame_type, Av1FrameType::SwitchFrame) {
true
} else if seq.reduced_still_picture_header {
false
} else {
r.read_bit()? != 0
};
let order_hint = if seq.reduced_still_picture_header {
0
} else if seq.order_hint_bits > 0 {
r.read_bits(seq.order_hint_bits)?
} else {
0
};
let primary_ref_frame = if frame_type.is_intra() || error_resilient {
7 } else if !seq.reduced_still_picture_header {
r.read_bits(3)? as u8
} else {
7
};
let refresh_frame_flags = if matches!(frame_type, Av1FrameType::SwitchFrame)
|| (matches!(frame_type, Av1FrameType::KeyFrame) && show_frame)
{
0xFF } else if !seq.reduced_still_picture_header {
r.read_bits(8)? as u8
} else {
0xFF
};
let (frame_width, frame_height) = if frame_size_override {
let w = r.read_bits(seq.frame_width_bits)? + 1;
let h = r.read_bits(seq.frame_height_bits)? + 1;
(w, h)
} else {
(seq.max_frame_width, seq.max_frame_height)
};
let (use_superres, superres_denom) = if seq.enable_superres {
let use_sr = r.read_bit()? != 0;
if use_sr {
let denom = (r.read_bits(3)? as u8) + 9; (true, denom)
} else {
(false, 8)
}
} else {
(false, 8)
};
let mut ref_frame_idx = [0u8; 7];
if !frame_type.is_intra() {
for slot in ref_frame_idx.iter_mut() {
*slot = r.read_bits(3)? as u8;
}
}
let tile_info = parse_tile_info(&mut r, seq, frame_width, frame_height)?;
let quantization_params = parse_quantization_params(&mut r, seq)?;
let segmentation_params = parse_segmentation_params(&mut r)?;
let loop_filter_params = parse_loop_filter_params(&mut r, seq, frame_type)?;
let cdef_params = parse_cdef_params(&mut r, seq, frame_type)?;
let tx_mode = if r.bits_remaining() >= 2 {
if r.read_bit()? != 0 {
2 } else {
1 }
} else {
2
};
let reduced_tx_set = if r.bits_remaining() >= 1 {
r.read_bit()? != 0
} else {
false
};
let allow_ref_frame_mvs = if !frame_type.is_intra()
&& seq.enable_ref_frame_mvs
&& !error_resilient
&& r.bits_remaining() >= 1
{
r.read_bit()? != 0
} else {
false
};
Ok(Av1FrameHeader {
frame_type,
show_frame,
showable_frame,
error_resilient,
disable_cdf_update,
allow_screen_content_tools,
force_integer_mv,
current_frame_id,
frame_width,
frame_height,
use_superres,
superres_denom,
primary_ref_frame,
refresh_frame_flags,
ref_frame_idx,
order_hint,
tile_info,
quantization_params,
segmentation_params,
loop_filter_params,
cdef_params,
reduced_tx_set,
tx_mode,
allow_ref_frame_mvs,
show_existing_frame: false,
frame_to_show_map_idx,
})
}
fn parse_tile_info(
r: &mut BitstreamReader<'_>,
seq: &Av1SequenceHeader,
frame_width: u32,
frame_height: u32,
) -> Result<Av1TileInfo, VideoError> {
let sb_size = seq.sb_size() as u32;
let sb_cols = frame_width.div_ceil(sb_size);
let sb_rows = frame_height.div_ceil(sb_size);
let min_log2_tile_cols = tile_log2(1, sb_cols);
let max_log2_tile_cols = tile_log2(1, sb_cols.min(64));
let max_log2_tile_rows = tile_log2(1, sb_rows.min(64));
let uniform_tile_spacing = r.read_bit()? != 0;
let mut tile_col_widths = Vec::new();
let mut tile_row_heights = Vec::new();
if uniform_tile_spacing {
let mut tile_cols_log2 = min_log2_tile_cols;
while tile_cols_log2 < max_log2_tile_cols {
if r.read_bit()? != 0 {
tile_cols_log2 += 1;
} else {
break;
}
}
let tile_width_sb = (sb_cols + (1 << tile_cols_log2) - 1) >> tile_cols_log2;
let mut remaining = sb_cols;
while remaining > 0 {
let w = tile_width_sb.min(remaining);
tile_col_widths.push(w);
remaining = remaining.saturating_sub(w);
}
let min_log2_tile_rows = if (tile_col_widths.len() as u32) > 1 {
tile_log2(1, sb_rows)
} else {
0
};
let _ = min_log2_tile_rows;
let mut tile_rows_log2 = 0u32;
while tile_rows_log2 < max_log2_tile_rows {
if r.read_bit()? != 0 {
tile_rows_log2 += 1;
} else {
break;
}
}
let tile_height_sb = (sb_rows + (1 << tile_rows_log2) - 1) >> tile_rows_log2;
let mut remaining = sb_rows;
while remaining > 0 {
let h = tile_height_sb.min(remaining);
tile_row_heights.push(h);
remaining = remaining.saturating_sub(h);
}
} else {
let mut remaining = sb_cols;
while remaining > 0 {
let max_w = remaining.min(sb_cols);
let w = if max_w > 1 { read_ns(r, max_w)? + 1 } else { 1 };
tile_col_widths.push(w);
remaining = remaining.saturating_sub(w);
}
let mut remaining = sb_rows;
while remaining > 0 {
let max_h = remaining.min(sb_rows);
let h = if max_h > 1 { read_ns(r, max_h)? + 1 } else { 1 };
tile_row_heights.push(h);
remaining = remaining.saturating_sub(h);
}
}
let tile_cols = tile_col_widths.len() as u32;
let tile_rows = tile_row_heights.len() as u32;
let tile_count = tile_cols * tile_rows;
let mut context_update_tile_id = 0u32;
let mut tile_size_bytes = 4u8;
if tile_count > 1 {
let tile_bits = tile_log2(1, tile_count);
if tile_bits > 0 {
context_update_tile_id = r.read_bits(tile_bits as u8)?;
}
tile_size_bytes = (r.read_bits(2)? as u8) + 1;
}
Ok(Av1TileInfo {
tile_cols,
tile_rows,
tile_col_widths,
tile_row_heights,
tile_count,
context_update_tile_id,
tile_size_bytes,
})
}
fn parse_quantization_params(
r: &mut BitstreamReader<'_>,
seq: &Av1SequenceHeader,
) -> Result<Av1QuantizationParams, VideoError> {
let base_q_idx = r.read_bits(8)? as u16;
let delta_q_y_dc = read_delta_q(r)?;
let (delta_q_u_dc, delta_q_u_ac, delta_q_v_dc, delta_q_v_ac) = if !seq.monochrome {
let diff_uv_delta = if seq.separate_uv_delta_q {
r.read_bit()? != 0
} else {
false
};
let u_dc = read_delta_q(r)?;
let u_ac = read_delta_q(r)?;
let (v_dc, v_ac) = if diff_uv_delta {
(read_delta_q(r)?, read_delta_q(r)?)
} else {
(u_dc, u_ac)
};
(u_dc, u_ac, v_dc, v_ac)
} else {
(0, 0, 0, 0)
};
let using_qmatrix = r.read_bit()? != 0;
if using_qmatrix {
let _qm_y = r.read_bits(4)?;
let _qm_u = r.read_bits(4)?;
if !seq.separate_uv_delta_q {
let _qm_v = _qm_u;
} else {
let _qm_v = r.read_bits(4)?;
}
}
Ok(Av1QuantizationParams {
base_q_idx,
delta_q_y_dc,
delta_q_u_dc,
delta_q_u_ac,
delta_q_v_dc,
delta_q_v_ac,
using_qmatrix,
})
}
fn read_delta_q(r: &mut BitstreamReader<'_>) -> Result<i8, VideoError> {
let present = r.read_bit()? != 0;
if present {
let val = r.read_bits(7)?;
let sign = (val >> 6) & 1;
let mag = val & 0x3F;
Ok(if sign != 0 { -(mag as i8) } else { mag as i8 })
} else {
Ok(0)
}
}
fn parse_segmentation_params(
r: &mut BitstreamReader<'_>,
) -> Result<Av1SegmentationParams, VideoError> {
let enabled = r.read_bit()? != 0;
if !enabled {
return Ok(Av1SegmentationParams::default());
}
let update_map = r.read_bit()? != 0;
let temporal_update = if update_map {
r.read_bit()? != 0
} else {
false
};
let update_data = r.read_bit()? != 0;
let mut feature_enabled = [[false; 8]; 8];
let mut feature_data = [[0i16; 8]; 8];
const SEG_FEATURE_BITS: [u8; 8] = [8, 6, 6, 6, 6, 3, 0, 0];
const SEG_FEATURE_SIGNED: [bool; 8] = [true, true, true, true, true, false, false, false];
if update_data {
for seg in 0..8 {
for feat in 0..8 {
let feat_enabled = r.read_bit()? != 0;
feature_enabled[seg][feat] = feat_enabled;
if feat_enabled {
let bits = SEG_FEATURE_BITS[feat];
if bits > 0 {
let val = r.read_bits(bits)? as i16;
if SEG_FEATURE_SIGNED[feat] {
let sign = r.read_bit()?;
feature_data[seg][feat] = if sign != 0 { -val } else { val };
} else {
feature_data[seg][feat] = val;
}
} else {
feature_data[seg][feat] = 0;
}
}
}
}
}
Ok(Av1SegmentationParams {
enabled,
update_map,
update_data,
temporal_update,
feature_enabled,
feature_data,
})
}
fn parse_loop_filter_params(
r: &mut BitstreamReader<'_>,
seq: &Av1SequenceHeader,
frame_type: Av1FrameType,
) -> Result<Av1LoopFilterParams, VideoError> {
if frame_type == Av1FrameType::KeyFrame && !seq.reduced_still_picture_header {
}
let level0 = r.read_bits(6)? as u8;
let level1 = r.read_bits(6)? as u8;
let (level2, level3) = if !seq.monochrome && (level0 != 0 || level1 != 0) {
(r.read_bits(6)? as u8, r.read_bits(6)? as u8)
} else {
(0, 0)
};
let sharpness = r.read_bits(3)? as u8;
let delta_enabled = r.read_bit()? != 0;
let mut ref_deltas = [1i8, 0, 0, 0, 0, -1, -1, -1]; let mut mode_deltas = [0i8; 2];
let mut delta_update = false;
if delta_enabled {
delta_update = r.read_bit()? != 0;
if delta_update {
for delta in ref_deltas.iter_mut() {
let update = r.read_bit()? != 0;
if update {
let val = r.read_bits(7)?;
let sign = (val >> 6) & 1;
let mag = (val & 0x3F) as i8;
*delta = if sign != 0 { -mag } else { mag };
}
}
for delta in mode_deltas.iter_mut() {
let update = r.read_bit()? != 0;
if update {
let val = r.read_bits(7)?;
let sign = (val >> 6) & 1;
let mag = (val & 0x3F) as i8;
*delta = if sign != 0 { -mag } else { mag };
}
}
}
}
Ok(Av1LoopFilterParams {
level: [level0, level1, level2, level3],
sharpness,
delta_enabled,
delta_update,
ref_deltas,
mode_deltas,
})
}
fn parse_cdef_params(
r: &mut BitstreamReader<'_>,
seq: &Av1SequenceHeader,
frame_type: Av1FrameType,
) -> Result<Av1CdefParams, VideoError> {
if !seq.enable_cdef {
return Ok(Av1CdefParams::default());
}
let _ = frame_type;
let damping = (r.read_bits(2)? as u8) + 3;
let bits = r.read_bits(2)? as u8;
let num_strengths = 1u32 << bits;
let mut strengths = Vec::with_capacity(num_strengths as usize);
for _ in 0..num_strengths {
let y_pri = r.read_bits(4)? as u8;
let y_sec = r.read_bits(2)? as u8;
let (uv_pri, uv_sec) = if seq.num_planes() > 1 {
(r.read_bits(4)? as u8, r.read_bits(2)? as u8)
} else {
(0, 0)
};
strengths.push((y_pri, y_sec, uv_pri, uv_sec));
}
Ok(Av1CdefParams {
damping,
bits,
strengths,
})
}
fn read_uvlc(r: &mut BitstreamReader<'_>) -> Result<u32, VideoError> {
let mut leading_zeros = 0u32;
loop {
let bit = r.read_bit()?;
if bit != 0 {
break;
}
leading_zeros += 1;
if leading_zeros > 32 {
return Err(VideoError::Codec("AV1: UVLC overflow".into()));
}
}
if leading_zeros == 0 {
return Ok(0);
}
let value = r.read_bits(leading_zeros as u8)?;
Ok(value + (1 << leading_zeros) - 1)
}
fn read_ns(r: &mut BitstreamReader<'_>, n: u32) -> Result<u32, VideoError> {
if n <= 1 {
return Ok(0);
}
let w = 32 - (n - 1).leading_zeros(); let m = (1u32 << w) - n;
let v = r.read_bits(w as u8 - 1)?;
if v < m {
Ok(v)
} else {
let extra = r.read_bit()? as u32;
Ok((v << 1) - m + extra)
}
}
fn tile_log2(blk_size: u32, target: u32) -> u32 {
let mut k = 0u32;
let mut val = blk_size;
while val < target {
k += 1;
val = blk_size << k;
}
k
}
#[derive(Debug, Clone)]
pub struct Av1Obu {
pub header: Av1ObuHeader,
pub payload_offset: usize,
pub payload_len: usize,
}
pub fn parse_obus(data: &[u8]) -> Result<Vec<Av1Obu>, VideoError> {
let mut obus = Vec::new();
let mut pos = 0;
while pos < data.len() {
let hdr = parse_obu_header(&data[pos..])?;
let after_header = pos + hdr.header_len;
if !hdr.has_size {
let payload_len = data.len() - after_header;
obus.push(Av1Obu {
header: hdr,
payload_offset: after_header,
payload_len,
});
break;
}
if after_header >= data.len() {
return Err(VideoError::Codec(
"AV1: truncated OBU (no size field)".into(),
));
}
let (obu_size, size_bytes) = read_leb128(&data[after_header..])?;
let payload_offset = after_header + size_bytes;
let payload_len = obu_size as usize;
if payload_offset + payload_len > data.len() {
return Err(VideoError::Codec(format!(
"AV1: OBU payload extends past data ({} + {} > {})",
payload_offset,
payload_len,
data.len()
)));
}
obus.push(Av1Obu {
header: hdr,
payload_offset,
payload_len,
});
pos = payload_offset + payload_len;
}
Ok(obus)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn leb128_round_trip() {
for &val in &[
0u64,
1,
127,
128,
255,
256,
16383,
16384,
0x0FFF_FFFF,
0xFFFF_FFFF,
] {
let encoded = write_leb128(val);
let (decoded, consumed) = read_leb128(&encoded).expect("decode should succeed");
assert_eq!(decoded, val, "round-trip failed for {val}");
assert_eq!(consumed, encoded.len());
}
}
#[test]
fn leb128_known_values() {
assert_eq!(read_leb128(&[0x00]).unwrap(), (0, 1));
assert_eq!(read_leb128(&[0x01]).unwrap(), (1, 1));
assert_eq!(read_leb128(&[0x80, 0x01]).unwrap(), (128, 2));
assert_eq!(read_leb128(&[0xAC, 0x02]).unwrap(), (300, 2));
}
#[test]
fn leb128_empty_data_error() {
assert!(read_leb128(&[]).is_err());
}
#[test]
fn obu_header_sequence_header() {
let data = [0x0A, 0x05]; let hdr = parse_obu_header(&data).expect("should parse");
assert_eq!(hdr.obu_type, Av1ObuType::SequenceHeader);
assert!(hdr.has_size);
assert!(!hdr.has_extension);
assert_eq!(hdr.header_len, 1);
}
#[test]
fn obu_header_with_extension() {
let data = [0x1E, 0x48];
let hdr = parse_obu_header(&data).expect("should parse");
assert_eq!(hdr.obu_type, Av1ObuType::FrameHeader);
assert!(hdr.has_size);
assert!(hdr.has_extension);
assert_eq!(hdr.extension.temporal_id, 2);
assert_eq!(hdr.extension.spatial_id, 1);
assert_eq!(hdr.header_len, 2);
}
#[test]
fn obu_header_forbidden_bit_error() {
assert!(parse_obu_header(&[0x8A]).is_err());
}
#[test]
fn obu_header_temporal_delimiter() {
let hdr = parse_obu_header(&[0x12]).expect("should parse");
assert_eq!(hdr.obu_type, Av1ObuType::TemporalDelimiter);
assert!(hdr.has_size);
}
#[test]
fn obu_header_padding() {
let hdr = parse_obu_header(&[0x7A]).expect("should parse");
assert_eq!(hdr.obu_type, Av1ObuType::Padding);
}
#[test]
fn parse_obus_multiple() {
let data = vec![0x12, 0x00, 0x7A, 0x03, 0x00, 0x00, 0x00];
let obus = parse_obus(&data).expect("should parse");
assert_eq!(obus.len(), 2);
assert_eq!(obus[0].header.obu_type, Av1ObuType::TemporalDelimiter);
assert_eq!(obus[0].payload_len, 0);
assert_eq!(obus[1].header.obu_type, Av1ObuType::Padding);
assert_eq!(obus[1].payload_len, 3);
}
#[test]
fn sequence_header_basic() {
let mut bits = Vec::new();
push_bits(&mut bits, 0b000, 3);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 5);
push_bits(&mut bits, 0, 12);
push_bits(&mut bits, 8, 5);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 10, 4);
push_bits(&mut bits, 10, 4);
push_bits(&mut bits, 1919, 11);
push_bits(&mut bits, 1079, 11);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 1, 1);
push_bits(&mut bits, 1, 1);
push_bits(&mut bits, 1, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 1, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 1, 1);
push_bits(&mut bits, 1, 1);
push_bits(&mut bits, 6, 3);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 1, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 2);
push_bits(&mut bits, 0, 1);
push_bits(&mut bits, 0, 1);
let data = bits_to_bytes(&bits);
let seq = parse_sequence_header(&data).expect("should parse sequence header");
assert_eq!(seq.profile, 0);
assert_eq!(seq.max_frame_width, 1920);
assert_eq!(seq.max_frame_height, 1080);
assert_eq!(seq.bit_depth, 8);
assert!(!seq.monochrome);
assert_eq!(seq.subsampling_x, 1);
assert_eq!(seq.subsampling_y, 1);
assert!(seq.use_128x128_superblock);
assert!(seq.enable_cdef);
assert!(!seq.film_grain_params_present);
assert_eq!(seq.order_hint_bits, 7);
}
fn push_bits(bits: &mut Vec<u8>, value: u32, count: u8) {
for i in (0..count).rev() {
bits.push(((value >> i) & 1) as u8);
}
}
fn bits_to_bytes(bits: &[u8]) -> Vec<u8> {
let mut bytes = Vec::with_capacity(bits.len().div_ceil(8));
for chunk in bits.chunks(8) {
let mut byte = 0u8;
for (i, &bit) in chunk.iter().enumerate() {
byte |= bit << (7 - i);
}
bytes.push(byte);
}
bytes
}
}