blob: 7a668f31b7aacfb568a222b0b8dbf21e86921316 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
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;
// Same as Segmentation_Feature_Bits in the specification. See 5.9.14
pub const FEATURE_BITS: [u8; SEG_LVL_MAX] = [8, 6, 6, 6, 6, 3, 0, 0];
// Same as Segmentation_Feature_Signed in the specification. See 5.9.14
pub const FEATURE_SIGNED: [bool; SEG_LVL_MAX] = [true, true, true, true, true, false, false, false];
// Same as Segmentation_Feature_Max in the specification. See 5.9.14
pub const FEATURE_MAX: [i32; SEG_LVL_MAX] = [255, 63, 63, 63, 63, 7, 0, 0];
/// Tells what should be done with the OBU after [`Parser::read_obu`] is called.
pub enum ObuAction<'a> {
/// We should process the OBU normally.
Process(Obu<'a>),
/// We should drop this OBU and advance to the next one. The u32 is how much
/// we should advance.
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 {
/// Length in bytes
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
if self.extension_flag {
2
} else {
1
}
}
}
/// Contains the OBU header and a reference to its data. The OBU itself hasn't been parsed yet.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Obu<'a> {
/// The OBU header.
pub header: ObuHeader,
/// Amount of bytes from the input consumed to parse this OBU.
pub bytes_used: usize,
/// The slice backing the OBU.
data: Cow<'a, [u8]>,
}
impl<'a> AsRef<[u8]> for Obu<'a> {
fn as_ref(&self) -> &[u8] {
self.data.as_ref()
}
}
/// A fully parsed OBU, with additional data when relevant.
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 {
/// Same as TileOffset in the specification.
pub tile_offset: u32,
/// Same as TileSize in the specification.
pub tile_size: u32,
/// Same as TileRow in the specification.
pub tile_row: u32,
/// Same as TileCol in the specification.
pub tile_col: u32,
// Same as MiRowStart in the specification.
pub mi_row_start: u32,
// Same as MiRowEnd in the specification.
pub mi_row_end: u32,
// Same as MiColStart in the specification.
pub mi_col_start: u32,
// Same as MiColEnd in the specification.
pub mi_col_end: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TileGroupObu<'a> {
/// The OBU backing this tile group.
pub obu: Obu<'a>,
/// Specifies whether tg_start and tg_end are present. If tg_start and
/// tg_end are not present, this tile group covers the entire frame.
pub tile_start_and_end_present_flag: bool,
/// Specifies the zero-based index of the first tile in the current tile
/// group.
pub tg_start: u32,
/// Specifies the zero-based index of the last tile in the current tile
/// group.
pub tg_end: u32,
/// Contains the tiles in this tile group. Use `tile_offset`to index into
/// the OBU data.
///
/// The tiles in the Vec span from tg_start to tg_end.
pub tiles: Vec<Tile>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct OperatingPoint {
/// Specifies the level that the coded video sequence conforms to when
/// operating point i is selected.
pub seq_level_idx: u8,
/// Specifies the tier that the coded video sequence conforms to when
/// operating point i is selected.
pub seq_tier: u8,
/// Specifies the value of operating_point_idc for the selected operating
/// point.
pub idc: u16,
/// If set, indicates that there is a decoder model associated with
/// operating point i. If not set, indicates that there is not a decoder
/// model associated with operating point i.
pub decoder_model_present_for_this_op: bool,
/// Specifies the time interval between the arrival of the first bit in the
/// smoothing buffer and the subsequent removal of the data that belongs to
/// the first coded frame for operating point op, measured in units of
/// 1/90000 seconds. The length of decoder_buffer_delay is specified by
/// buffer_delay_length_minus_1 + 1, in bits.
pub decoder_buffer_delay: u32,
/// Specifies, in combination with decoder_buffer_delay\[ op \] syntax
/// element, the first bit arrival time of frames to be decoded to the
/// smoothing buffer. encoder_buffer_delay is measured in units of 1/90000
/// seconds.
pub encoder_buffer_delay: u32,
/// If set, indicates that the smoothing buffer operates in low-delay mode
/// for operating point op. In low-delay mode late decode times and buffer
/// underflow are both permitted. If not set, indicates that the smoothing
/// buffer operates in strict mode, where buffer underflow is not allowed.
pub low_delay_mode_flag: bool,
/// If set, indicates that initial_display_delay_minus_1 is specified for
/// operating point i. If not set, indicates that
/// initial_display_delay_minus_1 is not specified for operating point i.
pub initial_display_delay_present_for_this_op: bool,
/// Plus 1 specifies, for operating point i, the number of decoded frames
/// that should be present in the buffer pool before the first presentable
/// frame is displayed. This will ensure that all presentable frames in the
/// sequence can be decoded at or before the time that they are scheduled
/// for display. If not signaled then initial_display_delay_minus_1\[ i \] =
/// BUFFER_POOL_MAX_SIZE - 1.
pub initial_display_delay_minus_1: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TimingInfo {
/// The number of time units of a clock operating at the frequency
/// time_scale Hz that corresponds to one increment of a clock tick counter.
/// A display clock tick, in seconds, is equal to num_units_in_display_tick
/// divided by time_scale:
pub num_units_in_display_tick: u32,
/// The number of time units that pass in one second.
pub time_scale: u32,
/// If set, indicates that pictures should be displayed according to their
/// output order with the number of ticks between two consecutive pictures
/// (without dropping frames) specified by num_ticks_per_picture_minus_1 +
/// 1. If not set, indicates that the interval between two consecutive
/// pictures is not specified.
pub equal_picture_interval: bool,
/// Plus 1 specifies the number of clock ticks corresponding to output time
/// between two consecutive pictures in the output order.
pub num_ticks_per_picture_minus_1: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct DecoderModelInfo {
/// Plus 1 specifies the length of the decoder_buffer_delay and the
/// encoder_buffer_delay syntax elements, in bits.
pub buffer_delay_length_minus_1: u8,
/// The number of time units of a decoding clock operating at the frequency
/// time_scale Hz that corresponds to one increment of a clock tick counter:
pub num_units_in_decoding_tick: u32,
/// Plus 1 specifies the length of the buffer_removal_time syntax element,
/// in bits.
pub buffer_removal_time_length_minus_1: u8,
/// Plus 1 specifies the length of the frame_presentation_time syntax
/// element, in bits.
pub frame_presentation_time_length_minus_1: u32,
}
/// Defined by the “Color primaries” section of ISO/IEC 23091-4/ITU-T H.273
/// See 6.4.2
#[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 {
/// Syntax elements which, together with seq_profile, determine the bit
/// depth.
pub high_bitdepth: bool,
/// Syntax elements which, together with seq_profile, determine the bit
/// depth.
pub twelve_bit: bool,
/// If set, indicates that the video does not contain U and V color planes.
/// If not set, indicates that the video contains Y, U, and V color planes.
pub mono_chrome: bool,
/// If set, specifies that color_primaries, transfer_characteristics, and
/// matrix_coefficients are present. If not set, specifies that
/// color_primaries, transfer_characteristics and matrix_coefficients are
/// not present.
pub color_description_present_flag: bool,
/// Defined by the “Color primaries” section of ISO/IEC 23091-4/ITU-T H.273.
pub color_primaries: ColorPrimaries,
/// Defined by the “Transfer characteristics” section of ISO/IEC
/// 23091-4/ITU-T H.273.
pub transfer_characteristics: TransferCharacteristics,
/// Defined by the “Matrix coefficients” section of ISO/IEC 23091-4/ITU-T
/// H.273.
pub matrix_coefficients: MatrixCoefficients,
/// Binary value that is associated with the VideoFullRangeFlag variable
/// specified in ISO/IEC 23091-4/ITU- T H.273. color range equal to 0 shall
/// be referred to as the studio swing representation and color range equal
/// to 1 shall be referred to as the full swing representation for all
/// intents relating to this specification.
pub color_range: bool,
/// Specify the chroma subsampling format
pub subsampling_x: bool,
/// Specify the chroma subsampling format
pub subsampling_y: bool,
/// Specifies the sample position for subsampled streams
pub chroma_sample_position: ChromaSamplePosition,
/// If set, indicates that the U and V planes may have separate delta
/// quantizer values. If not set, indicates that the U and V planes will
/// share the same delta quantizer value.
pub separate_uv_delta_q: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SequenceHeaderObu {
/// The OBU header from the OBU that generated this sequence.
pub obu_header: ObuHeader,
/// Specifies the features that can be used in the coded video sequence.
pub seq_profile: Profile,
/// If set, specifies that the coded video sequence contains only one coded
/// frame. If not set, specifies that the coded video sequence contains one
/// or more coded frames.
pub still_picture: bool,
/// Specifies that the syntax elements not needed by a still picture are
/// omitted.
pub reduced_still_picture_header: bool,
/// Specifies the number of bits minus 1 used for transmitting the frame
/// width syntax elements.
pub frame_width_bits_minus_1: u8,
/// Specifies the number of bits minus 1 used for transmitting the frame
/// height syntax elements.
pub frame_height_bits_minus_1: u8,
/// Specifies the maximum frame width minus 1 for the frames represented by
/// this sequence header.
pub max_frame_width_minus_1: u16,
/// Specifies the maximum frame height minus 1 for the frames represented by
/// this sequence header.
pub max_frame_height_minus_1: u16,
/// Specifies whether frame id numbers are present in the coded video
/// sequence.
pub frame_id_numbers_present_flag: bool,
/// Specifies the number of bits minus 2 used to encode delta_frame_id
/// syntax elements.
pub delta_frame_id_length_minus_2: u32,
/// Used to calculate the number of bits used to encode the frame_id syntax
/// element.
pub additional_frame_id_length_minus_1: u32,
/// When set, indicates that superblocks contain 128x128 luma samples. When
/// not set, it indicates that superblocks contain 64x64 luma samples. (The
/// number of contained chroma samples depends on subsampling_x and
/// subsampling_y.)
pub use_128x128_superblock: bool,
/// When set, specifies that the use_filter_intra syntax element may be
/// present. When not set, specifies that the use_filter_intra syntax
/// element will not be present.
pub enable_filter_intra: bool,
/// Specifies whether the intra edge filtering process should be enabled.
pub enable_intra_edge_filter: bool,
/// When set, specifies that the mode info for inter blocks may contain the
/// syntax element interintra. If not set, specifies that the syntax element
/// interintra will not be present.
pub enable_interintra_compound: bool,
/// When set, specifies that the mode info for inter blocks may contain the
/// syntax element compound_type. When not set, specifies that the syntax
/// element compound_type will not be present.
pub enable_masked_compound: bool,
/// When set, indicates that the allow_warped_motion syntax element may be
/// present. When not set, indicates that the allow_warped_motion syntax
/// element will not be present.
pub enable_warped_motion: bool,
/// When set, indicates that tools based on the values of order hints may be
/// used. When not set, indicates that tools based on order hints are
/// disabled.
pub enable_order_hint: bool,
/// When set, indicates that the inter prediction filter type may be
/// specified independently in the horizontal and vertical directions. If
/// the flag is not set, only one filter type may be specified, which is
/// then used in both directions.
pub enable_dual_filter: bool,
/// If set, indicates that the distance weights process may be used for
/// inter prediction.
pub enable_jnt_comp: bool,
/// If set, indicates that the use_ref_frame_mvs syntax element may be
/// present. If not set, indicates that the use_ref_frame_mvs syntax element
/// will not be present.
pub enable_ref_frame_mvs: bool,
/// If not set, indicates that the seq_force_screen_content_tools syntax
/// element will be present. If set, indicates that
/// seq_force_screen_content_tools should be set equal to
/// SELECT_SCREEN_CONTENT_TOOLS.
pub seq_choose_screen_content_tools: bool,
/// Equal to SELECT_SCREEN_CONTENT_TOOLS indicates that the
/// allow_screen_content_tools syntax element will be present in the frame
/// header. Otherwise, seq_force_screen_content_tools contains the value for
/// allow_screen_content_tools.
pub seq_force_screen_content_tools: u32,
/// If not set, indicates that the seq_force_integer_mv syntax element will
/// be present. If set, indicates that seq_force_integer_mv should be set
/// equal to SELECT_INTEGER_MV.
pub seq_choose_integer_mv: bool,
/// Equal to SELECT_INTEGER_MV indicates that the force_integer_mv syntax
/// element will be present in the frame header (providing
/// allow_screen_content_tools is equal to 1). Otherwise,
/// seq_force_integer_mv contains the value for force_integer_mv.
pub seq_force_integer_mv: u32,
/// Used to compute OrderHintBits.
pub order_hint_bits_minus_1: i32,
/// Specifies the number of bits used for the order_hint syntax element.
pub order_hint_bits: i32,
/// If set, specifies that the use_superres syntax element will be present
/// in the uncompressed header. If not set, specifies that the use_superres
/// syntax element will not be present (instead use_superres will be set to
/// 0 in the uncompressed header without being read).
pub enable_superres: bool,
/// If set, specifies that cdef filtering may be enabled. If not set,
/// specifies that cdef filtering is disabled.
pub enable_cdef: bool,
/// If set, specifies that loop restoration filtering may be enabled. If
/// not set, specifies that loop restoration filtering is disabled.
pub enable_restoration: bool,
/// Specifies whether film grain parameters are present in the coded video
/// sequence.
pub film_grain_params_present: bool,
/// Indicates the number of operating points minus 1 present in the coded
/// video sequence. An operating point specifies which spatial and temporal
/// layers should be decoded.
pub operating_points_cnt_minus_1: u32,
/// The set of operating points.
pub operating_points: [OperatingPoint; MAX_NUM_OPERATING_POINTS],
/// Specifies whether decoder model information is present in the coded
/// video sequence.
pub decoder_model_info_present_flag: bool,
/// The decoder model info.
pub decoder_model_info: DecoderModelInfo,
/// Specifies whether initial display delay information is present in the
/// coded video sequence.
pub initial_display_delay_present_flag: bool,
/// Specifies whether timing info is present in the coded video sequence.
pub timing_info_present_flag: bool,
/// The timing info.
pub timing_info: TimingInfo,
/// The color config.
pub color_config: ColorConfig,
/* CamelCase variables in the specification */
pub bit_depth: BitDepth,
pub num_planes: u32,
}
/// A TemporalDelimiterOBU
#[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>,
}
/// A FrameHeaderOBU
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FrameHeaderObu {
/// The original OBU header. This may be from a FrameOBU or a FrameHeaderOBU
/// directly.
pub obu_header: ObuHeader,
/// If set, indicates the frame indexed by frame_to_show_map_idx is to be
/// output; If not set, indicates that further processing is required.
pub show_existing_frame: bool,
/// Specifies the frame to be output. It is only available if
/// show_existing_frame is set.
pub frame_to_show_map_idx: u8,
/// Specifies the length of the frame_presentation_time syntax element, in
/// bits.
pub frame_presentation_time: u32,
/// Provides the frame id number for the frame to output.
pub display_frame_id: u32,
/// Specifies the type of the frame
pub frame_type: FrameType,
/// If set, specifies that this frame should be immediately output once
/// decoded. If not set specifies that this frame should not be
/// immediately output. (It may be output later if a later uncompressed
/// header uses show_existing_frame is set).
pub show_frame: bool,
/// When set, specifies that the frame may be output using the
/// show_existing_frame mechanism. When not set, specifies that this frame
/// will not be output using the show_existing_frame mechanism.
pub showable_frame: bool,
/// If set, indicates that error resilient mode is enabled;
/// error_resilient_mode equal to 0 indicates that error resilient mode is
/// disabled.
pub error_resilient_mode: bool,
/// Specifies whether the CDF update in the symbol decoding process should
/// be disabled.
pub disable_cdf_update: bool,
/// When set, indicates that intra blocks may use palette encoding; When not
/// set, indicates that palette encoding is never used.
pub allow_screen_content_tools: u32,
/// If set, specifies that motion vectors will always be integers. If not
/// set, specifies that motion vectors can contain fractional bits.
pub force_integer_mv: u32,
/// Specifies the frame id number for the current frame. Frame id numbers
/// are additional information that do not affect the decoding process, but
/// provide decoders with a way of detecting missing reference frames so
/// that appropriate action can be taken.
pub current_frame_id: u32,
/// If not set, specifies that the frame size is equal to the size in the
/// sequence header. If set, specifies that the frame size will either be
/// specified as the size of one of the reference frames, or computed from
/// the frame_width_minus_1 and frame_height_minus_1 syntax elements.
pub frame_size_override_flag: bool,
/// Specifies OrderHintBits least significant bits of the expected output
/// order for this frame.
pub order_hint: u32,
/// Specifies which reference frame contains the CDF values and other state
/// that should be loaded at the start of the frame.
pub primary_ref_frame: u32,
/// If set, specifies that buffer_removal_time is present. If not set,
/// specifies that buffer_removal_time is not present.
pub buffer_removal_time_present_flag: bool,
/// Specifies the frame removal time in units of DecCT clock ticks counted
/// from the removal time of the last random access point for operating
/// point opNum. buffer_removal_time is signaled as a fixed length unsigned
/// integer with a length in bits given by
/// buffer_removal_time_length_minus_1 + 1.
pub buffer_removal_time: Vec<u32>,
/// Contains a bitmask that specifies which reference frame slots will be
/// updated with the current frame after it is decoded.
pub refresh_frame_flags: u32,
/// Specifies the expected output order hint for each reference frame.
pub ref_order_hint: [u32; NUM_REF_FRAMES],
/// If set, indicates that intra block copy may be used in this frame. If
/// not set indicates that intra block copy is not allowed in this frame.
pub allow_intrabc: bool,
/// If set, indicates that only two reference frames are explicitly
/// signaled. If not set, indicates that all reference frames are explicitly
/// signaled.
pub frame_refs_short_signaling: bool,
/// Specifies the reference frame to use for LAST_FRAME.
pub last_frame_idx: u8,
/// Specifies the reference frame to use for GOLDEN_FRAME.
pub gold_frame_idx: u8,
/// Specifies which reference frames are used by inter frames
pub ref_frame_idx: [u8; REFS_PER_FRAME],
/// If not set, specifies that motion vectors are specified to quarter pel
/// precision; If set, specifies that motion vectors are specified to eighth
/// pel precision.
pub allow_high_precision_mv: bool,
/// If not set, specifies that only the SIMPLE motion mode will be used.
pub is_motion_mode_switchable: bool,
/// If set, specifies that motion vector information from a previous frame
/// can be used when decoding the current frame. If not set, specifies that
/// this information will not be used.
pub use_ref_frame_mvs: bool,
/// If set, indicates that the end of frame CDF update is disabled; If not
/// set, indicates that the end of frame CDF update is enabled.
pub disable_frame_end_update_cdf: bool,
/// If set, indicates that the syntax element motion_mode may be present.
/// If not set, indicates that the syntax element motion_mode will not be
/// present
pub allow_warped_motion: bool,
/// If set, specifies that the frame is restricted to a reduced subset of
/// the full set of transform types.
pub reduced_tx_set: bool,
/// If not set, means that the render width and height are inferred from the
/// frame width and height. If set, means that the render width and height
/// are explicitly coded.
pub render_and_frame_size_different: bool,
/// If not set, indicates that no upscaling is needed. If set, indicates
/// that upscaling is needed.
pub use_superres: bool,
/// If set indicates that the filter selection is signaled at the block
/// level; If not set, indicates that the filter selection is signaled at
/// the frame level.
pub is_filter_switchable: bool,
/// The interpolation filter parameters.
pub interpolation_filter: InterpolationFilter,
/// The loop filter parameters.
pub loop_filter_params: LoopFilterParams,
/// The quantization parameters.
pub quantization_params: QuantizationParams,
/// The segmentation parameters.
pub segmentation_params: SegmentationParams,
/// The tile info.
pub tile_info: TileInfo,
/// The CDEF parameters.
pub cdef_params: CdefParams,
/// The loop restoration parameters.
pub loop_restoration_params: LoopRestorationParams,
/// Used to compute TxMode.
pub tx_mode_select: u32,
/// If set specifies that the syntax element skip_mode will be present. If
/// not set, specifies that skip_mode will not be used for this frame.
pub skip_mode_present: bool,
/// If set, specifies that the mode info for inter blocks contains the
/// syntax element comp_mode that indicates whether to use single or
/// compound reference prediction. If not set, specifies that all inter
/// blocks will use single prediction.
pub reference_select: bool,
/// The global motion parameters.
pub global_motion_params: GlobalMotionParams,
/// The film grain parameters.
pub film_grain_params: FilmGrainParams,
/* CamelCase variables */
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 {
/// An array containing loop filter strength values. Different loop filter
/// strength values from the array are used depending on the image plane
/// being filtered, and the edge direction (vertical or horizontal) being
/// filtered.
pub loop_filter_level: [u8; 4],
/// Indicates the sharpness level. The loop_filter_level and
/// loop_filter_sharpness together determine when a block edge is filtered,
/// and by how much the filtering can change the sample values.
pub loop_filter_sharpness: u8,
/// If set, means that the filter level depends on the mode and reference
/// frame used to predict a block. If not set, means that the filter level
/// does not depend on the mode and reference frame.
pub loop_filter_delta_enabled: bool,
/// If set, means that additional syntax elements are present that specify
/// which mode and reference frame deltas are to be updated.
/// loop_filter_delta_update equal to 0 means that these syntax elements are
/// not present.
pub loop_filter_delta_update: bool,
/// Contains the adjustment needed for the filter level based on the chosen
/// reference frame. If this syntax element is not present, it maintains
/// its previous value.
pub loop_filter_ref_deltas: [i8; TOTAL_REFS_PER_FRAME],
/// Contains the adjustment needed for the filter level based on the chosen
/// mode. If this syntax element is not present in the, it maintains its
/// previous value.
pub loop_filter_mode_deltas: [i8; 2],
/// Specifies whether loop filter delta values are present.
pub delta_lf_present: bool,
/// Specifies the left shift which should be applied to decoded loop filter
/// delta values.
pub delta_lf_res: u8,
/// If set, specifies that separate loop filter deltas are sent for
/// horizontal luma edges, vertical luma edges, the U edges, and the V
/// edges. If not set, specifies that the same loop filter delta is used for
/// all edges.
pub delta_lf_multi: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct QuantizationParams {
/// Indicates the base frame qindex. This is used for Y AC coefficients and
/// as the base value for the other quantizers.
pub base_q_idx: u32,
/// Indicates the base frame qindex. This is used for Y AC coefficients and
/// as the base value for the other quantizers.
pub diff_uv_delta: bool,
/// Specifies that the quantizer matrix will be used to compute quantizers.
pub using_qmatrix: bool,
/// Specifies the level in the quantizer matrix that should be used for luma
/// plane decoding.
pub qm_y: u32,
/// Specifies the level in the quantizer matrix that should be used for
/// chroma U plane decoding.
pub qm_u: u32,
/// Specifies the level in the quantizer matrix that should be used for
/// chroma V plane decoding.
pub qm_v: u32,
/// Specifies whether quantizer index delta values are present.
pub delta_q_present: bool,
/// Specifies the left shift which should be applied to decoded quantizer
/// index delta values.
pub delta_q_res: u32,
/// Same as DeltaQYDc
pub delta_q_y_dc: i32,
/// Same as DeltaQUDc
pub delta_q_u_dc: i32,
/// Same as DeltaQUAc
pub delta_q_u_ac: i32,
/// Same as DeltaQVDc
pub delta_q_v_dc: i32,
/// Same as DeltaQVAc
pub delta_q_v_ac: i32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SegmentationParams {
/// If set, indicates that this frame makes use of the segmentation tool; If
/// not set, indicates that the frame does not use segmentation.
pub segmentation_enabled: bool,
/// If set, indicates that the segmentation map are updated during the
/// decoding of this frame. If not set, means that the segmentation map from
/// the previous frame is used.
pub segmentation_update_map: bool,
/// If set, indicates that the updates to the segmentation map are coded
/// relative to the existing segmentation map. If not set, indicates that
/// the new segmentation map is coded without reference to the existing
/// segmentation map.
pub segmentation_temporal_update: bool,
/// If set, indicates that new parameters are about to be specified for each
/// segment. If not set, indicates that the segmentation parameters should
/// keep their existing values.
pub segmentation_update_data: bool,
/// If not set, indicates that the corresponding feature is unused and has
/// value equal to 0. If set, indicates that the feature value is coded.
pub feature_enabled: [[bool; SEG_LVL_MAX]; MAX_SEGMENTS],
/// Specifies the feature data for a segment feature.
pub feature_data: [[i16; SEG_LVL_MAX]; MAX_SEGMENTS],
/// Same as SegIdPreSkip
pub seg_id_pre_skip: bool,
/// Same as LastActiveSegId
pub last_active_seg_id: u8,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TileInfo {
/// If set, means that the tiles are uniformly spaced across the frame. (In
/// other words, all tiles are the same size except for the ones at the
/// right and bottom edge which can be smaller.) If not set, means that the
/// tile sizes are coded.
pub uniform_tile_spacing_flag: bool,
/// Used to compute TileColsLog2.
pub increment_tile_rows_log2: u32,
/// Specifies the width of a tile minus 1 in units of superblocks.
pub width_in_sbs_minus_1: [u32; MAX_TILE_COLS],
/// Specifies the height of a tile minus 1 in units of superblocks.
pub height_in_sbs_minus_1: [u32; MAX_TILE_ROWS],
/// Specifies which tile to use for the CDF update
pub context_update_tile_id: u32,
/// An array specifying the start column (in units of 4x4 luma samples) for
/// each tile across the image.
pub mi_col_starts: [u32; MAX_TILE_COLS + 1],
/// An array specifying the start row (in units of 4x4 luma samples) for
/// each tile down the image.
pub mi_row_starts: [u32; MAX_TILE_ROWS + 1],
/// Specifies the base 2 logarithm of the desired number of tiles down the
/// frame.
pub tile_cols_log2: u32,
/// Specifies the number of tiles across the frame.
pub tile_cols: u32,
/// Specifies the base 2 logarithm of the desired number of tiles down the
/// frame.
pub tile_rows_log2: u32,
/// Secifies the number of tiles down the frame
pub tile_rows: u32,
/// Specifies the number of bytes needed to code each tile size.
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 {
/// Controls the amount of damping in the deringing filter.
pub cdef_damping: u32,
/// Specifies the number of bits needed to specify which CDEF filter to
/// apply.
pub cdef_bits: u32,
/// Specify the strength of the primary filter.
pub cdef_y_pri_strength: [u32; CDEF_MAX],
/// Specify the strength of the secondary filter.
pub cdef_y_sec_strength: [u32; CDEF_MAX],
/// Specify the strength of the primary filter.
pub cdef_uv_pri_strength: [u32; CDEF_MAX],
/// Specify the strength of the secondary filter.
pub cdef_uv_sec_strength: [u32; CDEF_MAX],
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LoopRestorationParams {
/// Specifies if the luma restoration size should be halved.
pub lr_unit_shift: u8,
/// Only present for 4:2:0 formats and specifies if the chroma size should
/// be half the luma size.
pub lr_uv_shift: u8,
/// Same as FrameRestorationType in the specification.
pub frame_restoration_type: [FrameRestorationType; MAX_NUM_PLANES],
/// Same as LoopRestorationSize in the specification.
pub loop_restoration_size: [u16; MAX_NUM_PLANES],
/// Same as UsesLr in the specification.
pub uses_lr: bool,
/// Same as UsesChromaLr in the specification.
pub uses_chroma_lr: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct GlobalMotionParams {
/// Specifies whether global motion parameters are present for a particular
/// reference frame.
pub is_global: [bool; NUM_REF_FRAMES],
/// Specifies whether a particular reference frame uses rotation and zoom
/// global motion.
pub is_rot_zoom: [bool; NUM_REF_FRAMES],
/// Specifies whether a particular reference frame uses translation global
/// motion.
pub is_translation: [bool; NUM_REF_FRAMES],
/// gm_params\[ ref \]\[ j \] is set equal to SavedGmParams\[
/// frame_to_show_map_idx \]\[ ref \]\[ j \] for ref = LAST_FRAME..ALTREF_FRAME,
/// for j = 0..5.
pub gm_params: [[i32; 6]; NUM_REF_FRAMES],
/// Whether the parameters are valid (see warpValid and section 7.11.3.6)
pub warp_valid: [bool; NUM_REF_FRAMES],
/// Same as GmType.
pub gm_type: [WarpModelType; NUM_REF_FRAMES],
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FilmGrainParams {
/// If set, specifies that film grain should be added to this frame. If not
/// set, specifies that film grain should not be added.
pub apply_grain: bool,
/// Specifies the starting value for the pseudo-random numbers used during
/// film grain synthesis.
pub grain_seed: u16,
/// If set means that a new set of parameters should be sent. If not set,
/// means that the previous set of parameters should be used.
pub update_grain: bool,
/// Indicates which reference frame contains the film grain parameters to be
/// used for this frame.
pub film_grain_params_ref_idx: u8,
/// Specifies the number of points for the piece-wise linear scaling
/// function of the luma component.
pub num_y_points: u8,
/// Represents the x (luma value) coordinate for the i-th point of the
/// piecewise linear scaling function for luma component. The values are
/// signaled on the scale of 0..255. (In case of 10 bit video, these values
/// correspond to luma values divided by 4. In case of 12 bit video, these
/// values correspond to luma values divided by 16.)
pub point_y_value: [u8; MAX_NUM_Y_POINTS],
/// Pepresents the scaling (output) value for the i-th point of the
/// piecewise linear scaling function for luma component.
pub point_y_scaling: [u8; MAX_NUM_Y_POINTS],
/// Specifies that the chroma scaling is inferred from the luma scaling.
pub chroma_scaling_from_luma: bool,
/// Specifies the number of points for the piece-wise linear scaling
/// function of the cb component.
pub num_cb_points: u8,
/// Represents the x coordinate for the i-th point of the piece-wise linear
/// scaling function for cb component. The values are signaled on the scale
/// of 0..255.
pub point_cb_value: [u8; MAX_NUM_CB_POINTS],
/// Represents the scaling (output) value for the i-th point of the
/// piecewise linear scaling function for cb component.
pub point_cb_scaling: [u8; MAX_NUM_CB_POINTS],
/// Specifies represents the number of points for the piece-wise linear
/// scaling function of the cr component.
pub num_cr_points: u8,
/// Represents the x coordinate for the i-th point of the piece-wise linear
/// scaling function for cr component. The values are signaled on the scale
/// of 0..255.
pub point_cr_value: [u8; MAX_NUM_CR_POINTS],
/// Represents the scaling (output) value for the i-th point of the
/// piecewise linear scaling function for cr component.
pub point_cr_scaling: [u8; MAX_NUM_CR_POINTS],
/// Represents the shift – 8 applied to the values of the chroma component.
/// The grain_scaling_minus_8 can take values of 0..3 and determines the
/// range and quantization step of the standard deviation of film grain.
pub grain_scaling_minus_8: u8,
/// Specifies the number of auto-regressive coefficients for luma and chroma.
pub ar_coeff_lag: u32,
/// Specifies auto-regressive coefficients used for the Y plane.
pub ar_coeffs_y_plus_128: [u8; MAX_NUM_POS_LUMA],
/// Specifies auto-regressive coefficients used for the U plane.
pub ar_coeffs_cb_plus_128: [u8; MAX_NUM_POS_LUMA],
/// Specifies auto-regressive coefficients used for the V plane.
pub ar_coeffs_cr_plus_128: [u8; MAX_NUM_POS_LUMA],
/// Specifies the range of the auto-regressive coefficients. Values of 0, 1,
/// 2, and 3 correspond to the ranges for auto-regressive coefficients of
/// [-2, 2), [-1, 1), [-0.5, 0.5) and [-0.25, 0.25) respectively.
pub ar_coeff_shift_minus_6: u8,
/// Specifies how much the Gaussian random numbers should be scaled down
/// during the grain synthesis process.
pub grain_scale_shift: u8,
/// Represents a multiplier for the cb component used in derivation of the
/// input index to the cb component scaling function.
pub cb_mult: u8,
/// Represents a multiplier for the average luma component used in
/// derivation of the input index to the cb component scaling function.
pub cb_luma_mult: u8,
/// Represents an offset used in derivation of the input index to the cb
/// component scaling function.
pub cb_offset: u16,
/// Represents a multiplier for the cr component used in derivation of the
/// input index to the cr component scaling function.
pub cr_mult: u8,
/// Represents a multiplier for the average luma component used in
/// derivation of the input index to the cr component scaling function.
pub cr_luma_mult: u8,
/// Represents an offset used in derivation of the input index to the cr
/// component scaling function.
pub cr_offset: u16,
/// If set, indicates that the overlap between film grain blocks shall be
/// applied. If not set, indicates that the overlap between film grain
/// blocks shall not be applied.
pub overlap_flag: bool,
/// If set, indicates that clipping to the restricted (studio) range shall
/// be applied to the sample values after adding the film grain (see the
/// semantics for color_range for an explanation of studio swing). If not
/// set, indicates that clipping to the full range shall be applied to the
/// sample values after adding the film grain.
pub clip_to_restricted_range: bool,
}
/// Keeps track of the state of the reference frames in the parser. All
/// variables are CamelCase.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct ReferenceFrameInfo {
/// An array which is indexed by a reference picture slot number. A value of
/// true in the array signifies that the corresponding reference picture
/// slot is valid for use as a reference picture, while a value of false
/// signifies that the corresponding reference picture slot is not valid for
/// use as a reference picture.
ref_valid: bool,
/// Specifies the frame id for each reference frame.
ref_frame_id: u32,
/// See 7.20 Reference Frame Update Process.
ref_upscaled_width: u32,
/// See 7.20 Reference Frame Update Process.
ref_frame_width: u32,
/// See 7.20 Reference Frame Update Process.
ref_frame_height: u32,
/// See 7.20 Reference Frame Update Process.
ref_render_width: u32,
/// See 7.20 Reference Frame Update Process.
ref_render_height: u32,
/// See 7.20 Reference Frame Update Process.
ref_mi_cols: u32,
/// See 7.20 Reference Frame Update Process.
ref_mi_rows: u32,
/// See 7.20 Reference Frame Update Process.
ref_frame_type: FrameType,
/// See 7.20 Reference Frame Update Process.
ref_subsampling_x: bool,
/// See 7.20 Reference Frame Update Process.
ref_subsampling_y: bool,
/// See 7.20 Reference Frame Update Process.
ref_bit_depth: BitDepth,
/// See 7.20 Reference Frame Update Process.
ref_order_hint: u32,
/// The saved segmentation parameters.
segmentation_params: SegmentationParams,
/// The saved global motion parameters.
global_motion_params: GlobalMotionParams,
/// The saved loop filter parameters.
loop_filter_params: LoopFilterParams,
/// The saved film grain parameters.
film_grain_params: FilmGrainParams,
/// The saved tile info parameters.
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,
/// Same as SeenFrameHeader in the specification
seen_frame_header: bool,
/// We keep this to implement frame_header_copy() in the specification.
last_frame_header: Option<FrameHeaderObu>,
operating_point_idc: u16,
should_probe_for_annexb: bool,
is_first_frame: bool,
ref_info: [ReferenceFrameInfo; NUM_REF_FRAMES],
/* CamelCase variables */
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,
/// The last SequenceHeaderObu parsed.
pub sequence_header: Option<Rc<SequenceHeaderObu>>,
}
impl Parser {
/// Probes the input data for the Annex B format. Anything other than
/// Ok(true) refers to data in "low-overhead" format instead, as we are trying to parse
fn annexb_probe(data: &[u8]) -> Result<bool, String> {
let mut r = Reader::new(data);
let mut seen_sequence = false;
let mut seen_frame = false;
// Try reading the first TU and frame unit size
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);
}
// The first OBU in the first frame_unit of each temporal_unit must
// be a temporal delimiter OBU (and this is the only place temporal
// delimiter OBUs can appear)
let header = Self::parse_obu_header(&mut r.clone())?;
if !matches!(header.obu_type, ObuType::TemporalDelimiter) {
return Ok(false);
}
// Try identifying a sequence and a frame.
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 {
// OK, enough evidence of Annex B format.
return Ok(true);
}
if num_bytes_read >= frame_unit_size {
// We read what we've identified as the first frame and yet no
// sequence and no actual frames were found.
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;
}
// 5.9.8
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(())
}
// 7.8 verbatim.
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(())
}
// 5.9.5.
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[i];
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(())
}
/// Skip the padding bits, ensuring that they actually make sense.
fn skip_and_check_trailing_bits(r: &mut Reader, obu: &Obu) -> Result<(), String> {
// We can't have that in parse_obu as per the spec, because the reader
// is not initialized on our design at that point, so move the check to
// inside this function.
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); // Must be set to zero as per spec.
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)
}
/// Parses one OBU from `data`, which can be in Annex B or low-overhead
/// format.
///
/// `None` may eventually be returned if the OBU is to be dropped.
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 {
// Try probing for Annex B data.
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
{
// Read the length to skip to the start of the open_bitstream_unit()
// syntax element.
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);
// Both "low-overhead" and Annex B are now at the same point, i.e.: a
// open_bitstream_unit() follows.
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 {
/* trap any bugs when computing the final length */
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(())
}
/// Selects an operating point. Only call this after the Sequence OBU for
/// which the operating point should apply has been parsed.
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 {
/* Default::default() already ensures a lot of this, but lets go verbatim */
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;
// frame_width_bits_minus_1 has been read from 4 bits, meaning we can read 16 bits at most.
s.max_frame_width_minus_1 =
r.0.read_bits::<u32>(s.frame_width_bits_minus_1 as usize + 1)? as u16;
// frame_height_bits_minus_1 has been read from 4 bits, meaning we can read 16 bits at most.
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());
/* Client is supposed to set the operating point through external means,
* here we just set 0 as default. */
self.choose_operating_point(0)?;
Ok(rc)
}
/// Implements 7.21. Note that 7.20 will use the information from the
/// header, so we must save them now, as they will not be parsed from the
/// bitstream. We also save some internal parser state which will be useful
/// later.
fn load_reference_frame(&self, fh: &mut FrameHeaderObu) -> Result<(), String> {
let rf = &self.ref_info[fh.frame_to_show_map_idx as usize];
// Section 6.8.1: It is a requirement of bitstream conformance that a
// sequence header OBU has been received before a frame header OBU.
let seq = self.sequence()?;
/* at least save the sizes and for both kf and non-kf */
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;
/* Save into the frame header */
if fh.frame_type == FrameType::KeyFrame {
fh.current_frame_id = rf.ref_frame_id;
/* We don't keep track of sequence information at the frame level */
fh.mi_cols = rf.ref_mi_cols;
fh.mi_rows = rf.ref_mi_rows;
/* The accelerator is keeping track of CDF values, so that is skipped too */
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));
}
/* compute this anyways */
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));
}
/* compute this anyways */
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 {
/* copy from prev_frame */
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;
}
}
/* assume the default params to be valid */
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(());
}
// Following libgav1: implement part of setup_past_independence() and
// load_previous(), i.e.: the parts that refer to the global motion
// parameters.
if fh.primary_ref_frame == PRIMARY_REF_NONE {
// setup_past_independence()
#[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 {
// load_previous():
// 1. The variable prevFrame is set equal to ref_frame_idx[ primary_ref_frame ].
// 2. PrevGmParams is set equal to SavedGmParams[ prevFrame ].
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());
}
// load_grain_params()
*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()
};
// Section 6.8.1: It is a requirement of bitstream conformance that a
// sequence header OBU has been received before a frame header OBU.
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());
}
// In decode_frame_wrapup():
//
// Otherwise (show_existing_frame is equal to 1), if frame_type
// is equal to KEY_FRAME, the reference frame loading process as
// specified in section 7.21 is invoked (this process loads
// frame state from the reference frames into the current frame
// state variables)
//
// The following ordered steps now apply:
//
// 1. The reference frame update process as specified in section
// 7.20 is invoked (this process saves the current frame state
// into the reference frames).
//
// 2. If show_frame is equal to 1 or show_existing_frame is
// equal to 1, the output process as specified in section 7.18
// is invoked (this will output the current frame or a saved
// frame).
//
// We implement 1. here while 2. is left to the actual decoder
self.load_reference_frame(&mut fh)?;
if fh.frame_type == FrameType::KeyFrame {
fh.refresh_frame_flags = ALL_FRAMES;
}
if film_grain_params_present {
// load_grain_params()
fh.film_grain_params = self.ref_info[fh.frame_to_show_map_idx as usize]
.film_grain_params
.clone();
}
// See 5.10.
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;
/* conformance checking, as per aom */
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
));
}
}
/* mark_ref_frames (idLen) */
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)?;
}
/* equivalent boolean expression */
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 {
/* DeltaFrameId */
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()?;
}
/* read_interpolation_filter */
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 {
/* load from the past reference */
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());
}
/* load_loop_filter_params: load ref_deltas and mode_deltas */
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;
/* load_segmentation_params: load feature_enabled and feature_data */
fh.segmentation_params.feature_enabled = prev_frame.segmentation_params.feature_enabled;
fh.segmentation_params.feature_data = prev_frame.segmentation_params.feature_data;
}
// TODO: we can live without this for now.
// if fh.use_ref_frame_mvs {
// // motion_field_estimators()
// }
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)?;
// See 5.10
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);
// init_symbol, decode_tile() and exit_symbol() left to the accelerator.
// Skip the actual tile data
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 {
// left to the accelerator:
// if ( !disable_frame_end_update_cdf ) {
// frame_end_update_cdf( )
// }
// decode_frame_wrapup( )
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 {
/* TileNum = 0 */
self.seen_frame_header = true;
self.last_frame_header = Some(header.clone());
}
Ok(header)
}
}
/// Implements 7.20. This function should be called right after decoding a
/// frame.
pub fn ref_frame_update(&mut self, fh: &FrameHeaderObu) -> Result<(), String> {
// This was found as a bug otherwise by Nicolas Dufresne in GStreamer's
// av1parse.
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 {
/* No scalability information, all OBUs must be decoded */
None
} else {
Some(helpers::floor_log2((self.operating_point_idc >> 8) as u32))
}
}
/// Fully parse an OBU.
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;
/// Same as test-25fps.av1.ivf from Chromium
const STREAM_TEST_25_FPS: &[u8] = include_bytes!("test_data/test-25fps.ivf.av1");
/// Encoded with
///
/// gst-launch-1.0 videotestsrc num-buffers=1 !
/// video/x-raw,format=I420,width=64,height=64 ! filesink
/// location=aom_input.yuv
///
/// And:
///
/// aomenc -p 1 --ivf -w 64 -h 64 -o av1-annexb.ivf.av1 aom_input.yuv --annexb=1
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,
// This OBU should be dropped.
ObuAction::Drop(length) => {
consumed += usize::try_from(length).unwrap();
continue;
}
};
consumed += obu.bytes_used;
num_obus += 1;
}
}
// Manually checked with GStreamer under GDB by using a hitcount on
// "gst_av1_parse_identify_one_obu" *after* the stream format has been
// detected.
assert_eq!(num_obus, 525);
}
#[test]
/// Test that we can correctly identify streams in both "low-overhead" and
/// Annex B formats.
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]
/// Test that we can correctly identify streams in both "low-overhead" and
/// Annex B formats and identify all the OBUs in the stream until the end.
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,
// This OBU should be dropped.
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,
// This OBU should be dropped.
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,
// This OBU should be dropped.
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;
}
}
}
}