use std::borrow::Cow;
use std::rc::Rc;
use crate::codec::av1::helpers;
use crate::codec::av1::reader::Reader;
pub const TOTAL_REFS_PER_FRAME: usize = 8;
pub const NUM_REF_FRAMES: usize = 8;
pub const REFS_PER_FRAME: usize = 7;
pub const MAX_SEGMENTS: usize = 8;
pub const SEG_LVL_ALT_Q: usize = 0;
pub const SEG_LVL_ALT_LF_Y_V: usize = 1;
pub const SEG_LVL_REF_FRAME: usize = 5;
pub const SEG_LVL_SKIP: usize = 6;
pub const SEG_LVL_GLOBAL_MV: usize = 7;
pub const SEG_LVL_MAX: usize = 8;
pub const MAX_TILE_COLS: usize = 64;
pub const MAX_TILE_ROWS: usize = 64;
pub const CDEF_MAX: usize = 1 << 3;
pub const MAX_NUM_PLANES: usize = 3;
pub const MAX_NUM_Y_POINTS: usize = 16;
pub const MAX_NUM_CB_POINTS: usize = 16;
pub const MAX_NUM_CR_POINTS: usize = 16;
pub const MAX_NUM_POS_LUMA: usize = 25;
pub const MAX_NUM_SPATIAL_LAYERS: usize = 4;
pub const MAX_NUM_TEMPORAL_LAYERS: usize = 8;
pub const MAX_NUM_OPERATING_POINTS: usize = MAX_NUM_SPATIAL_LAYERS * MAX_NUM_TEMPORAL_LAYERS;
pub const SELECT_SCREEN_CONTENT_TOOLS: usize = 2;
pub const SELECT_INTEGER_MV: usize = 2;
pub const PRIMARY_REF_NONE: u32 = 7;
pub const SUPERRES_DENOM_BITS: usize = 3;
pub const SUPERRES_DENOM_MIN: usize = 9;
pub const SUPERRES_NUM: usize = 8;
pub const MAX_TILE_WIDTH: u32 = 4096;
pub const MAX_TILE_HEIGHT: u32 = 2304;
pub const MAX_TILE_AREA: u32 = MAX_TILE_WIDTH * MAX_TILE_HEIGHT;
pub const RESTORATION_TILESIZE_MAX: u16 = 256;
pub const WARPEDMODEL_PREC_BITS: u32 = 16;
pub const WARP_PARAM_REDUCE_BITS: u32 = 6;
pub const GM_ABS_ALPHA_BITS: u32 = 12;
pub const GM_ALPHA_PREC_BITS: u32 = 15;
pub const GM_ABS_TRANS_ONLY_BITS: u32 = 9;
pub const GM_TRANS_ONLY_PREC_BITS: u32 = 3;
pub const GM_ABS_TRANS_BITS: u32 = 12;
pub const GM_TRANS_PREC_BITS: u32 = 6;
pub const FEATURE_BITS: [u8; SEG_LVL_MAX] = [8, 6, 6, 6, 6, 3, 0, 0];
pub const FEATURE_SIGNED: [bool; SEG_LVL_MAX] = [true, true, true, true, true, false, false, false];
pub const FEATURE_MAX: [i32; SEG_LVL_MAX] = [255, 63, 63, 63, 63, 7, 0, 0];
pub enum ObuAction<'a> {
Process(Obu<'a>),
Drop(u32),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum ObuType {
#[default]
Reserved = 0,
SequenceHeader = 1,
TemporalDelimiter = 2,
FrameHeader = 3,
TileGroup = 4,
Metadata = 5,
Frame = 6,
RedundantFrameHeader = 7,
TileList = 8,
Reserved2 = 9,
Reserved3 = 10,
Reserved4 = 11,
Reserved5 = 12,
Reserved6 = 13,
Reserved7 = 14,
Padding = 15,
}
impl TryFrom<u32> for ObuType {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(ObuType::Reserved),
1 => Ok(ObuType::SequenceHeader),
2 => Ok(ObuType::TemporalDelimiter),
3 => Ok(ObuType::FrameHeader),
4 => Ok(ObuType::TileGroup),
5 => Ok(ObuType::Metadata),
6 => Ok(ObuType::Frame),
7 => Ok(ObuType::RedundantFrameHeader),
8 => Ok(ObuType::TileList),
9 => Ok(ObuType::Reserved2),
10 => Ok(ObuType::Reserved3),
11 => Ok(ObuType::Reserved4),
12 => Ok(ObuType::Reserved5),
13 => Ok(ObuType::Reserved6),
14 => Ok(ObuType::Reserved7),
15 => Ok(ObuType::Padding),
_ => Err(format!("Invalid ObuType {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum Profile {
#[default]
Profile0 = 0,
Profile1 = 1,
Profile2 = 2,
}
impl TryFrom<u32> for Profile {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Profile::Profile0),
1 => Ok(Profile::Profile1),
2 => Ok(Profile::Profile2),
_ => Err(format!("Invalid Profile {}", value)),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ObuHeader {
pub obu_type: ObuType,
pub extension_flag: bool,
pub has_size_field: bool,
pub temporal_id: u32,
pub spatial_id: u32,
}
impl ObuHeader {
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
if self.extension_flag {
2
} else {
1
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Obu<'a> {
pub header: ObuHeader,
pub bytes_used: usize,
data: Cow<'a, [u8]>,
}
impl<'a> AsRef<[u8]> for Obu<'a> {
fn as_ref(&self) -> &[u8] {
self.data.as_ref()
}
}
pub enum ParsedObu<'a> {
Reserved,
SequenceHeader(Rc<SequenceHeaderObu>),
TemporalDelimiter,
FrameHeader(FrameHeaderObu),
TileGroup(TileGroupObu<'a>),
Metadata,
Frame(FrameObu<'a>),
RedundantFrameHeader,
TileList,
Reserved2,
Reserved3,
Reserved4,
Reserved5,
Reserved6,
Reserved7,
Padding,
}
impl<'a> ParsedObu<'a> {
pub fn obu_type(&self) -> ObuType {
match self {
ParsedObu::Reserved => ObuType::Reserved,
ParsedObu::SequenceHeader(_) => ObuType::SequenceHeader,
ParsedObu::TemporalDelimiter => ObuType::TemporalDelimiter,
ParsedObu::FrameHeader(_) => ObuType::FrameHeader,
ParsedObu::TileGroup(_) => ObuType::TileGroup,
ParsedObu::Metadata => ObuType::Metadata,
ParsedObu::Frame(_) => ObuType::Frame,
ParsedObu::RedundantFrameHeader => ObuType::RedundantFrameHeader,
ParsedObu::TileList => ObuType::TileList,
ParsedObu::Reserved2 => ObuType::Reserved2,
ParsedObu::Reserved3 => ObuType::Reserved3,
ParsedObu::Reserved4 => ObuType::Reserved4,
ParsedObu::Reserved5 => ObuType::Reserved5,
ParsedObu::Reserved6 => ObuType::Reserved6,
ParsedObu::Reserved7 => ObuType::Reserved7,
ParsedObu::Padding => ObuType::Padding,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Tile {
pub tile_offset: u32,
pub tile_size: u32,
pub tile_row: u32,
pub tile_col: u32,
pub mi_row_start: u32,
pub mi_row_end: u32,
pub mi_col_start: u32,
pub mi_col_end: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TileGroupObu<'a> {
pub obu: Obu<'a>,
pub tile_start_and_end_present_flag: bool,
pub tg_start: u32,
pub tg_end: u32,
pub tiles: Vec<Tile>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct OperatingPoint {
pub seq_level_idx: u8,
pub seq_tier: u8,
pub idc: u16,
pub decoder_model_present_for_this_op: bool,
pub decoder_buffer_delay: u32,
pub encoder_buffer_delay: u32,
pub low_delay_mode_flag: bool,
pub initial_display_delay_present_for_this_op: bool,
pub initial_display_delay_minus_1: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TimingInfo {
pub num_units_in_display_tick: u32,
pub time_scale: u32,
pub equal_picture_interval: bool,
pub num_ticks_per_picture_minus_1: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct DecoderModelInfo {
pub buffer_delay_length_minus_1: u8,
pub num_units_in_decoding_tick: u32,
pub buffer_removal_time_length_minus_1: u8,
pub frame_presentation_time_length_minus_1: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum ColorPrimaries {
Bt709 = 1,
#[default]
Unspecified = 2,
Bt470M = 4,
Bt470bg = 5,
Bt601 = 6,
Smpte240 = 7,
GenericFilm = 8,
Bt2020 = 9,
Xyz = 10,
Smpte431 = 11,
Smpte432 = 12,
Ebu3213 = 22,
}
impl TryFrom<u32> for ColorPrimaries {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
1 => Ok(ColorPrimaries::Bt709),
2 => Ok(ColorPrimaries::Unspecified),
4 => Ok(ColorPrimaries::Bt470M),
5 => Ok(ColorPrimaries::Bt470bg),
6 => Ok(ColorPrimaries::Bt601),
7 => Ok(ColorPrimaries::Smpte240),
8 => Ok(ColorPrimaries::GenericFilm),
9 => Ok(ColorPrimaries::Bt2020),
10 => Ok(ColorPrimaries::Xyz),
11 => Ok(ColorPrimaries::Smpte431),
12 => Ok(ColorPrimaries::Smpte432),
22 => Ok(ColorPrimaries::Ebu3213),
_ => Err(format!("Invalid ColorPrimaries {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum TransferCharacteristics {
Reserved0 = 0,
Bt709 = 1,
#[default]
Unspecified = 2,
Reserved3 = 3,
Bt470m = 4,
Bt470bg = 5,
Bt601 = 6,
Smpte240 = 7,
Linear = 8,
Log100 = 9,
Log100Sqrt10 = 10,
Iec61966 = 11,
Bt1361 = 12,
Srgb = 13,
Bt202010Bit = 14,
Bt202012Bit = 15,
Smpte2084 = 16,
Smpte428 = 17,
Hlg = 18,
}
impl TryFrom<u32> for TransferCharacteristics {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(TransferCharacteristics::Reserved0),
1 => Ok(TransferCharacteristics::Bt709),
2 => Ok(TransferCharacteristics::Unspecified),
3 => Ok(TransferCharacteristics::Reserved3),
4 => Ok(TransferCharacteristics::Bt470m),
5 => Ok(TransferCharacteristics::Bt470bg),
6 => Ok(TransferCharacteristics::Bt601),
7 => Ok(TransferCharacteristics::Smpte240),
8 => Ok(TransferCharacteristics::Linear),
9 => Ok(TransferCharacteristics::Log100),
10 => Ok(TransferCharacteristics::Log100Sqrt10),
11 => Ok(TransferCharacteristics::Iec61966),
12 => Ok(TransferCharacteristics::Bt1361),
13 => Ok(TransferCharacteristics::Srgb),
14 => Ok(TransferCharacteristics::Bt202010Bit),
15 => Ok(TransferCharacteristics::Bt202012Bit),
16 => Ok(TransferCharacteristics::Smpte2084),
17 => Ok(TransferCharacteristics::Smpte428),
18 => Ok(TransferCharacteristics::Hlg),
_ => Err(format!("Invalid TransferCharacteristics {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum BitDepth {
#[default]
Depth8 = 0,
Depth10 = 1,
Depth12 = 2,
}
impl TryFrom<u32> for BitDepth {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(BitDepth::Depth8),
1 => Ok(BitDepth::Depth10),
2 => Ok(BitDepth::Depth12),
_ => Err(format!("Invalid BitDepth {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum MatrixCoefficients {
Identity = 0,
Bt709 = 1,
#[default]
Unspecified = 2,
Reserved3 = 3,
Fcc = 4,
Bt470bg = 5,
Bt601 = 6,
Smpte240 = 7,
Ycgco = 8,
Bt2020Ncl = 9,
Bt2020Cl = 10,
Smpte2085 = 11,
ChromaDerivedNcl = 12,
ChromaDerivedCl = 13,
Ictcp = 14,
}
impl TryFrom<u32> for MatrixCoefficients {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(MatrixCoefficients::Identity),
1 => Ok(MatrixCoefficients::Bt709),
2 => Ok(MatrixCoefficients::Unspecified),
3 => Ok(MatrixCoefficients::Reserved3),
4 => Ok(MatrixCoefficients::Fcc),
5 => Ok(MatrixCoefficients::Bt470bg),
6 => Ok(MatrixCoefficients::Bt601),
7 => Ok(MatrixCoefficients::Smpte240),
8 => Ok(MatrixCoefficients::Ycgco),
9 => Ok(MatrixCoefficients::Bt2020Ncl),
10 => Ok(MatrixCoefficients::Bt2020Cl),
11 => Ok(MatrixCoefficients::Smpte2085),
12 => Ok(MatrixCoefficients::ChromaDerivedNcl),
13 => Ok(MatrixCoefficients::ChromaDerivedCl),
14 => Ok(MatrixCoefficients::Ictcp),
_ => Err(format!("Invalid MatrixCoefficients {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum ChromaSamplePosition {
#[default]
Unknown = 0,
Vertical = 1,
Colocated = 2,
Reserved = 3,
}
impl TryFrom<u32> for ChromaSamplePosition {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(ChromaSamplePosition::Unknown),
1 => Ok(ChromaSamplePosition::Vertical),
2 => Ok(ChromaSamplePosition::Colocated),
3 => Ok(ChromaSamplePosition::Reserved),
_ => Err(format!("Invalid ChromaSamplePosition {}", value)),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ColorConfig {
pub high_bitdepth: bool,
pub twelve_bit: bool,
pub mono_chrome: bool,
pub color_description_present_flag: bool,
pub color_primaries: ColorPrimaries,
pub transfer_characteristics: TransferCharacteristics,
pub matrix_coefficients: MatrixCoefficients,
pub color_range: bool,
pub subsampling_x: bool,
pub subsampling_y: bool,
pub chroma_sample_position: ChromaSamplePosition,
pub separate_uv_delta_q: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SequenceHeaderObu {
pub obu_header: ObuHeader,
pub seq_profile: Profile,
pub still_picture: bool,
pub reduced_still_picture_header: bool,
pub frame_width_bits_minus_1: u8,
pub frame_height_bits_minus_1: u8,
pub max_frame_width_minus_1: u16,
pub max_frame_height_minus_1: u16,
pub frame_id_numbers_present_flag: bool,
pub delta_frame_id_length_minus_2: u32,
pub additional_frame_id_length_minus_1: u32,
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_order_hint: bool,
pub enable_dual_filter: bool,
pub enable_jnt_comp: bool,
pub enable_ref_frame_mvs: bool,
pub seq_choose_screen_content_tools: bool,
pub seq_force_screen_content_tools: u32,
pub seq_choose_integer_mv: bool,
pub seq_force_integer_mv: u32,
pub order_hint_bits_minus_1: i32,
pub order_hint_bits: i32,
pub enable_superres: bool,
pub enable_cdef: bool,
pub enable_restoration: bool,
pub film_grain_params_present: bool,
pub operating_points_cnt_minus_1: u32,
pub operating_points: [OperatingPoint; MAX_NUM_OPERATING_POINTS],
pub decoder_model_info_present_flag: bool,
pub decoder_model_info: DecoderModelInfo,
pub initial_display_delay_present_flag: bool,
pub timing_info_present_flag: bool,
pub timing_info: TimingInfo,
pub color_config: ColorConfig,
pub bit_depth: BitDepth,
pub num_planes: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct StreamInfo {
pub seq_header: Rc<SequenceHeaderObu>,
pub render_width: u32,
pub render_height: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TemporalDelimiterObu {
pub obu_header: ObuHeader,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum InterpolationFilter {
#[default]
EightTap = 0,
EightTapSmooth = 1,
EightTapSharp = 2,
Bilinear = 3,
Switchable = 4,
}
impl TryFrom<u32> for InterpolationFilter {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(InterpolationFilter::EightTap),
1 => Ok(InterpolationFilter::EightTapSmooth),
2 => Ok(InterpolationFilter::EightTapSharp),
3 => Ok(InterpolationFilter::Bilinear),
4 => Ok(InterpolationFilter::Switchable),
_ => Err(format!("Invalid InterpolationFilter {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum TxModes {
#[default]
Only4x4 = 0,
Largest = 1,
Select = 2,
}
impl TryFrom<u32> for TxModes {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(TxModes::Only4x4),
1 => Ok(TxModes::Largest),
2 => Ok(TxModes::Select),
_ => Err(format!("Invalid TxModes {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum FrameRestorationType {
#[default]
None = 0,
Wiener = 1,
Sgrproj = 2,
Switchable = 3,
}
impl TryFrom<u32> for FrameRestorationType {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(FrameRestorationType::None),
1 => Ok(FrameRestorationType::Wiener),
2 => Ok(FrameRestorationType::Sgrproj),
3 => Ok(FrameRestorationType::Switchable),
_ => Err(format!("Invalid FrameRestorationType {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum ReferenceFrameType {
#[default]
Intra = 0,
Last = 1,
Last2 = 2,
Last3 = 3,
Golden = 4,
BwdRef = 5,
AltRef2 = 6,
AltRef = 7,
}
impl TryFrom<u32> for ReferenceFrameType {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(ReferenceFrameType::Intra),
1 => Ok(ReferenceFrameType::Last),
2 => Ok(ReferenceFrameType::Last2),
3 => Ok(ReferenceFrameType::Last3),
4 => Ok(ReferenceFrameType::Golden),
5 => Ok(ReferenceFrameType::BwdRef),
6 => Ok(ReferenceFrameType::AltRef2),
7 => Ok(ReferenceFrameType::AltRef),
_ => Err(format!("Invalid ReferenceFrameType {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum WarpModelType {
#[default]
Identity = 0,
Translation = 1,
RotZoom = 2,
Affine = 3,
}
impl TryFrom<u32> for WarpModelType {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(WarpModelType::Identity),
1 => Ok(WarpModelType::Translation),
2 => Ok(WarpModelType::RotZoom),
3 => Ok(WarpModelType::Affine),
_ => Err(format!("Invalid WarpModelType {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum FrameType {
#[default]
KeyFrame = 0,
InterFrame = 1,
IntraOnlyFrame = 2,
SwitchFrame = 3,
}
impl TryFrom<u32> for FrameType {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(FrameType::KeyFrame),
1 => Ok(FrameType::InterFrame),
2 => Ok(FrameType::IntraOnlyFrame),
3 => Ok(FrameType::SwitchFrame),
_ => Err(format!("Invalid FrameType {}", value)),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum TxMode {
#[default]
Only4x4 = 0,
Largest = 1,
Select = 2,
}
impl TryFrom<u32> for TxMode {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(TxMode::Only4x4),
1 => Ok(TxMode::Largest),
2 => Ok(TxMode::Select),
_ => Err(format!("Invalid TxMode {}", value)),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FrameObu<'a> {
pub header: FrameHeaderObu,
pub tile_group: TileGroupObu<'a>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FrameHeaderObu {
pub obu_header: ObuHeader,
pub show_existing_frame: bool,
pub frame_to_show_map_idx: u8,
pub frame_presentation_time: u32,
pub display_frame_id: u32,
pub frame_type: FrameType,
pub show_frame: bool,
pub showable_frame: bool,
pub error_resilient_mode: bool,
pub disable_cdf_update: bool,
pub allow_screen_content_tools: u32,
pub force_integer_mv: u32,
pub current_frame_id: u32,
pub frame_size_override_flag: bool,
pub order_hint: u32,
pub primary_ref_frame: u32,
pub buffer_removal_time_present_flag: bool,
pub buffer_removal_time: Vec<u32>,
pub refresh_frame_flags: u32,
pub ref_order_hint: [u32; NUM_REF_FRAMES],
pub allow_intrabc: bool,
pub frame_refs_short_signaling: bool,
pub last_frame_idx: u8,
pub gold_frame_idx: u8,
pub ref_frame_idx: [u8; REFS_PER_FRAME],
pub allow_high_precision_mv: bool,
pub is_motion_mode_switchable: bool,
pub use_ref_frame_mvs: bool,
pub disable_frame_end_update_cdf: bool,
pub allow_warped_motion: bool,
pub reduced_tx_set: bool,
pub render_and_frame_size_different: bool,
pub use_superres: bool,
pub is_filter_switchable: bool,
pub interpolation_filter: InterpolationFilter,
pub loop_filter_params: LoopFilterParams,
pub quantization_params: QuantizationParams,
pub segmentation_params: SegmentationParams,
pub tile_info: TileInfo,
pub cdef_params: CdefParams,
pub loop_restoration_params: LoopRestorationParams,
pub tx_mode_select: u32,
pub skip_mode_present: bool,
pub reference_select: bool,
pub global_motion_params: GlobalMotionParams,
pub film_grain_params: FilmGrainParams,
pub superres_denom: u32,
pub frame_is_intra: bool,
pub order_hints: [u32; NUM_REF_FRAMES],
pub ref_frame_sign_bias: [bool; NUM_REF_FRAMES],
pub coded_lossless: bool,
pub all_lossless: bool,
pub lossless_array: [bool; MAX_SEGMENTS],
pub seg_qm_level: [[u32; MAX_SEGMENTS]; 3],
pub upscaled_width: u32,
pub frame_width: u32,
pub frame_height: u32,
pub render_width: u32,
pub render_height: u32,
pub tx_mode: TxMode,
pub skip_mode_frame: [u32; 2],
pub mi_cols: u32,
pub mi_rows: u32,
pub header_bytes: usize,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LoopFilterParams {
pub loop_filter_level: [u8; 4],
pub loop_filter_sharpness: u8,
pub loop_filter_delta_enabled: bool,
pub loop_filter_delta_update: bool,
pub loop_filter_ref_deltas: [i8; TOTAL_REFS_PER_FRAME],
pub loop_filter_mode_deltas: [i8; 2],
pub delta_lf_present: bool,
pub delta_lf_res: u8,
pub delta_lf_multi: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct QuantizationParams {
pub base_q_idx: u32,
pub diff_uv_delta: bool,
pub using_qmatrix: bool,
pub qm_y: u32,
pub qm_u: u32,
pub qm_v: u32,
pub delta_q_present: bool,
pub delta_q_res: u32,
pub delta_q_y_dc: i32,
pub delta_q_u_dc: i32,
pub delta_q_u_ac: i32,
pub delta_q_v_dc: i32,
pub delta_q_v_ac: i32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SegmentationParams {
pub segmentation_enabled: bool,
pub segmentation_update_map: bool,
pub segmentation_temporal_update: bool,
pub segmentation_update_data: bool,
pub feature_enabled: [[bool; SEG_LVL_MAX]; MAX_SEGMENTS],
pub feature_data: [[i16; SEG_LVL_MAX]; MAX_SEGMENTS],
pub seg_id_pre_skip: bool,
pub last_active_seg_id: u8,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TileInfo {
pub uniform_tile_spacing_flag: bool,
pub increment_tile_rows_log2: u32,
pub width_in_sbs_minus_1: [u32; MAX_TILE_COLS],
pub height_in_sbs_minus_1: [u32; MAX_TILE_ROWS],
pub context_update_tile_id: u32,
pub mi_col_starts: [u32; MAX_TILE_COLS + 1],
pub mi_row_starts: [u32; MAX_TILE_ROWS + 1],
pub tile_cols_log2: u32,
pub tile_cols: u32,
pub tile_rows_log2: u32,
pub tile_rows: u32,
pub tile_size_bytes: u32,
}
impl Default for TileInfo {
fn default() -> Self {
Self {
uniform_tile_spacing_flag: Default::default(),
increment_tile_rows_log2: Default::default(),
width_in_sbs_minus_1: [0; MAX_TILE_COLS],
height_in_sbs_minus_1: [0; MAX_TILE_ROWS],
context_update_tile_id: Default::default(),
mi_col_starts: [0; MAX_TILE_COLS + 1],
mi_row_starts: [0; MAX_TILE_ROWS + 1],
tile_cols_log2: Default::default(),
tile_cols: Default::default(),
tile_rows_log2: Default::default(),
tile_rows: Default::default(),
tile_size_bytes: Default::default(),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CdefParams {
pub cdef_damping: u32,
pub cdef_bits: u32,
pub cdef_y_pri_strength: [u32; CDEF_MAX],
pub cdef_y_sec_strength: [u32; CDEF_MAX],
pub cdef_uv_pri_strength: [u32; CDEF_MAX],
pub cdef_uv_sec_strength: [u32; CDEF_MAX],
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LoopRestorationParams {
pub lr_unit_shift: u8,
pub lr_uv_shift: u8,
pub frame_restoration_type: [FrameRestorationType; MAX_NUM_PLANES],
pub loop_restoration_size: [u16; MAX_NUM_PLANES],
pub uses_lr: bool,
pub uses_chroma_lr: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct GlobalMotionParams {
pub is_global: [bool; NUM_REF_FRAMES],
pub is_rot_zoom: [bool; NUM_REF_FRAMES],
pub is_translation: [bool; NUM_REF_FRAMES],
pub gm_params: [[i32; 6]; NUM_REF_FRAMES],
pub warp_valid: [bool; NUM_REF_FRAMES],
pub gm_type: [WarpModelType; NUM_REF_FRAMES],
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FilmGrainParams {
pub apply_grain: bool,
pub grain_seed: u16,
pub update_grain: bool,
pub film_grain_params_ref_idx: u8,
pub num_y_points: u8,
pub point_y_value: [u8; MAX_NUM_Y_POINTS],
pub point_y_scaling: [u8; MAX_NUM_Y_POINTS],
pub chroma_scaling_from_luma: bool,
pub num_cb_points: u8,
pub point_cb_value: [u8; MAX_NUM_CB_POINTS],
pub point_cb_scaling: [u8; MAX_NUM_CB_POINTS],
pub num_cr_points: u8,
pub point_cr_value: [u8; MAX_NUM_CR_POINTS],
pub point_cr_scaling: [u8; MAX_NUM_CR_POINTS],
pub grain_scaling_minus_8: u8,
pub ar_coeff_lag: u32,
pub ar_coeffs_y_plus_128: [u8; MAX_NUM_POS_LUMA],
pub ar_coeffs_cb_plus_128: [u8; MAX_NUM_POS_LUMA],
pub ar_coeffs_cr_plus_128: [u8; MAX_NUM_POS_LUMA],
pub ar_coeff_shift_minus_6: u8,
pub grain_scale_shift: u8,
pub cb_mult: u8,
pub cb_luma_mult: u8,
pub cb_offset: u16,
pub cr_mult: u8,
pub cr_luma_mult: u8,
pub cr_offset: u16,
pub overlap_flag: bool,
pub clip_to_restricted_range: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct ReferenceFrameInfo {
ref_valid: bool,
ref_frame_id: u32,
ref_upscaled_width: u32,
ref_frame_width: u32,
ref_frame_height: u32,
ref_render_width: u32,
ref_render_height: u32,
ref_mi_cols: u32,
ref_mi_rows: u32,
ref_frame_type: FrameType,
ref_subsampling_x: bool,
ref_subsampling_y: bool,
ref_bit_depth: BitDepth,
ref_order_hint: u32,
segmentation_params: SegmentationParams,
global_motion_params: GlobalMotionParams,
loop_filter_params: LoopFilterParams,
film_grain_params: FilmGrainParams,
tile_info: TileInfo,
display_frame_id: u32,
showable_frame: bool,
}
#[derive(Clone, Debug, Default)]
pub struct AnnexBState {
pub temporal_unit_size: u32,
pub frame_unit_size: u32,
pub temporal_unit_consumed: u32,
pub frame_unit_consumed: u32,
}
#[derive(Clone, Debug)]
enum StreamFormat {
LowOverhead,
AnnexB(AnnexBState),
}
#[derive(Debug)]
pub struct Parser {
stream_format: StreamFormat,
operating_point: u32,
seen_frame_header: bool,
operating_point_idc: u16,
should_probe_for_annexb: bool,
is_first_frame: bool,
ref_info: [ReferenceFrameInfo; NUM_REF_FRAMES],
mi_cols: u32,
mi_rows: u32,
prev_frame_id: u32,
current_frame_id: u32,
mi_col_starts: [u32; MAX_TILE_COLS + 1],
mi_row_starts: [u32; MAX_TILE_ROWS + 1],
tile_cols_log2: u32,
tile_cols: u32,
tile_rows_log2: u32,
tile_rows: u32,
tile_size_bytes: u32,
pub last_frame_header: Option<FrameHeaderObu>,
pub sequence_header: Option<Rc<SequenceHeaderObu>>,
}
impl Parser {
fn annexb_probe(data: &[u8]) -> Result<bool, String> {
let mut r = Reader::new(data);
let mut seen_sequence = false;
let mut seen_frame = false;
let temporal_unit_size = r.read_leb128()?;
if temporal_unit_size == 0 {
return Ok(false);
}
let frame_unit_size = r.read_leb128()?;
if frame_unit_size == 0 || frame_unit_size > temporal_unit_size {
return Ok(false);
}
let obu_length = r.read_leb128()?;
if obu_length == 0 || obu_length > frame_unit_size {
return Ok(false);
}
let header = Self::parse_obu_header(&mut r.clone())?;
if !matches!(header.obu_type, ObuType::TemporalDelimiter) {
return Ok(false);
}
r.0.skip_bits(obu_length as usize * 8)?;
let mut num_bytes_read = 0;
loop {
let obu_length = r.read_leb128()?;
let mut obu_reader = r.clone();
r.0.skip_bits(obu_length as usize * 8)?;
num_bytes_read += obu_length;
if !seen_sequence {
let header = Self::parse_obu_header(&mut obu_reader)?;
seen_sequence = matches!(header.obu_type, ObuType::SequenceHeader);
}
if !seen_frame {
let header = Self::parse_obu_header(&mut obu_reader)?;
seen_frame = matches!(header.obu_type, ObuType::Frame | ObuType::FrameHeader);
}
if seen_sequence && seen_frame {
return Ok(true);
}
if num_bytes_read >= frame_unit_size {
return Ok(false);
}
}
}
fn compute_image_size(&mut self, fh: &mut FrameHeaderObu) {
fh.mi_cols = 2 * ((fh.frame_width + 7) >> 3);
fh.mi_rows = 2 * ((fh.frame_height + 7) >> 3);
self.mi_cols = fh.mi_cols;
self.mi_rows = fh.mi_rows;
}
fn parse_superres_params(
fh: &mut FrameHeaderObu,
r: &mut Reader,
seq: &SequenceHeaderObu,
) -> Result<(), String> {
if seq.enable_superres {
fh.use_superres = r.0.read_bit()?;
} else {
fh.use_superres = false;
}
if fh.use_superres {
fh.superres_denom =
r.0.read_bits::<u32>(SUPERRES_DENOM_BITS)? + SUPERRES_DENOM_MIN as u32;
} else {
fh.superres_denom = SUPERRES_NUM as u32;
}
fh.upscaled_width = fh.frame_width;
fh.frame_width =
(fh.upscaled_width * SUPERRES_NUM as u32 + (fh.superres_denom / 2)) / fh.superres_denom;
Ok(())
}
fn set_frame_refs(
&self,
fh: &mut FrameHeaderObu,
ref_order_hint: &[u32; NUM_REF_FRAMES],
) -> Result<(), String> {
let seq = self.sequence()?;
let mut ref_frame_idx = [-1i32; REFS_PER_FRAME];
ref_frame_idx[0] = fh.last_frame_idx.into();
ref_frame_idx[ReferenceFrameType::Golden as usize - ReferenceFrameType::Last as usize] =
fh.gold_frame_idx.into();
let mut used_frame = [false; NUM_REF_FRAMES];
used_frame[fh.last_frame_idx as usize] = true;
used_frame[fh.gold_frame_idx as usize] = true;
let cur_frame_hint = 1 << (seq.order_hint_bits - 1);
let mut shifted_order_hints = [0; NUM_REF_FRAMES];
for i in 0..NUM_REF_FRAMES {
shifted_order_hints[i] = cur_frame_hint
+ helpers::get_relative_dist(
seq.enable_order_hint,
seq.order_hint_bits,
ref_order_hint[i].try_into().unwrap(),
fh.order_hint.try_into().unwrap(),
);
}
let mut latest_order_hint = shifted_order_hints[fh.last_frame_idx as usize];
if latest_order_hint >= cur_frame_hint {
return Err("It is a requirement of bitstream conformance that last_order_hint < cur_frame_hint".into());
}
let mut earliest_order_hint = shifted_order_hints[fh.gold_frame_idx as usize];
if earliest_order_hint >= cur_frame_hint {
return Err("It is a requirement of bitstream conformance that gold_order_hint < cur_frame_hint".into());
}
let ref_ = helpers::find_latest_backward(
&shifted_order_hints,
&used_frame,
cur_frame_hint,
&mut latest_order_hint,
);
if ref_ >= 0 {
ref_frame_idx
[ReferenceFrameType::AltRef as usize - ReferenceFrameType::Last as usize] = ref_;
used_frame[ref_ as usize] = true;
}
let ref_ = helpers::find_earliest_backward(
&shifted_order_hints,
&used_frame,
cur_frame_hint,
&mut earliest_order_hint,
);
if ref_ >= 0 {
ref_frame_idx
[ReferenceFrameType::BwdRef as usize - ReferenceFrameType::Last as usize] = ref_;
used_frame[ref_ as usize] = true;
}
let ref_ = helpers::find_earliest_backward(
&shifted_order_hints,
&used_frame,
cur_frame_hint,
&mut earliest_order_hint,
);
if ref_ >= 0 {
ref_frame_idx
[ReferenceFrameType::AltRef2 as usize - ReferenceFrameType::Last as usize] = ref_;
used_frame[ref_ as usize] = true;
}
const REF_FRAME_LIST: [usize; 5] = [
ReferenceFrameType::Last2 as usize - ReferenceFrameType::Last as usize,
ReferenceFrameType::Last3 as usize - ReferenceFrameType::Last as usize,
ReferenceFrameType::BwdRef as usize - ReferenceFrameType::Last as usize,
ReferenceFrameType::AltRef2 as usize - ReferenceFrameType::Last as usize,
ReferenceFrameType::AltRef as usize - ReferenceFrameType::Last as usize,
];
#[allow(clippy::needless_range_loop)]
for i in 0..REFS_PER_FRAME - 2 {
let ref_frame = REF_FRAME_LIST[i];
if ref_frame_idx[ref_frame] < 0 {
let ref_ = helpers::find_latest_forward(
&shifted_order_hints,
&used_frame,
cur_frame_hint,
&mut latest_order_hint,
);
if ref_ >= 0 {
ref_frame_idx[ref_frame] = ref_;
used_frame[ref_ as usize] = true;
}
}
}
let mut ref_ = 0;
earliest_order_hint = shifted_order_hints[0];
#[allow(clippy::needless_range_loop)]
for i in 1..NUM_REF_FRAMES {
let hint = shifted_order_hints[i];
if hint < earliest_order_hint {
ref_ = i as u8;
earliest_order_hint = hint;
}
}
fh.ref_frame_idx
.iter_mut()
.zip(ref_frame_idx.iter().copied())
.for_each(|(dest, src)| *dest = if src < 0 { ref_ } else { src as u8 });
Ok(())
}
fn parse_frame_size(&mut self, fh: &mut FrameHeaderObu, r: &mut Reader) -> Result<(), String> {
let seq = self.sequence()?;
if fh.frame_size_override_flag {
let n = seq.frame_width_bits_minus_1 + 1;
fh.frame_width = r.0.read_bits::<u32>(n as usize)? + 1;
let n = seq.frame_height_bits_minus_1 + 1;
fh.frame_height = r.0.read_bits::<u32>(n as usize)? + 1;
} else {
fh.frame_width = seq.max_frame_width_minus_1 as u32 + 1;
fh.frame_height = seq.max_frame_height_minus_1 as u32 + 1;
}
Self::parse_superres_params(fh, r, seq)?;
self.compute_image_size(fh);
Ok(())
}
fn parse_render_size(fh: &mut FrameHeaderObu, r: &mut Reader) -> Result<(), String> {
fh.render_and_frame_size_different = r.0.read_bit()?;
if fh.render_and_frame_size_different {
fh.render_width = r.0.read_bits::<u32>(16)? + 1;
fh.render_height = r.0.read_bits::<u32>(16)? + 1;
} else {
fh.render_width = fh.upscaled_width;
fh.render_height = fh.frame_height;
}
Ok(())
}
fn frame_size_with_refs(
&mut self,
fh: &mut FrameHeaderObu,
r: &mut Reader,
) -> Result<(), String> {
let mut found_ref = false;
let seq = self.sequence()?;
for i in 0..REFS_PER_FRAME {
found_ref = r.0.read_bit()?;
if found_ref {
let rf = &self.ref_info[fh.ref_frame_idx[i] as usize];
fh.upscaled_width = rf.ref_upscaled_width;
fh.frame_width = fh.upscaled_width;
fh.frame_height = rf.ref_frame_height;
fh.render_width = rf.ref_render_width;
fh.render_height = rf.ref_render_height;
break;
}
}
if !found_ref {
self.parse_frame_size(fh, r)?;
Self::parse_render_size(fh, r)?;
} else {
Self::parse_superres_params(fh, r, seq)?;
self.compute_image_size(fh);
}
Ok(())
}
fn skip_and_check_trailing_bits(r: &mut Reader, obu: &Obu) -> Result<(), String> {
if obu.data.len() == 0
|| matches!(
obu.header.obu_type,
ObuType::TileList | ObuType::TileGroup | ObuType::Frame
)
{
return Ok(());
}
let num_trailing = obu.as_ref().len() as u64 * 8 - r.0.position();
r.read_trailing_bits(num_trailing)?;
Ok(())
}
fn parse_obu_header(r: &mut Reader) -> Result<ObuHeader, String> {
let _obu_forbidden_bit = r.0.read_bit()?;
let mut header = ObuHeader {
obu_type: ObuType::try_from(r.0.read_bits::<u32>(4)?)?,
extension_flag: r.0.read_bit()?,
has_size_field: r.0.read_bit()?,
temporal_id: Default::default(),
spatial_id: Default::default(),
};
let obu_reserved_1bit = r.0.read_bit()?;
assert!(!obu_reserved_1bit);
if header.extension_flag {
header.temporal_id = r.0.read_bits::<u32>(3)?;
header.spatial_id = r.0.read_bits::<u32>(2)?;
let _ = r.0.read_bits::<u32>(3)?;
}
Ok(header)
}
pub fn read_obu<'a>(&mut self, data: &'a [u8]) -> Result<ObuAction<'a>, String> {
if data.is_empty() {
return Err("Empty data".into());
}
let mut reader = Reader::new(data);
if self.should_probe_for_annexb {
self.stream_format = if matches!(Self::annexb_probe(data), Ok(true)) {
log::debug!("Parsing an Annex B stream");
StreamFormat::AnnexB(AnnexBState::default())
} else {
log::debug!("Parsing a low-overhead stream");
StreamFormat::LowOverhead
};
self.should_probe_for_annexb = false;
}
let obu_length: usize = if let StreamFormat::AnnexB(annexb_state) = &mut self.stream_format
{
let obu_length = reader.current_annexb_obu_length(annexb_state)?;
match obu_length {
Some(length) => length,
None => return Ok(ObuAction::Drop(reader.consumed(0))),
}
} else {
0
};
let start_pos = reader.consumed(0);
let header = Self::parse_obu_header(&mut reader)?;
if matches!(self.stream_format, StreamFormat::LowOverhead) {
assert!(header.has_size_field);
}
let obu_size: usize = if header.has_size_field {
reader.read_leb128()? as usize
} else {
obu_length
.checked_sub(1)
.ok_or::<String>("obu_length must be greater than 0".into())?
.checked_sub(usize::from(header.extension_flag))
.ok_or::<String>("obu_length too short".into())?
};
let consumed = reader.consumed(start_pos);
if let StreamFormat::AnnexB(annexb_state) = &mut self.stream_format {
annexb_state.temporal_unit_consumed += consumed;
annexb_state.frame_unit_consumed += consumed;
annexb_state.temporal_unit_consumed += u32::try_from(obu_size).unwrap();
annexb_state.frame_unit_consumed += u32::try_from(obu_size).unwrap();
}
assert!(reader.0.position() % 8 == 0);
let start_offset: usize = (reader.0.position() / 8).try_into().unwrap();
log::debug!(
"Identified OBU type {:?}, data size: {}, obu_size: {}",
header.obu_type,
start_offset + obu_size,
obu_size
);
if header.obu_type != ObuType::SequenceHeader
&& header.obu_type != ObuType::TemporalDelimiter
&& self.operating_point_idc != 0
&& header.extension_flag
{
let in_temporal_layer = ((self.operating_point_idc >> header.temporal_id) & 1) != 0;
let in_spatial_layer = ((self.operating_point_idc >> (header.spatial_id + 8)) & 1) != 0;
if !in_temporal_layer || !in_spatial_layer {
log::debug!("Dropping obu as per drop_obu() in the specification",);
return Ok(ObuAction::Drop(reader.consumed(0)));
}
}
Ok(ObuAction::Process(Obu {
header,
data: Cow::from(&data[start_offset..start_offset + obu_size]),
bytes_used: start_offset + obu_size,
}))
}
fn parse_color_config(s: &mut SequenceHeaderObu, r: &mut Reader) -> Result<(), String> {
let cc = &mut s.color_config;
cc.high_bitdepth = r.0.read_bit()?;
if s.seq_profile as u32 == 2 && cc.high_bitdepth {
cc.twelve_bit = r.0.read_bit()?;
if cc.twelve_bit {
s.bit_depth = BitDepth::Depth12;
} else {
s.bit_depth = BitDepth::Depth10;
}
} else if s.seq_profile as u32 <= 2 {
s.bit_depth = if cc.high_bitdepth { BitDepth::Depth10 } else { BitDepth::Depth8 };
}
if s.seq_profile as u32 == 1 {
cc.mono_chrome = false;
} else {
cc.mono_chrome = r.0.read_bit()?;
}
if cc.mono_chrome {
s.num_planes = 1;
} else {
s.num_planes = 3;
}
cc.color_description_present_flag = r.0.read_bit()?;
if cc.color_description_present_flag {
cc.color_primaries = ColorPrimaries::try_from(r.0.read_bits::<u32>(8)?)?;
cc.transfer_characteristics =
TransferCharacteristics::try_from(r.0.read_bits::<u32>(8)?)?;
cc.matrix_coefficients = MatrixCoefficients::try_from(r.0.read_bits::<u32>(8)?)?;
} else {
cc.color_primaries = ColorPrimaries::Unspecified;
cc.transfer_characteristics = TransferCharacteristics::Unspecified;
cc.matrix_coefficients = MatrixCoefficients::Unspecified;
}
if cc.mono_chrome {
cc.color_range = r.0.read_bit()?;
cc.subsampling_x = true;
cc.subsampling_y = true;
cc.chroma_sample_position = ChromaSamplePosition::Unknown;
cc.separate_uv_delta_q = false;
return Ok(());
} else if matches!(cc.color_primaries, ColorPrimaries::Bt709)
&& matches!(cc.transfer_characteristics, TransferCharacteristics::Srgb)
&& matches!(cc.matrix_coefficients, MatrixCoefficients::Identity)
{
cc.color_range = true;
cc.subsampling_x = false;
cc.subsampling_y = false;
} else {
cc.color_range = r.0.read_bit()?;
if s.seq_profile as u32 == 0 {
cc.subsampling_x = true;
cc.subsampling_y = true;
} else if s.seq_profile as u32 == 1 {
cc.subsampling_x = false;
cc.subsampling_y = false;
} else if matches!(s.bit_depth, BitDepth::Depth12) {
cc.subsampling_x = r.0.read_bit()?;
if cc.subsampling_x {
cc.subsampling_y = r.0.read_bit()?;
} else {
cc.subsampling_y = false;
}
} else {
cc.subsampling_x = true;
cc.subsampling_y = false;
}
if cc.subsampling_x && cc.subsampling_y {
cc.chroma_sample_position =
ChromaSamplePosition::try_from(r.0.read_bits::<u32>(2)?)?;
}
}
cc.separate_uv_delta_q = r.0.read_bit()?;
Ok(())
}
fn parse_operating_parameters_info(
opi: &mut OperatingPoint,
r: &mut Reader,
buffer_delay_length_minus_1: u8,
) -> Result<(), String> {
let n = buffer_delay_length_minus_1 + 1;
opi.decoder_buffer_delay = r.0.read_bits::<u32>(n as usize)?;
opi.encoder_buffer_delay = r.0.read_bits::<u32>(n as usize)?;
opi.low_delay_mode_flag = r.0.read_bit()?;
Ok(())
}
fn parse_decoder_model_info(dmi: &mut DecoderModelInfo, r: &mut Reader) -> Result<(), String> {
dmi.buffer_delay_length_minus_1 = r.0.read_bits::<u32>(5)? as u8;
dmi.num_units_in_decoding_tick = r.0.read_bits::<u32>(32)?;
dmi.buffer_removal_time_length_minus_1 = r.0.read_bits::<u32>(5)? as u8;
dmi.frame_presentation_time_length_minus_1 = r.0.read_bits::<u32>(5)?;
Ok(())
}
fn parse_timing_info(ti: &mut TimingInfo, r: &mut Reader) -> Result<(), String> {
ti.num_units_in_display_tick = r.0.read_bits::<u32>(32)?;
ti.time_scale = r.0.read_bits::<u32>(32)?;
ti.equal_picture_interval = r.0.read_bit()?;
if ti.equal_picture_interval {
ti.num_ticks_per_picture_minus_1 = r.read_uvlc()?;
}
Ok(())
}
pub fn choose_operating_point(&mut self, operating_point: u32) -> Result<(), String> {
if operating_point > self.sequence()?.operating_points_cnt_minus_1 {
return Err(format!(
"Invalid operating point {} (max {})",
operating_point,
self.sequence()?.operating_points_cnt_minus_1
));
}
self.operating_point = operating_point;
self.operating_point_idc = self.sequence()?.operating_points[operating_point as usize].idc;
Ok(())
}
fn parse_temporal_delimiter_obu(&mut self) -> Result<(), String> {
self.seen_frame_header = false;
Ok(())
}
fn parse_sequence_header_obu(&mut self, obu: &Obu) -> Result<Rc<SequenceHeaderObu>, String> {
let mut s = SequenceHeaderObu { obu_header: obu.header.clone(), ..Default::default() };
let mut r = Reader::new(obu.as_ref());
let profile = r.0.read_bits::<u32>(3)?;
s.seq_profile = Profile::try_from(profile)?;
s.still_picture = r.0.read_bit()?;
s.reduced_still_picture_header = r.0.read_bit()?;
if s.reduced_still_picture_header {
s.timing_info_present_flag = false;
s.decoder_model_info_present_flag = false;
s.initial_display_delay_present_flag = false;
s.operating_points_cnt_minus_1 = 0;
s.operating_points[0].idc = 0;
s.operating_points[0].seq_level_idx = r.0.read_bits::<u32>(5)? as u8;
s.operating_points[0].seq_tier = 0;
s.operating_points[0].decoder_model_present_for_this_op = false;
s.operating_points[0].initial_display_delay_present_for_this_op = false;
} else {
s.timing_info_present_flag = r.0.read_bit()?;
if s.timing_info_present_flag {
Self::parse_timing_info(&mut s.timing_info, &mut r)?;
s.decoder_model_info_present_flag = r.0.read_bit()?;
if s.decoder_model_info_present_flag {
Self::parse_decoder_model_info(&mut s.decoder_model_info, &mut r)?;
}
} else {
s.decoder_model_info_present_flag = false;
}
s.initial_display_delay_present_flag = r.0.read_bit()?;
s.operating_points_cnt_minus_1 = r.0.read_bits::<u32>(5)?;
if s.operating_points_cnt_minus_1 > MAX_NUM_OPERATING_POINTS as u32 {
return Err(format!(
"Invalid operating_points_cnt_minus_1 {}",
s.operating_points_cnt_minus_1
));
}
for i in 0..=s.operating_points_cnt_minus_1 as usize {
s.operating_points[i].idc = r.0.read_bits::<u32>(12)? as u16;
s.operating_points[i].seq_level_idx = r.0.read_bits::<u32>(5)? as u8;
if s.operating_points[i].seq_level_idx > 7 {
s.operating_points[i].seq_tier = r.0.read_bit()? as u8;
} else {
s.operating_points[i].seq_tier = 0;
}
if s.decoder_model_info_present_flag {
s.operating_points[i].decoder_model_present_for_this_op = r.0.read_bit()?;
if s.operating_points[i].decoder_model_present_for_this_op {
let buffer_delay_length_minus_1 =
s.decoder_model_info.buffer_delay_length_minus_1;
Self::parse_operating_parameters_info(
&mut s.operating_points[i],
&mut r,
buffer_delay_length_minus_1,
)?;
}
} else {
s.operating_points[i].decoder_model_present_for_this_op = false;
}
if s.initial_display_delay_present_flag {
s.operating_points[i].initial_display_delay_present_for_this_op =
r.0.read_bit()?;
if s.operating_points[i].initial_display_delay_present_for_this_op {
s.operating_points[i].initial_display_delay_minus_1 =
r.0.read_bits::<u32>(4)?;
}
}
}
}
s.frame_width_bits_minus_1 = r.0.read_bits::<u32>(4)? as u8;
s.frame_height_bits_minus_1 = r.0.read_bits::<u32>(4)? as u8;
s.max_frame_width_minus_1 =
r.0.read_bits::<u32>(s.frame_width_bits_minus_1 as usize + 1)? as u16;
s.max_frame_height_minus_1 =
r.0.read_bits::<u32>(s.frame_height_bits_minus_1 as usize + 1)? as u16;
if s.reduced_still_picture_header {
s.frame_id_numbers_present_flag = false;
} else {
s.frame_id_numbers_present_flag = r.0.read_bit()?;
}
if s.frame_id_numbers_present_flag {
s.delta_frame_id_length_minus_2 = r.0.read_bits::<u32>(4)?;
s.additional_frame_id_length_minus_1 = r.0.read_bits::<u32>(3)?;
let frame_id_length =
s.additional_frame_id_length_minus_1 + s.delta_frame_id_length_minus_2 + 3;
if frame_id_length > 16 {
return Err(format!("Invalid frame_id_length {}", frame_id_length));
}
}
s.use_128x128_superblock = r.0.read_bit()?;
s.enable_filter_intra = r.0.read_bit()?;
s.enable_intra_edge_filter = r.0.read_bit()?;
if s.reduced_still_picture_header {
s.enable_interintra_compound = false;
s.enable_masked_compound = false;
s.enable_warped_motion = false;
s.enable_dual_filter = false;
s.enable_order_hint = false;
s.enable_jnt_comp = false;
s.enable_ref_frame_mvs = false;
s.seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS as _;
s.seq_force_integer_mv = SELECT_INTEGER_MV as _;
s.order_hint_bits = 0;
s.order_hint_bits_minus_1 = -1;
} else {
s.enable_interintra_compound = r.0.read_bit()?;
s.enable_masked_compound = r.0.read_bit()?;
s.enable_warped_motion = r.0.read_bit()?;
s.enable_dual_filter = r.0.read_bit()?;
s.enable_order_hint = r.0.read_bit()?;
if s.enable_order_hint {
s.enable_jnt_comp = r.0.read_bit()?;
s.enable_ref_frame_mvs = r.0.read_bit()?;
} else {
s.enable_jnt_comp = false;
s.enable_ref_frame_mvs = false;
}
s.seq_choose_screen_content_tools = r.0.read_bit()?;
if s.seq_choose_screen_content_tools {
s.seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS as _;
} else {
s.seq_force_screen_content_tools = r.0.read_bit()? as _;
}
if s.seq_force_screen_content_tools > 0 {
s.seq_choose_integer_mv = r.0.read_bit()?;
if s.seq_choose_integer_mv {
s.seq_force_integer_mv = SELECT_INTEGER_MV as _;
} else {
s.seq_force_integer_mv = r.0.read_bit()? as _;
}
} else {
s.seq_force_integer_mv = SELECT_INTEGER_MV as _;
}
if s.enable_order_hint {
s.order_hint_bits_minus_1 = r.0.read_bits::<u32>(3)?.try_into().unwrap();
s.order_hint_bits = s.order_hint_bits_minus_1 + 1;
} else {
s.order_hint_bits_minus_1 = -1;
s.order_hint_bits = 0;
}
}
s.enable_superres = r.0.read_bit()?;
s.enable_cdef = r.0.read_bit()?;
s.enable_restoration = r.0.read_bit()?;
Self::parse_color_config(&mut s, &mut r)?;
s.film_grain_params_present = r.0.read_bit()?;
Self::skip_and_check_trailing_bits(&mut r, obu)?;
let rc = Rc::new(s);
self.sequence_header = Some(rc.clone());
self.choose_operating_point(0)?;
Ok(rc)
}
fn load_reference_frame(&self, fh: &mut FrameHeaderObu) -> Result<(), String> {
let rf = &self.ref_info[fh.frame_to_show_map_idx as usize];
let seq = self.sequence()?;
fh.frame_type = rf.ref_frame_type;
fh.upscaled_width = rf.ref_upscaled_width;
fh.frame_width = rf.ref_frame_width;
fh.frame_height = rf.ref_frame_height;
fh.render_width = rf.ref_render_width;
fh.render_height = rf.ref_render_height;
if fh.frame_type == FrameType::KeyFrame {
fh.current_frame_id = rf.ref_frame_id;
fh.mi_cols = rf.ref_mi_cols;
fh.mi_rows = rf.ref_mi_rows;
fh.global_motion_params = rf.global_motion_params.clone();
if seq.film_grain_params_present {
fh.film_grain_params = rf.film_grain_params.clone();
}
fh.loop_filter_params = rf.loop_filter_params.clone();
fh.segmentation_params = rf.segmentation_params.clone();
}
Ok(())
}
fn setup_past_independence(fh: &mut FrameHeaderObu) {
fh.segmentation_params.feature_enabled = Default::default();
fh.segmentation_params.feature_data = Default::default();
for i in ReferenceFrameType::Last as usize..ReferenceFrameType::AltRef as usize {
fh.global_motion_params.gm_type[i] = WarpModelType::Identity;
}
fh.loop_filter_params.loop_filter_delta_enabled = true;
fh.loop_filter_params.loop_filter_ref_deltas = [1, 0, 0, 0, -1, 0, -1, -1];
fh.loop_filter_params.loop_filter_mode_deltas = Default::default();
}
fn parse_tile_info(&mut self, r: &mut Reader, ti: &mut TileInfo) -> Result<(), String> {
let seq = self.sequence()?;
let sb_cols = if seq.use_128x128_superblock {
(self.mi_cols + 31) >> 5
} else {
(self.mi_cols + 15) >> 4
};
let sb_rows = if seq.use_128x128_superblock {
(self.mi_rows + 31) >> 5
} else {
(self.mi_rows + 15) >> 4
};
let sb_shift = if seq.use_128x128_superblock { 5 } else { 4 };
let sb_size = sb_shift + 2;
let max_tile_width_sb = MAX_TILE_WIDTH >> sb_size;
let mut max_tile_area_sb = MAX_TILE_AREA >> (2 * sb_size);
let min_log2_tile_cols = helpers::tile_log2(max_tile_width_sb, sb_cols);
let max_log2_tile_cols =
helpers::tile_log2(1, std::cmp::min(sb_cols, MAX_TILE_COLS as u32));
let max_log2_tile_rows =
helpers::tile_log2(1, std::cmp::min(sb_rows, MAX_TILE_ROWS as u32));
let min_log2_tiles = std::cmp::max(
min_log2_tile_cols,
helpers::tile_log2(max_tile_area_sb, sb_rows * sb_cols),
);
ti.uniform_tile_spacing_flag = r.0.read_bit()?;
if ti.uniform_tile_spacing_flag {
self.tile_cols_log2 = min_log2_tile_cols;
while self.tile_cols_log2 < max_log2_tile_cols {
let increment_tile_cols_log_2 = r.0.read_bit()?;
if increment_tile_cols_log_2 {
self.tile_cols_log2 += 1;
} else {
break;
}
}
let tile_width_sb = (sb_cols + (1 << self.tile_cols_log2) - 1) >> self.tile_cols_log2;
let mut i = 0;
let mut start_sb = 0;
while start_sb < sb_cols {
self.mi_col_starts[i] = start_sb << sb_shift;
i += 1;
start_sb += tile_width_sb;
}
self.mi_col_starts[i] = self.mi_cols;
self.tile_cols = i as _;
if self.tile_cols > MAX_TILE_COLS as u32 {
return Err(format!("Invalid tile_cols {}", self.tile_cols));
}
while i >= 1 {
ti.width_in_sbs_minus_1[i - 1] =
((self.mi_col_starts[i] - self.mi_col_starts[i - 1] + ((1 << sb_shift) - 1))
>> sb_shift)
- 1;
i -= 1;
}
let min_log2_tile_rows =
std::cmp::max(min_log2_tiles.saturating_sub(self.tile_cols_log2), 0);
self.tile_rows_log2 = min_log2_tile_rows;
while self.tile_rows_log2 < max_log2_tile_rows {
let increment_tile_rows_log_2 = r.0.read_bit()?;
if increment_tile_rows_log_2 {
self.tile_rows_log2 += 1;
} else {
break;
}
}
let tile_height_sb = (sb_rows + (1 << self.tile_rows_log2) - 1) >> self.tile_rows_log2;
let mut i = 0;
let mut start_sb = 0;
while start_sb < sb_rows {
self.mi_row_starts[i] = start_sb << sb_shift;
i += 1;
start_sb += tile_height_sb;
}
self.mi_row_starts[i] = self.mi_rows;
self.tile_rows = i as _;
if self.tile_rows > MAX_TILE_ROWS as u32 {
return Err(format!("Invalid tile_rows {}", self.tile_cols));
}
while i >= 1 {
ti.height_in_sbs_minus_1[i - 1] =
((self.mi_row_starts[i] - self.mi_row_starts[i - 1] + ((1 << sb_shift) - 1))
>> sb_shift)
- 1;
i -= 1;
}
} else {
let mut widest_tile_sb = 0;
let mut start_sb = 0;
let mut i = 0;
while start_sb < sb_cols {
self.mi_col_starts[i] = start_sb << sb_shift;
let max_width = std::cmp::min(sb_cols - start_sb, max_tile_width_sb);
ti.width_in_sbs_minus_1[i] = r.read_ns(max_width.try_into().unwrap())?;
let size_sb = ti.width_in_sbs_minus_1[i] + 1;
widest_tile_sb = std::cmp::max(size_sb, widest_tile_sb);
start_sb += size_sb;
i += 1;
}
self.mi_col_starts[i] = self.mi_cols;
self.tile_cols = i as _;
self.tile_cols_log2 = helpers::tile_log2(1, self.tile_cols);
if min_log2_tiles > 0 {
max_tile_area_sb = (sb_rows * sb_cols) >> (min_log2_tiles + 1);
} else {
max_tile_area_sb = sb_rows * sb_cols;
}
let max_tile_height_sb = std::cmp::max(max_tile_area_sb / widest_tile_sb, 1);
let mut start_sb = 0;
let mut i = 0;
while start_sb < sb_rows {
self.mi_row_starts[i] = start_sb << sb_shift;
let max_height = std::cmp::min(sb_rows - start_sb, max_tile_height_sb);
ti.height_in_sbs_minus_1[i] = r.read_ns(max_height.try_into().unwrap())?;
let size_sb = ti.height_in_sbs_minus_1[i] + 1;
start_sb += size_sb;
i += 1;
}
self.mi_row_starts[i] = self.mi_rows;
self.tile_rows = i as _;
self.tile_rows_log2 = helpers::tile_log2(1, self.tile_rows);
}
if self.tile_cols_log2 > 0 || self.tile_rows_log2 > 0 {
let num_bits: usize = (self.tile_rows_log2 + self.tile_cols_log2).try_into().unwrap();
ti.context_update_tile_id = r.0.read_bits::<u32>(num_bits)?;
if ti.context_update_tile_id >= self.tile_rows * self.tile_cols {
return Err(format!(
"Invalid context_update_tile_id {}",
ti.context_update_tile_id
));
}
self.tile_size_bytes = r.0.read_bits::<u32>(2)? + 1;
} else {
ti.context_update_tile_id = 0;
}
ti.mi_col_starts = self.mi_col_starts;
ti.mi_row_starts = self.mi_row_starts;
ti.tile_cols_log2 = self.tile_cols_log2;
ti.tile_cols = self.tile_cols;
ti.tile_rows_log2 = self.tile_rows_log2;
ti.tile_rows = self.tile_rows;
ti.tile_size_bytes = self.tile_size_bytes;
Ok(())
}
fn parse_quantization_params(
r: &mut Reader,
q: &mut QuantizationParams,
num_planes: u32,
separate_uv_delta_q: bool,
) -> Result<(), String> {
q.base_q_idx = r.0.read_bits::<u32>(8)?;
q.delta_q_y_dc = r.read_delta_q()?;
if num_planes > 1 {
if separate_uv_delta_q {
q.diff_uv_delta = r.0.read_bit()?;
} else {
q.diff_uv_delta = false;
}
q.delta_q_u_dc = r.read_delta_q()?;
q.delta_q_u_ac = r.read_delta_q()?;
if q.diff_uv_delta {
q.delta_q_v_dc = r.read_delta_q()?;
q.delta_q_v_ac = r.read_delta_q()?;
} else {
q.delta_q_v_dc = q.delta_q_u_dc;
q.delta_q_v_ac = q.delta_q_u_ac;
}
} else {
q.delta_q_u_dc = 0;
q.delta_q_u_ac = 0;
q.delta_q_v_dc = 0;
q.delta_q_v_ac = 0;
}
q.using_qmatrix = r.0.read_bit()?;
if q.using_qmatrix {
q.qm_y = r.0.read_bits::<u32>(4)?;
q.qm_u = r.0.read_bits::<u32>(4)?;
if !separate_uv_delta_q {
q.qm_v = q.qm_u;
} else {
q.qm_v = r.0.read_bits::<u32>(4)?;
}
}
Ok(())
}
fn parse_delta_q_params(r: &mut Reader, q: &mut QuantizationParams) -> Result<(), String> {
q.delta_q_res = 0;
q.delta_q_present = false;
if q.base_q_idx > 0 {
q.delta_q_present = r.0.read_bit()?;
}
if q.delta_q_present {
q.delta_q_res = r.0.read_bits::<u32>(2)?;
}
Ok(())
}
fn parse_delta_lf_params(
r: &mut Reader,
lf: &mut LoopFilterParams,
delta_q_present: bool,
allow_intrabc: bool,
) -> Result<(), String> {
lf.delta_lf_present = false;
lf.delta_lf_res = 0;
lf.delta_lf_multi = false;
if delta_q_present {
if !allow_intrabc {
lf.delta_lf_present = r.0.read_bit()?;
}
if lf.delta_lf_present {
lf.delta_lf_res = r.0.read_bits::<u32>(2)? as u8;
lf.delta_lf_multi = r.0.read_bit()?;
}
}
Ok(())
}
fn parse_segmentation_params(
&self,
r: &mut Reader,
fh: &mut FrameHeaderObu,
) -> Result<(), String> {
let s = &mut fh.segmentation_params;
s.segmentation_enabled = r.0.read_bit()?;
if s.segmentation_enabled {
if fh.primary_ref_frame == PRIMARY_REF_NONE {
s.segmentation_update_map = true;
s.segmentation_temporal_update = false;
s.segmentation_update_data = true;
} else {
s.segmentation_update_map = r.0.read_bit()?;
if s.segmentation_update_map {
s.segmentation_temporal_update = r.0.read_bit()?;
}
s.segmentation_update_data = r.0.read_bit()?;
}
if s.segmentation_update_data {
for i in 0..MAX_SEGMENTS {
for j in 0..SEG_LVL_MAX {
let feature_enabled = r.0.read_bit()?;
s.feature_enabled[i][j] = feature_enabled;
if feature_enabled {
let bits_to_read = FEATURE_BITS[j];
let limit = FEATURE_MAX[j];
let signed = FEATURE_SIGNED[j];
if signed {
let feature_value = r.read_su(1 + bits_to_read as usize)?;
let clipped_value = helpers::clip3(-limit, limit, feature_value);
s.feature_data[i][j] = clipped_value as _;
} else {
let feature_value = r.0.read_bits::<u32>(bits_to_read as usize)?;
let clipped_value = helpers::clip3(
0,
limit,
feature_value
.try_into()
.map_err(|_| "Invalid feature_value")?,
);
s.feature_data[i][j] = clipped_value as _;
}
}
}
}
} else {
let prev_frame =
&self.ref_info[fh.ref_frame_idx[fh.primary_ref_frame as usize] as usize];
if !prev_frame.ref_valid {
return Err("Reference is invalid".into());
}
s.feature_enabled = prev_frame.segmentation_params.feature_enabled;
s.feature_data = prev_frame.segmentation_params.feature_data;
}
} else {
for i in 0..MAX_SEGMENTS {
for j in 0..SEG_LVL_MAX {
s.feature_enabled[i][j] = false;
s.feature_data[i][j] = 0;
}
}
}
s.seg_id_pre_skip = false;
s.last_active_seg_id = 0;
for i in 0..MAX_SEGMENTS {
for j in 0..SEG_LVL_MAX {
if s.feature_enabled[i][j] {
s.last_active_seg_id = i as u8;
if j >= SEG_LVL_REF_FRAME {
s.seg_id_pre_skip = true;
}
}
}
}
Ok(())
}
fn parse_loop_filter_parameters(
r: &mut Reader,
fh: &mut FrameHeaderObu,
num_planes: u32,
) -> Result<(), String> {
let lf = &mut fh.loop_filter_params;
if fh.coded_lossless || fh.allow_intrabc {
lf.loop_filter_level[0] = 0;
lf.loop_filter_level[1] = 0;
lf.loop_filter_ref_deltas = [1, 0, 0, 0, -1, 0, -1, -1];
lf.loop_filter_mode_deltas = Default::default();
return Ok(());
}
lf.loop_filter_level[0] = r.0.read_bits::<u32>(6)? as u8;
lf.loop_filter_level[1] = r.0.read_bits::<u32>(6)? as u8;
if num_planes > 1 && (lf.loop_filter_level[0] > 0 || lf.loop_filter_level[1] > 0) {
lf.loop_filter_level[2] = r.0.read_bits::<u32>(6)? as u8;
lf.loop_filter_level[3] = r.0.read_bits::<u32>(6)? as u8;
}
lf.loop_filter_sharpness = r.0.read_bits::<u32>(3)? as u8;
lf.loop_filter_delta_enabled = r.0.read_bit()?;
if lf.loop_filter_delta_enabled {
lf.loop_filter_delta_update = r.0.read_bit()?;
if lf.loop_filter_delta_update {
for i in 0..TOTAL_REFS_PER_FRAME {
let update_ref_delta = r.0.read_bit()?;
if update_ref_delta {
lf.loop_filter_ref_deltas[i] = r.read_su(7)? as i8;
}
}
for i in 0..2 {
let update_mode_delta = r.0.read_bit()?;
if update_mode_delta {
lf.loop_filter_mode_deltas[i] = r.read_su(7)? as i8;
}
}
}
}
Ok(())
}
fn parse_cdef_params(
r: &mut Reader,
fh: &mut FrameHeaderObu,
enable_cdef: bool,
num_planes: u32,
) -> Result<(), String> {
let cdef = &mut fh.cdef_params;
if fh.coded_lossless || fh.allow_intrabc || !enable_cdef {
cdef.cdef_bits = 0;
cdef.cdef_y_pri_strength[0] = 0;
cdef.cdef_y_sec_strength[0] = 0;
cdef.cdef_uv_pri_strength[0] = 0;
cdef.cdef_uv_sec_strength[0] = 0;
cdef.cdef_damping = 3;
return Ok(());
}
cdef.cdef_damping = r.0.read_bits::<u32>(2)? + 3;
cdef.cdef_bits = r.0.read_bits::<u32>(2)?;
for i in 0..(1 << cdef.cdef_bits) as usize {
cdef.cdef_y_pri_strength[i] = r.0.read_bits::<u32>(4)?;
cdef.cdef_y_sec_strength[i] = r.0.read_bits::<u32>(2)?;
if cdef.cdef_y_sec_strength[i] == 3 {
cdef.cdef_y_sec_strength[i] += 1;
}
if num_planes > 1 {
cdef.cdef_uv_pri_strength[i] = r.0.read_bits::<u32>(4)?;
cdef.cdef_uv_sec_strength[i] = r.0.read_bits::<u32>(2)?;
if cdef.cdef_uv_sec_strength[i] == 3 {
cdef.cdef_uv_sec_strength[i] += 1;
}
}
}
Ok(())
}
fn parse_loop_restoration_params(
r: &mut Reader,
fh: &mut FrameHeaderObu,
enable_restoration: bool,
num_planes: u32,
use_128x128_superblock: bool,
subsampling_x: bool,
subsampling_y: bool,
) -> Result<(), String> {
let lr = &mut fh.loop_restoration_params;
if fh.all_lossless || fh.allow_intrabc || !enable_restoration {
lr.frame_restoration_type[0] = FrameRestorationType::None;
lr.frame_restoration_type[1] = FrameRestorationType::None;
lr.frame_restoration_type[2] = FrameRestorationType::None;
lr.uses_lr = false;
return Ok(());
}
lr.uses_lr = false;
lr.uses_chroma_lr = false;
const REMAP_LR_TYPE: [FrameRestorationType; 4] = [
FrameRestorationType::None,
FrameRestorationType::Switchable,
FrameRestorationType::Wiener,
FrameRestorationType::Sgrproj,
];
for i in 0..num_planes as usize {
let lr_type = r.0.read_bits::<u32>(2)?;
lr.frame_restoration_type[i] = REMAP_LR_TYPE[lr_type as usize];
if lr.frame_restoration_type[i] != FrameRestorationType::None {
lr.uses_lr = true;
if i > 0 {
lr.uses_chroma_lr = true;
}
}
}
if lr.uses_lr {
if use_128x128_superblock {
lr.lr_unit_shift = r.0.read_bits::<u32>(1)? as u8 + 1;
} else {
lr.lr_unit_shift = r.0.read_bits::<u32>(1)? as u8;
if lr.lr_unit_shift > 0 {
lr.lr_unit_shift += r.0.read_bits::<u32>(1)? as u8;
}
}
lr.loop_restoration_size[0] = RESTORATION_TILESIZE_MAX >> (2 - lr.lr_unit_shift);
if subsampling_x && subsampling_y && lr.uses_chroma_lr {
lr.lr_uv_shift = r.0.read_bits::<u32>(1)? as u8;
} else {
lr.lr_uv_shift = 0;
}
lr.loop_restoration_size[1] = lr.loop_restoration_size[0] >> lr.lr_uv_shift;
lr.loop_restoration_size[2] = lr.loop_restoration_size[0] >> lr.lr_uv_shift;
}
Ok(())
}
fn read_tx_mode(r: &mut Reader, fh: &mut FrameHeaderObu) -> Result<(), String> {
if fh.coded_lossless {
fh.tx_mode = TxMode::Only4x4;
} else {
let tx_mode_select = r.0.read_bit()?;
if tx_mode_select {
fh.tx_mode = TxMode::Select;
} else {
fh.tx_mode = TxMode::Largest;
}
}
Ok(())
}
fn parse_skip_mode_params(
&self,
r: &mut Reader,
fh: &mut FrameHeaderObu,
enable_order_hint: bool,
order_hint_bits: i32,
) -> Result<(), String> {
let skip_mode_allowed;
if fh.frame_is_intra || !fh.reference_select || !enable_order_hint {
skip_mode_allowed = false;
} else {
let mut forward_idx = -1;
let mut backward_idx = -1;
let mut forward_hint = 0;
let mut backward_hint = 0;
for i in 0..REFS_PER_FRAME {
let ref_hint = self.ref_info[fh.ref_frame_idx[i] as usize].ref_order_hint;
if helpers::get_relative_dist(
enable_order_hint,
order_hint_bits,
ref_hint.try_into().unwrap(),
fh.order_hint.try_into().unwrap(),
) < 0
&& (forward_idx < 0
|| helpers::get_relative_dist(
enable_order_hint,
order_hint_bits,
ref_hint.try_into().unwrap(),
forward_hint,
) > 0)
{
forward_idx = i32::try_from(i).unwrap();
forward_hint = ref_hint.try_into().unwrap();
} else if helpers::get_relative_dist(
enable_order_hint,
order_hint_bits,
ref_hint.try_into().unwrap(),
fh.order_hint.try_into().unwrap(),
) > 0
&& (backward_idx < 0 || {
helpers::get_relative_dist(
enable_order_hint,
order_hint_bits,
ref_hint.try_into().unwrap(),
backward_hint,
) < 0
})
{
backward_idx = i32::try_from(i).unwrap();
backward_hint = ref_hint.try_into().unwrap();
}
}
if forward_idx < 0 {
skip_mode_allowed = false;
} else if backward_idx >= 0 {
skip_mode_allowed = true;
fh.skip_mode_frame[0] = ReferenceFrameType::Last as u32
+ u32::try_from(std::cmp::min(forward_idx, backward_idx)).unwrap();
fh.skip_mode_frame[1] = ReferenceFrameType::Last as u32
+ u32::try_from(std::cmp::max(forward_idx, backward_idx)).unwrap();
} else {
let mut second_forward_idx = -1;
let mut second_forward_hint = 0;
for i in 0..REFS_PER_FRAME {
let ref_hint = self.ref_info[fh.ref_frame_idx[i] as usize].ref_order_hint;
if helpers::get_relative_dist(
enable_order_hint,
order_hint_bits,
ref_hint.try_into().unwrap(),
forward_hint,
) < 0
&& (second_forward_idx < 0
|| helpers::get_relative_dist(
enable_order_hint,
order_hint_bits,
ref_hint.try_into().unwrap(),
second_forward_hint,
) > 0)
{
second_forward_idx = i32::try_from(i).unwrap();
second_forward_hint = ref_hint.try_into().unwrap();
}
}
if second_forward_idx < 0 {
skip_mode_allowed = false;
} else {
skip_mode_allowed = true;
fh.skip_mode_frame[0] = ReferenceFrameType::Last as u32
+ u32::try_from(std::cmp::min(forward_idx, second_forward_idx)).unwrap();
fh.skip_mode_frame[1] = ReferenceFrameType::Last as u32
+ u32::try_from(std::cmp::max(forward_idx, second_forward_idx)).unwrap();
}
}
}
if skip_mode_allowed {
fh.skip_mode_present = r.0.read_bit()?;
} else {
fh.skip_mode_present = false;
}
Ok(())
}
fn parse_frame_reference_mode(r: &mut Reader, fh: &mut FrameHeaderObu) -> Result<(), String> {
if fh.frame_is_intra {
fh.reference_select = false;
} else {
fh.reference_select = r.0.read_bit()?;
}
Ok(())
}
fn seg_feature_active_idx(seg: &SegmentationParams, idx: u32, feature: u32) -> bool {
seg.segmentation_enabled && seg.feature_enabled[idx as usize][feature as usize]
}
fn get_qindex(fh: &FrameHeaderObu, ignore_deltaq: bool, segment_id: u32) -> i32 {
let base_q_idx = i32::try_from(fh.quantization_params.base_q_idx).unwrap();
if Self::seg_feature_active_idx(&fh.segmentation_params, segment_id, SEG_LVL_ALT_Q as u32) {
let data = fh.segmentation_params.feature_data[segment_id as usize][SEG_LVL_ALT_Q];
let mut qindex = base_q_idx + i32::from(data);
if !ignore_deltaq && fh.quantization_params.delta_q_present {
qindex += i32::try_from(fh.quantization_params.delta_q_res).unwrap();
}
helpers::clip3(0, 255, qindex)
} else {
base_q_idx
}
}
fn setup_shear(warp_params: &[i32; 6]) -> Result<bool, String> {
let mut default = true;
for (i, param) in warp_params.iter().enumerate() {
let default_value = if i % 3 == 2 { 1 << WARPEDMODEL_PREC_BITS } else { 0 };
if *param != default_value {
default = false;
break;
}
}
if default {
return Ok(true);
}
let alpha0 = helpers::clip3(-32768, 32767, warp_params[2] - (1 << WARPEDMODEL_PREC_BITS));
let beta0 = helpers::clip3(-32768, 32767, warp_params[3]);
let (div_shift, div_factor) = helpers::resolve_divisor(warp_params[2])?;
let v = i64::from(warp_params[4] << WARPEDMODEL_PREC_BITS);
let v = (v * i64::from(div_factor)) as i32;
let gamma0 = helpers::clip3(-32678, 32767, helpers::round2signed(v, div_shift)?);
let w = warp_params[3] * warp_params[4];
let delta0 = helpers::clip3(
-32768,
32767,
warp_params[5]
- helpers::round2signed(w * div_factor, div_shift)?
- (1 << WARPEDMODEL_PREC_BITS),
);
let alpha =
helpers::round2signed(alpha0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS;
let beta = helpers::round2signed(beta0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS;
let gamma =
helpers::round2signed(gamma0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS;
let delta =
helpers::round2signed(delta0, WARP_PARAM_REDUCE_BITS)? << WARP_PARAM_REDUCE_BITS;
#[allow(clippy::needless_bool)]
let warp_valid = if 4 * alpha.abs() + 7 * beta.abs() >= (1 << WARPEDMODEL_PREC_BITS)
|| 4 * gamma.abs() + 4 * delta.abs() >= (1 << WARPEDMODEL_PREC_BITS)
{
false
} else {
true
};
Ok(warp_valid)
}
fn read_global_param(
reader: &mut Reader,
type_: WarpModelType,
ref_frame: usize,
idx: usize,
allow_high_precision_mv: bool,
prev_gm_params: &[[i32; 6]; NUM_REF_FRAMES],
gm_params: &mut [[i32; 6]; NUM_REF_FRAMES],
) -> Result<(), String> {
let mut abs_bits = GM_ABS_ALPHA_BITS;
let mut prec_bits = GM_ALPHA_PREC_BITS;
if idx < 2 {
if type_ == WarpModelType::Translation {
abs_bits = GM_ABS_TRANS_ONLY_BITS - !allow_high_precision_mv as u32;
prec_bits = GM_TRANS_ONLY_PREC_BITS - !allow_high_precision_mv as u32;
} else {
abs_bits = GM_ABS_TRANS_BITS;
prec_bits = GM_TRANS_PREC_BITS;
}
}
let prec_diff = WARPEDMODEL_PREC_BITS - prec_bits;
let (round, sub) =
if (idx % 3) == 2 { (1 << WARPEDMODEL_PREC_BITS, 1 << prec_bits) } else { (0, 0) };
let mx = 1 << abs_bits;
let r = (prev_gm_params[ref_frame][idx] >> prec_diff) - sub;
gm_params[ref_frame][idx] =
(reader.decode_signed_subexp_with_ref(-mx, mx + 1, r)? << prec_diff) + round;
Ok(())
}
fn parse_global_motion_params(
&mut self,
r: &mut Reader,
fh: &mut FrameHeaderObu,
) -> Result<(), String> {
let gm = &mut fh.global_motion_params;
let mut type_;
let mut prev_gm_params: [[i32; 6]; NUM_REF_FRAMES] = Default::default();
for ref_frame in ReferenceFrameType::Last as usize..=ReferenceFrameType::AltRef as usize {
gm.gm_type[ref_frame] = WarpModelType::Identity;
for i in 0..6 {
gm.gm_params[ref_frame][i] = if i % 3 == 2 { 1 << WARPEDMODEL_PREC_BITS } else { 0 }
}
gm.warp_valid[ref_frame] = true;
}
if fh.frame_is_intra {
return Ok(());
}
if fh.primary_ref_frame == PRIMARY_REF_NONE {
#[allow(clippy::needless_range_loop)]
for ref_frame in ReferenceFrameType::Last as usize..ReferenceFrameType::AltRef as usize
{
for i in 0..5 {
prev_gm_params[ref_frame][i] =
if i % 3 == 2 { 1 << WARPEDMODEL_PREC_BITS } else { 0 }
}
}
} else {
let prev_frame = fh.ref_frame_idx[fh.primary_ref_frame as usize];
prev_gm_params = self.ref_info[prev_frame as usize].global_motion_params.gm_params;
}
for ref_frame in ReferenceFrameType::Last as usize..=ReferenceFrameType::AltRef as usize {
gm.is_global[ref_frame] = r.0.read_bit()?;
if gm.is_global[ref_frame] {
gm.is_rot_zoom[ref_frame] = r.0.read_bit()?;
if gm.is_rot_zoom[ref_frame] {
type_ = WarpModelType::RotZoom;
} else {
gm.is_translation[ref_frame] = r.0.read_bit()?;
if gm.is_translation[ref_frame] {
type_ = WarpModelType::Translation;
} else {
type_ = WarpModelType::Affine;
}
}
} else {
type_ = WarpModelType::Identity;
}
gm.gm_type[ref_frame] = type_;
if gm.gm_type[ref_frame] as u32 >= WarpModelType::RotZoom as u32 {
Self::read_global_param(
r,
type_,
ref_frame,
2,
fh.allow_high_precision_mv,
&prev_gm_params,
&mut gm.gm_params,
)?;
Self::read_global_param(
r,
type_,
ref_frame,
3,
fh.allow_high_precision_mv,
&prev_gm_params,
&mut gm.gm_params,
)?;
if type_ == WarpModelType::Affine {
Self::read_global_param(
r,
type_,
ref_frame,
4,
fh.allow_high_precision_mv,
&prev_gm_params,
&mut gm.gm_params,
)?;
Self::read_global_param(
r,
type_,
ref_frame,
5,
fh.allow_high_precision_mv,
&prev_gm_params,
&mut gm.gm_params,
)?;
} else {
gm.gm_params[ref_frame][4] = -gm.gm_params[ref_frame][3];
gm.gm_params[ref_frame][5] = gm.gm_params[ref_frame][2];
}
}
if gm.gm_type[ref_frame] as u32 >= WarpModelType::Translation as u32 {
Self::read_global_param(
r,
type_,
ref_frame,
0,
fh.allow_high_precision_mv,
&prev_gm_params,
&mut gm.gm_params,
)?;
Self::read_global_param(
r,
type_,
ref_frame,
1,
fh.allow_high_precision_mv,
&prev_gm_params,
&mut gm.gm_params,
)?;
}
gm.warp_valid[ref_frame] = Self::setup_shear(&gm.gm_params[ref_frame])?;
}
Ok(())
}
fn parse_film_grain_parameters(
&self,
r: &mut Reader,
fh: &mut FrameHeaderObu,
film_grain_params_present: bool,
mono_chrome: bool,
subsampling_x: bool,
subsampling_y: bool,
) -> Result<(), String> {
let fg = &mut fh.film_grain_params;
if !film_grain_params_present || (!fh.show_frame && !fh.showable_frame) {
*fg = Default::default();
return Ok(());
}
fg.apply_grain = r.0.read_bit()?;
if !fg.apply_grain {
*fg = Default::default();
return Ok(());
}
fg.grain_seed = r.0.read_bits::<u32>(16)? as u16;
if fh.frame_type == FrameType::InterFrame {
fg.update_grain = r.0.read_bit()?;
} else {
fg.update_grain = true;
}
if !fg.update_grain {
fg.film_grain_params_ref_idx = r.0.read_bits::<u32>(3)? as u8;
let temp_grain_seed = fg.grain_seed;
if !fh
.ref_frame_idx
.iter()
.any(|&ref_frame_idx| ref_frame_idx == fg.film_grain_params_ref_idx)
{
return Err("Invalid film_grain_params_ref_idx".into());
}
*fg = self.ref_info[fg.film_grain_params_ref_idx as usize].film_grain_params.clone();
fg.grain_seed = temp_grain_seed;
return Ok(());
}
fg.num_y_points = r.0.read_bits::<u32>(4)? as u8;
fg.point_y_value
.iter_mut()
.zip(fg.point_y_scaling.iter_mut())
.take(fg.num_y_points as usize)
.try_for_each(|(point_y_value, point_y_scaling)| {
*point_y_value = r.0.read_bits::<u32>(8)? as u8;
*point_y_scaling = r.0.read_bits::<u32>(8)? as u8;
Ok::<_, String>(())
})?;
if mono_chrome {
fg.chroma_scaling_from_luma = false;
} else {
fg.chroma_scaling_from_luma = r.0.read_bit()?;
}
if mono_chrome
|| fg.chroma_scaling_from_luma
|| (subsampling_x && subsampling_y && fg.num_y_points == 0)
{
fg.num_cb_points = 0;
fg.num_cr_points = 0;
} else {
fg.num_cb_points = r.0.read_bits::<u32>(4)? as u8;
if fg.num_cb_points > 10 {
return Err(format!("Invalid num_cb_points {}", fg.num_cb_points));
}
for i in 0..fg.num_cb_points as usize {
fg.point_cb_value[i] = r.0.read_bits::<u32>(8)? as u8;
if i > 0 && fg.point_cb_value[i - 1] >= fg.point_cb_value[i] {
return Err(format!("Invalid point_cb_value[{}] {}", i, fg.point_cb_value[i]));
}
fg.point_cb_scaling[i] = r.0.read_bits::<u32>(8)? as u8;
}
fg.num_cr_points = r.0.read_bits::<u32>(4)? as u8;
for i in 0..fg.num_cr_points as usize {
fg.point_cr_value[i] = r.0.read_bits::<u32>(8)? as u8;
if i > 0 && fg.point_cr_value[i - 1] >= fg.point_cr_value[i] {
return Err(format!("Invalid point_cr_value[{}] {}", i, fg.point_cr_value[i]));
}
fg.point_cr_scaling[i] = r.0.read_bits::<u32>(8)? as u8;
}
}
fg.grain_scaling_minus_8 = r.0.read_bits::<u32>(2)? as u8;
fg.ar_coeff_lag = r.0.read_bits::<u32>(2)?;
let num_pos_luma = 2 * fg.ar_coeff_lag * (fg.ar_coeff_lag + 1);
let num_pos_chroma = if fg.num_y_points > 0 {
for i in 0..num_pos_luma as usize {
fg.ar_coeffs_y_plus_128[i] = r.0.read_bits::<u32>(8)? as u8;
}
num_pos_luma + 1
} else {
num_pos_luma
};
if fg.chroma_scaling_from_luma || fg.num_cb_points > 0 {
for i in 0..num_pos_chroma as usize {
fg.ar_coeffs_cb_plus_128[i] = r.0.read_bits::<u32>(8)? as u8;
}
}
if fg.chroma_scaling_from_luma || fg.num_cr_points > 0 {
for i in 0..num_pos_chroma as usize {
fg.ar_coeffs_cr_plus_128[i] = r.0.read_bits::<u32>(8)? as u8;
}
}
fg.ar_coeff_shift_minus_6 = r.0.read_bits::<u32>(2)? as u8;
fg.grain_scale_shift = r.0.read_bits::<u32>(2)? as u8;
if fg.num_cb_points > 0 {
fg.cb_mult = r.0.read_bits::<u32>(8)? as u8;
fg.cb_luma_mult = r.0.read_bits::<u32>(8)? as u8;
fg.cb_offset = r.0.read_bits::<u32>(9)? as u16;
}
if fg.num_cr_points > 0 {
fg.cr_mult = r.0.read_bits::<u32>(8)? as u8;
fg.cr_luma_mult = r.0.read_bits::<u32>(8)? as u8;
fg.cr_offset = r.0.read_bits::<u32>(9)? as u16;
}
fg.overlap_flag = r.0.read_bit()?;
fg.clip_to_restricted_range = r.0.read_bit()?;
Ok(())
}
fn sequence(&self) -> Result<&SequenceHeaderObu, String> {
let Some(seq) = self.sequence_header.as_ref() else {
return Err("No sequence header parsed yet".into());
};
Ok(seq)
}
fn parse_uncompressed_frame_header(&mut self, obu: &Obu) -> Result<FrameHeaderObu, String> {
let mut r = Reader::new(obu.as_ref());
let mut fh = FrameHeaderObu { obu_header: obu.header.clone(), ..Default::default() };
let &SequenceHeaderObu {
operating_points_cnt_minus_1,
seq_force_integer_mv,
additional_frame_id_length_minus_1,
delta_frame_id_length_minus_2,
decoder_model_info_present_flag,
reduced_still_picture_header,
frame_id_numbers_present_flag,
use_128x128_superblock,
enable_order_hint,
seq_force_screen_content_tools,
order_hint_bits,
enable_cdef,
enable_restoration,
enable_warped_motion,
color_config:
ColorConfig { subsampling_x, subsampling_y, separate_uv_delta_q, mono_chrome, .. },
timing_info: TimingInfo { equal_picture_interval, .. },
decoder_model_info:
DecoderModelInfo {
frame_presentation_time_length_minus_1,
buffer_removal_time_length_minus_1,
..
},
num_planes,
film_grain_params_present,
..
} = self.sequence()?;
let mut id_len = 0;
if frame_id_numbers_present_flag {
id_len = additional_frame_id_length_minus_1 + delta_frame_id_length_minus_2 + 3;
}
const ALL_FRAMES: u32 = (1 << NUM_REF_FRAMES) - 1;
if reduced_still_picture_header {
fh.show_existing_frame = false;
fh.frame_type = FrameType::KeyFrame;
fh.frame_is_intra = true;
fh.show_frame = true;
fh.showable_frame = false;
} else {
fh.show_existing_frame = r.0.read_bit()?;
if matches!(obu.header.obu_type, ObuType::Frame) && fh.show_existing_frame {
return Err("If obu_type is equal to OBU_FRAME, it is a requirement of bitstream conformance that show_existing_frame is equal to 0.".into());
}
if fh.show_existing_frame {
fh.frame_to_show_map_idx = r.0.read_bits::<u32>(3)? as u8;
if decoder_model_info_present_flag && !equal_picture_interval {
fh.frame_presentation_time =
r.0.read_bits::<u32>(frame_presentation_time_length_minus_1 as usize + 1)?;
}
let ref_frame = &self.ref_info[fh.frame_to_show_map_idx as usize];
fh.refresh_frame_flags = 0;
if frame_id_numbers_present_flag {
if id_len == 0 {
return Err(format!("Invalid id_len {}", id_len));
}
fh.display_frame_id = r.0.read_bits::<u32>(id_len.try_into().unwrap())?;
if ref_frame.display_frame_id != fh.display_frame_id || !ref_frame.ref_valid {
return Err("Invalid display_frame_id".into());
}
}
if !ref_frame.showable_frame {
return Err("Invalid bitstream: can't show this past frame".into());
}
self.load_reference_frame(&mut fh)?;
if fh.frame_type == FrameType::KeyFrame {
fh.refresh_frame_flags = ALL_FRAMES;
}
if film_grain_params_present {
fh.film_grain_params =
self.ref_info[fh.frame_to_show_map_idx as usize].film_grain_params.clone();
}
if matches!(obu.header.obu_type, ObuType::Frame) {
r.byte_alignment()?;
}
fh.header_bytes = usize::try_from(r.0.position() / 8).unwrap();
return Ok(fh);
}
fh.frame_type = FrameType::try_from(r.0.read_bits::<u32>(2)?)?;
fh.frame_is_intra =
matches!(fh.frame_type, FrameType::IntraOnlyFrame | FrameType::KeyFrame);
fh.show_frame = r.0.read_bit()?;
if fh.show_frame && decoder_model_info_present_flag && equal_picture_interval {
fh.frame_presentation_time =
r.0.read_bits::<u32>(frame_presentation_time_length_minus_1 as usize + 1)?;
}
if fh.show_frame {
fh.showable_frame = !matches!(fh.frame_type, FrameType::KeyFrame);
} else {
fh.showable_frame = r.0.read_bit()?;
}
if fh.frame_type == FrameType::SwitchFrame
|| (fh.frame_type == FrameType::KeyFrame && fh.show_frame)
{
fh.error_resilient_mode = true;
} else {
fh.error_resilient_mode = r.0.read_bit()?;
}
}
if fh.frame_type == FrameType::KeyFrame && fh.show_frame {
for i in 0..NUM_REF_FRAMES {
self.ref_info[i].ref_valid = false;
self.ref_info[i].ref_order_hint = 0;
}
for i in 0..REFS_PER_FRAME {
fh.order_hints[ReferenceFrameType::Last as usize + i] = 0;
}
}
fh.disable_cdf_update = r.0.read_bit()?;
if seq_force_screen_content_tools == SELECT_SCREEN_CONTENT_TOOLS as u32 {
fh.allow_screen_content_tools = r.0.read_bit()? as u32;
} else {
fh.allow_screen_content_tools = seq_force_screen_content_tools;
}
if fh.allow_screen_content_tools > 0 {
if seq_force_integer_mv == SELECT_INTEGER_MV as u32 {
fh.force_integer_mv = r.0.read_bit()? as u32;
} else {
fh.force_integer_mv = seq_force_integer_mv;
}
} else {
fh.force_integer_mv = 0;
}
if fh.frame_is_intra {
fh.force_integer_mv = 1;
}
if frame_id_numbers_present_flag {
self.prev_frame_id = self.current_frame_id;
self.current_frame_id = r.0.read_bits::<u32>(id_len.try_into().unwrap())?;
fh.current_frame_id = self.current_frame_id;
let have_prev_frame_id =
!(self.is_first_frame || fh.frame_type == FrameType::KeyFrame && fh.show_frame);
if have_prev_frame_id {
let frame_id_length =
additional_frame_id_length_minus_1 + delta_frame_id_length_minus_2 + 3;
let diff_frame_id = if self.current_frame_id > self.prev_frame_id {
self.current_frame_id - self.prev_frame_id
} else {
if frame_id_length > 16 {
return Err(format!("Invalid frame_id_length {}", frame_id_length));
}
(1 << frame_id_length) + self.current_frame_id - self.prev_frame_id
};
if self.prev_frame_id == self.current_frame_id
|| diff_frame_id >= (1 << (frame_id_length - 1))
{
return Err(format!(
"Invalid frame_id: prev_frame_id = {}, current_frame_id = {}",
self.prev_frame_id, self.current_frame_id
));
}
}
let diff_len = delta_frame_id_length_minus_2 + 2;
let shifted_diff_len = 1 << diff_len;
let shifted_id_len = 1 << id_len;
for i in 0..NUM_REF_FRAMES {
if self.current_frame_id > shifted_diff_len {
if self.ref_info[i].ref_frame_id > self.current_frame_id
|| self.ref_info[i].ref_frame_id
< (self.current_frame_id - shifted_diff_len)
{
self.ref_info[i].ref_valid = false;
}
} else if self.ref_info[i].ref_frame_id > self.current_frame_id
&& self.ref_info[i].ref_frame_id
< shifted_id_len + self.current_frame_id - shifted_diff_len
{
self.ref_info[i].ref_valid = false;
}
}
} else {
self.current_frame_id = 0;
self.prev_frame_id = self.current_frame_id;
fh.current_frame_id = self.current_frame_id;
}
if fh.frame_type == FrameType::SwitchFrame {
fh.frame_size_override_flag = true;
} else if reduced_still_picture_header {
fh.frame_size_override_flag = false;
} else {
fh.frame_size_override_flag = r.0.read_bit()?;
}
fh.order_hint = r.0.read_bits::<u32>(order_hint_bits.try_into().unwrap())?;
if fh.frame_is_intra || fh.error_resilient_mode {
fh.primary_ref_frame = PRIMARY_REF_NONE;
} else {
fh.primary_ref_frame = r.0.read_bits::<u32>(3)?;
}
let operating_points = &self.sequence()?.operating_points;
if decoder_model_info_present_flag {
fh.buffer_removal_time_present_flag = r.0.read_bit()?;
if fh.buffer_removal_time_present_flag {
#[allow(clippy::needless_range_loop)]
for op_num in 0..=operating_points_cnt_minus_1 as usize {
if operating_points[op_num].decoder_model_present_for_this_op {
let op_pt_idc = operating_points[op_num].idc;
let in_temporal_layer = (op_pt_idc >> fh.obu_header.temporal_id) & 1 != 0;
let in_spatial_layer =
(op_pt_idc >> (fh.obu_header.spatial_id + 8)) & 1 != 0;
if op_pt_idc == 0 || (in_temporal_layer && in_spatial_layer) {
let n = buffer_removal_time_length_minus_1 + 1;
fh.buffer_removal_time[op_num] = r.0.read_bits::<u32>(n as usize)?;
}
}
}
}
}
fh.allow_high_precision_mv = false;
fh.use_ref_frame_mvs = false;
fh.allow_intrabc = false;
if fh.frame_type == FrameType::SwitchFrame
|| (fh.frame_type == FrameType::KeyFrame && fh.show_frame)
{
fh.refresh_frame_flags = ALL_FRAMES;
} else {
fh.refresh_frame_flags = r.0.read_bits::<u32>(8)?;
}
if (!fh.frame_is_intra || fh.refresh_frame_flags != ALL_FRAMES)
&& fh.error_resilient_mode
&& enable_order_hint
{
for i in 0..NUM_REF_FRAMES {
fh.ref_order_hint[i] = r.0.read_bits::<u32>(order_hint_bits.try_into().unwrap())?;
if fh.ref_order_hint[i] != self.ref_info[i].ref_order_hint {
self.ref_info[i].ref_valid = false;
}
}
}
if fh.frame_is_intra {
self.parse_frame_size(&mut fh, &mut r)?;
Self::parse_render_size(&mut fh, &mut r)?;
if fh.allow_screen_content_tools > 0 && fh.upscaled_width == fh.frame_width {
fh.allow_intrabc = r.0.read_bit()?;
}
} else {
if !enable_order_hint {
fh.frame_refs_short_signaling = false;
} else {
fh.frame_refs_short_signaling = r.0.read_bit()?;
if fh.frame_refs_short_signaling {
fh.last_frame_idx = r.0.read_bits::<u32>(3)? as u8;
fh.gold_frame_idx = r.0.read_bits::<u32>(3)? as u8;
let ref_order_hints = self
.ref_info
.iter()
.map(|i| i.ref_order_hint)
.collect::<Vec<_>>()
.try_into()
.unwrap();
self.set_frame_refs(&mut fh, &ref_order_hints)?;
}
}
let mut expected_frame_id = [0; REFS_PER_FRAME];
#[allow(clippy::needless_range_loop)]
for i in 0..REFS_PER_FRAME {
if !fh.frame_refs_short_signaling {
fh.ref_frame_idx[i] = r.0.read_bits::<u32>(3)?.try_into().unwrap();
}
if frame_id_numbers_present_flag {
let delta_frame_id =
r.0.read_bits::<u32>(delta_frame_id_length_minus_2 as usize + 2)? + 1;
if id_len == 0 {
return Err(format!("Invalid id_len {}", id_len));
}
let shifted_id_len = 1 << id_len;
expected_frame_id[i] =
(self.current_frame_id + shifted_id_len - delta_frame_id) % shifted_id_len;
let actual_frame_id = self.ref_info[fh.ref_frame_idx[i] as usize].ref_frame_id;
if expected_frame_id[i] != actual_frame_id {
return Err(format!(
"Invalid frame id, expected {} got {}",
expected_frame_id[i], actual_frame_id
));
}
}
}
if fh.frame_size_override_flag && !fh.error_resilient_mode {
self.frame_size_with_refs(&mut fh, &mut r)?;
} else {
self.parse_frame_size(&mut fh, &mut r)?;
Self::parse_render_size(&mut fh, &mut r)?;
}
if fh.force_integer_mv > 0 {
fh.allow_high_precision_mv = false;
} else {
fh.allow_high_precision_mv = r.0.read_bit()?;
}
fh.is_filter_switchable = r.0.read_bit()?;
if fh.is_filter_switchable {
fh.interpolation_filter = InterpolationFilter::Switchable;
} else {
fh.interpolation_filter = InterpolationFilter::try_from(r.0.read_bits::<u32>(2)?)?;
}
fh.is_motion_mode_switchable = r.0.read_bit()?;
if fh.error_resilient_mode || !self.sequence()?.enable_ref_frame_mvs {
fh.use_ref_frame_mvs = false;
} else {
fh.use_ref_frame_mvs = r.0.read_bit()?;
}
for i in 0..REFS_PER_FRAME {
let ref_frame = ReferenceFrameType::Last as usize + i;
let hint = self.ref_info[fh.ref_frame_idx[i] as usize].ref_order_hint;
fh.order_hints[ref_frame] = hint;
if !enable_order_hint {
fh.ref_frame_sign_bias[i] = false;
} else {
fh.ref_frame_sign_bias[i] = helpers::get_relative_dist(
enable_order_hint,
order_hint_bits,
hint.try_into().unwrap(),
fh.order_hint.try_into().unwrap(),
) > 0;
}
}
}
if reduced_still_picture_header || fh.disable_cdf_update {
fh.disable_frame_end_update_cdf = true;
} else {
fh.disable_frame_end_update_cdf = r.0.read_bit()?;
}
if fh.primary_ref_frame == PRIMARY_REF_NONE {
Self::setup_past_independence(&mut fh);
} else {
let prev_frame =
&self.ref_info[fh.ref_frame_idx[fh.primary_ref_frame as usize] as usize];
if !prev_frame.ref_valid {
return Err("Reference is invalid".into());
}
fh.loop_filter_params.loop_filter_ref_deltas =
prev_frame.loop_filter_params.loop_filter_ref_deltas;
fh.loop_filter_params.loop_filter_mode_deltas =
prev_frame.loop_filter_params.loop_filter_mode_deltas;
fh.segmentation_params.feature_enabled = prev_frame.segmentation_params.feature_enabled;
fh.segmentation_params.feature_data = prev_frame.segmentation_params.feature_data;
}
self.parse_tile_info(&mut r, &mut fh.tile_info)?;
Self::parse_quantization_params(
&mut r,
&mut fh.quantization_params,
num_planes,
separate_uv_delta_q,
)?;
self.parse_segmentation_params(&mut r, &mut fh)?;
Self::parse_delta_q_params(&mut r, &mut fh.quantization_params)?;
Self::parse_delta_lf_params(
&mut r,
&mut fh.loop_filter_params,
fh.quantization_params.delta_q_present,
fh.allow_intrabc,
)?;
fh.coded_lossless = true;
for segment_id in 0..MAX_SEGMENTS {
let q_index = Self::get_qindex(&fh, true, segment_id as _);
let q = &fh.quantization_params;
fh.lossless_array[segment_id] = q_index == 0
&& q.delta_q_y_dc == 0
&& q.delta_q_u_ac == 0
&& q.delta_q_u_dc == 0
&& q.delta_q_v_ac == 0
&& q.delta_q_v_dc == 0;
if !fh.lossless_array[segment_id] {
fh.coded_lossless = false;
}
if q.using_qmatrix {
if fh.lossless_array[segment_id] {
fh.seg_qm_level[0][segment_id] = 15;
fh.seg_qm_level[1][segment_id] = 15;
fh.seg_qm_level[2][segment_id] = 15;
} else {
fh.seg_qm_level[0][segment_id] = q.qm_y;
fh.seg_qm_level[1][segment_id] = q.qm_u;
fh.seg_qm_level[2][segment_id] = q.qm_v;
}
}
}
fh.all_lossless = fh.coded_lossless && fh.frame_width == fh.upscaled_width;
Self::parse_loop_filter_parameters(&mut r, &mut fh, num_planes)?;
Self::parse_cdef_params(&mut r, &mut fh, enable_cdef, num_planes)?;
Self::parse_loop_restoration_params(
&mut r,
&mut fh,
enable_restoration,
num_planes,
use_128x128_superblock,
subsampling_x,
subsampling_y,
)?;
Self::read_tx_mode(&mut r, &mut fh)?;
Self::parse_frame_reference_mode(&mut r, &mut fh)?;
self.parse_skip_mode_params(&mut r, &mut fh, enable_order_hint, order_hint_bits)?;
if fh.frame_is_intra || fh.error_resilient_mode || !enable_warped_motion {
fh.allow_warped_motion = false;
} else {
fh.allow_warped_motion = r.0.read_bit()?;
}
fh.reduced_tx_set = r.0.read_bit()?;
self.parse_global_motion_params(&mut r, &mut fh)?;
self.parse_film_grain_parameters(
&mut r,
&mut fh,
film_grain_params_present,
mono_chrome,
subsampling_x,
subsampling_y,
)?;
Self::skip_and_check_trailing_bits(&mut r, obu)?;
if matches!(obu.header.obu_type, ObuType::Frame) {
r.byte_alignment()?;
}
fh.header_bytes = usize::try_from(r.0.position() / 8).unwrap();
Ok(fh)
}
fn parse_tile_group_obu<'a>(&mut self, obu: Obu<'a>) -> Result<TileGroupObu<'a>, String> {
let mut tg = TileGroupObu { obu, ..Default::default() };
let mut r = Reader::new(tg.obu.as_ref());
if r.0.num_bits_left() % 8 != 0 {
return Err("Bitstream is not byte aligned".into());
}
let mut sz: u64 = r.0.num_bits_left() as u64 / 8;
let num_tiles = self.tile_rows * self.tile_cols;
let start_bit_pos = r.0.position();
if num_tiles > 1 {
tg.tile_start_and_end_present_flag = r.0.read_bit()?;
}
if num_tiles == 1 || !tg.tile_start_and_end_present_flag {
tg.tg_start = 0;
tg.tg_end = num_tiles - 1;
} else {
let tile_bits = (self.tile_cols_log2 + self.tile_rows_log2) as usize;
tg.tg_start = r.0.read_bits::<u32>(tile_bits)?;
tg.tg_end = r.0.read_bits::<u32>(tile_bits)?;
}
r.byte_alignment()?;
let end_bit_pos = r.0.position();
let header_bytes = (end_bit_pos - start_bit_pos) / 8;
sz -= header_bytes;
let mut tile_num = tg.tg_start;
while tile_num <= tg.tg_end {
let tile_row = tile_num / self.tile_cols;
let tile_col = tile_num % self.tile_cols;
let last_tile = tile_num == tg.tg_end;
let tile_size;
if last_tile {
tile_size = u32::try_from(sz).unwrap();
} else {
tile_size = r.0.read_le::<u32>(self.tile_size_bytes.try_into().unwrap())? + 1;
sz -= u64::from(tile_size + self.tile_size_bytes);
}
let tile = Tile {
tile_offset: u32::try_from(r.0.position()).unwrap() / 8,
tile_size,
tile_row,
tile_col,
mi_row_start: self.mi_row_starts[tile_row as usize],
mi_row_end: self.mi_row_starts[tile_row as usize + 1],
mi_col_start: self.mi_row_starts[tile_col as usize],
mi_col_end: self.mi_row_starts[tile_col as usize + 1],
};
tg.tiles.push(tile);
if tile_num < tg.tg_end {
r.0.skip_bits(tile_size as usize * 8)?;
}
tile_num += 1;
}
if tg.tg_end == num_tiles - 1 {
self.seen_frame_header = false;
}
Ok(tg)
}
fn parse_frame_obu<'a>(&mut self, obu: Obu<'a>) -> Result<FrameObu<'a>, String> {
if !matches!(obu.header.obu_type, ObuType::Frame) {
return Err(format!("Expected a FrameOBU, got {:?}", obu.header.obu_type));
}
let frame_header_obu = self.parse_frame_header_obu(&obu)?;
let obu = Obu {
header: obu.header,
data: match obu.data {
Cow::Borrowed(d) => Cow::Borrowed(&d[frame_header_obu.header_bytes..]),
Cow::Owned(d) => Cow::Owned(d[frame_header_obu.header_bytes..].to_owned()),
},
bytes_used: obu.bytes_used,
};
let tile_group_obu = self.parse_tile_group_obu(obu)?;
Ok(FrameObu { header: frame_header_obu, tile_group: tile_group_obu })
}
pub fn parse_frame_header_obu(&mut self, obu: &Obu) -> Result<FrameHeaderObu, String> {
if !matches!(obu.header.obu_type, ObuType::FrameHeader | ObuType::Frame) {
return Err(format!("Expected a FrameHeaderOBU, got {:?}", obu.header.obu_type));
}
if self.seen_frame_header {
Ok(self
.last_frame_header
.clone()
.take()
.ok_or::<String>("Broken stream: no previous frame header to copy".into())?)
} else {
self.seen_frame_header = true;
let header = self.parse_uncompressed_frame_header(obu)?;
if header.show_existing_frame {
self.last_frame_header = None;
self.seen_frame_header = false;
} else {
self.seen_frame_header = true;
self.last_frame_header = Some(header.clone());
}
Ok(header)
}
}
pub fn ref_frame_update(&mut self, fh: &FrameHeaderObu) -> Result<(), String> {
if fh.show_existing_frame && !matches!(fh.frame_type, FrameType::KeyFrame) {
return Ok(());
}
if matches!(fh.frame_type, FrameType::IntraOnlyFrame) && fh.refresh_frame_flags == 0xff {
return Err("Intra-only frames cannot refresh all of the DPB as per the spec.".into());
}
let &SequenceHeaderObu {
color_config: ColorConfig { subsampling_x, subsampling_y, .. },
film_grain_params_present,
bit_depth,
..
} = self.sequence()?;
for (i, ref_info) in self.ref_info.iter_mut().enumerate() {
if ((fh.refresh_frame_flags >> i) & 1) != 0 {
ref_info.ref_valid = true;
ref_info.ref_frame_id = fh.current_frame_id;
ref_info.ref_frame_type = fh.frame_type;
ref_info.ref_upscaled_width = fh.upscaled_width;
ref_info.ref_frame_width = fh.frame_width;
ref_info.ref_frame_height = fh.frame_height;
ref_info.ref_render_width = fh.render_width;
ref_info.ref_render_height = fh.render_height;
ref_info.ref_order_hint = fh.order_hint;
ref_info.ref_mi_cols = self.mi_cols;
ref_info.ref_mi_rows = self.mi_rows;
ref_info.ref_subsampling_x = subsampling_x;
ref_info.ref_subsampling_y = subsampling_y;
ref_info.ref_bit_depth = bit_depth;
ref_info.segmentation_params = fh.segmentation_params.clone();
ref_info.global_motion_params = fh.global_motion_params.clone();
ref_info.loop_filter_params = fh.loop_filter_params.clone();
ref_info.tile_info = fh.tile_info.clone();
ref_info.display_frame_id = fh.display_frame_id;
ref_info.showable_frame = fh.showable_frame;
if film_grain_params_present {
ref_info.film_grain_params = fh.film_grain_params.clone();
}
}
}
Ok(())
}
pub fn highest_operating_point(&self) -> Option<u32> {
if self.operating_point_idc == 0 {
None
} else {
Some(helpers::floor_log2((self.operating_point_idc >> 8) as u32))
}
}
pub fn parse_obu<'a>(&mut self, obu: Obu<'a>) -> Result<ParsedObu<'a>, String> {
match obu.header.obu_type {
ObuType::Reserved => Ok(ParsedObu::Reserved),
ObuType::SequenceHeader => {
self.parse_sequence_header_obu(&obu).map(ParsedObu::SequenceHeader)
}
ObuType::TemporalDelimiter => {
self.parse_temporal_delimiter_obu().map(|_| ParsedObu::TemporalDelimiter)
}
ObuType::FrameHeader => self.parse_frame_header_obu(&obu).map(ParsedObu::FrameHeader),
ObuType::TileGroup => self.parse_tile_group_obu(obu).map(ParsedObu::TileGroup),
ObuType::Metadata => Ok(ParsedObu::Metadata),
ObuType::Frame => self.parse_frame_obu(obu).map(ParsedObu::Frame),
ObuType::RedundantFrameHeader => Ok(ParsedObu::RedundantFrameHeader),
ObuType::TileList => Ok(ParsedObu::TileList),
ObuType::Reserved2 => Ok(ParsedObu::Reserved2),
ObuType::Reserved3 => Ok(ParsedObu::Reserved3),
ObuType::Reserved4 => Ok(ParsedObu::Reserved4),
ObuType::Reserved5 => Ok(ParsedObu::Reserved5),
ObuType::Reserved6 => Ok(ParsedObu::Reserved6),
ObuType::Reserved7 => Ok(ParsedObu::Reserved7),
ObuType::Padding => Ok(ParsedObu::Padding),
}
}
}
impl Default for Parser {
fn default() -> Self {
Self {
stream_format: StreamFormat::LowOverhead,
operating_point: Default::default(),
seen_frame_header: Default::default(),
last_frame_header: Default::default(),
operating_point_idc: Default::default(),
should_probe_for_annexb: true,
is_first_frame: Default::default(),
mi_cols: Default::default(),
mi_rows: Default::default(),
prev_frame_id: Default::default(),
current_frame_id: Default::default(),
ref_info: Default::default(),
mi_col_starts: [0; MAX_TILE_COLS + 1],
mi_row_starts: [0; MAX_TILE_ROWS + 1],
tile_cols_log2: Default::default(),
tile_cols: Default::default(),
tile_rows_log2: Default::default(),
tile_rows: Default::default(),
tile_size_bytes: Default::default(),
sequence_header: Default::default(),
}
}
}
impl Clone for Parser {
fn clone(&self) -> Self {
let sequence_header = self.sequence_header.as_ref().map(|s| Rc::new((**s).clone()));
Self {
stream_format: self.stream_format.clone(),
operating_point: self.operating_point,
seen_frame_header: self.seen_frame_header,
last_frame_header: self.last_frame_header.clone(),
operating_point_idc: self.operating_point_idc,
should_probe_for_annexb: self.should_probe_for_annexb,
is_first_frame: self.is_first_frame,
ref_info: self.ref_info.clone(),
mi_cols: self.mi_cols,
mi_rows: self.mi_rows,
prev_frame_id: self.prev_frame_id,
current_frame_id: self.current_frame_id,
mi_col_starts: self.mi_col_starts,
mi_row_starts: self.mi_row_starts,
tile_cols_log2: self.tile_cols_log2,
tile_cols: self.tile_cols,
tile_rows_log2: self.tile_rows_log2,
tile_rows: self.tile_rows,
tile_size_bytes: self.tile_size_bytes,
sequence_header,
}
}
}
#[cfg(test)]
mod tests {
use crate::bitstream_utils::IvfIterator;
use crate::codec::av1::parser::{ObuAction, Parser, StreamFormat};
use super::ObuType;
const STREAM_TEST_25_FPS: &[u8] = include_bytes!("test_data/test-25fps.ivf.av1");
const STREAM_ANNEXB: &[u8] = include_bytes!("test_data/av1-annexb.ivf.av1");
#[test]
fn parse_test25fps() {
let mut parser = Parser::default();
let ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS);
let mut num_obus = 0;
for packet in ivf_iter {
let mut consumed = 0;
while let Ok(obu) = parser.read_obu(&packet[consumed..]) {
let obu = match obu {
ObuAction::Process(obu) => obu,
ObuAction::Drop(length) => {
consumed += usize::try_from(length).unwrap();
continue;
}
};
consumed += obu.bytes_used;
num_obus += 1;
}
}
assert_eq!(num_obus, 525);
}
#[test]
fn parse_annexb() {
let mut parser = Parser::default();
let mut ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS);
let packet = ivf_iter.next().unwrap();
parser.read_obu(packet).unwrap();
assert!(matches!(parser.stream_format, StreamFormat::LowOverhead));
let mut parser = Parser::default();
let mut ivf_iter = IvfIterator::new(STREAM_ANNEXB);
let packet = ivf_iter.next().unwrap();
parser.read_obu(packet).unwrap();
assert!(matches!(parser.stream_format, StreamFormat::AnnexB { .. }));
}
#[test]
fn parse_annexb_full() {
let mut parser = Parser::default();
let ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS);
for packet in ivf_iter {
let mut consumed = 0;
while let Ok(obu) = parser.read_obu(&packet[consumed..]) {
let obu = match obu {
ObuAction::Process(obu) => obu,
ObuAction::Drop(length) => {
consumed += usize::try_from(length).unwrap();
continue;
}
};
assert!(matches!(parser.stream_format, StreamFormat::LowOverhead));
consumed += obu.bytes_used;
}
}
let mut parser = Parser::default();
let ivf_iter = IvfIterator::new(STREAM_ANNEXB);
let mut num_obus = 0;
for packet in ivf_iter {
let mut consumed = 0;
while let Ok(obu) = parser.read_obu(&packet[consumed..]) {
let obu = match obu {
ObuAction::Process(obu) => obu,
ObuAction::Drop(length) => {
consumed += usize::try_from(length).unwrap();
continue;
}
};
assert!(matches!(parser.stream_format, StreamFormat::AnnexB { .. }));
consumed += obu.bytes_used;
num_obus += 1;
}
}
assert_eq!(num_obus, 3);
let annexb_state = match parser.stream_format {
StreamFormat::AnnexB(annexb_state) => annexb_state,
_ => panic!("Wrong StreamFormat, expected AnnexB"),
};
assert_eq!(annexb_state.temporal_unit_consumed, annexb_state.temporal_unit_size);
assert_eq!(annexb_state.frame_unit_consumed, annexb_state.frame_unit_size);
}
#[test]
fn parse_test25fps_obus() {
let mut parser = Parser::default();
let ivf_iter = IvfIterator::new(STREAM_TEST_25_FPS);
for packet in ivf_iter {
let mut consumed = 0;
while let Ok(obu) = parser.read_obu(&packet[consumed..]) {
let obu = match obu {
ObuAction::Process(obu) => obu,
ObuAction::Drop(length) => {
consumed += usize::try_from(length).unwrap();
continue;
}
};
let data_len = obu.bytes_used;
match obu.header.obu_type {
ObuType::SequenceHeader => {
parser.parse_sequence_header_obu(&obu).unwrap();
}
ObuType::FrameHeader | ObuType::RedundantFrameHeader => {
let fh = parser.parse_frame_header_obu(&obu).unwrap();
parser.ref_frame_update(&fh).unwrap();
}
ObuType::TileGroup => {
parser.parse_tile_group_obu(obu).unwrap();
}
ObuType::Frame => {
let frame = parser.parse_frame_obu(obu).unwrap();
parser.ref_frame_update(&frame.header).unwrap();
}
_ => {}
};
consumed += data_len;
}
}
}
}