| // 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; |
| } |
| } |
| } |
| } |