blob: 856c1319bbcea8795061c44cbf9d93e62c626c2c [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! An Annex B h.265 parser.
//!
//! Parses VPSs, SPSs, PPSs and Slices from NALUs.
use std::collections::BTreeMap;
use anyhow::anyhow;
use anyhow::Context;
use bitreader::BitReader;
use bytes::Buf;
use enumn::N;
use crate::codec::h264::nalu;
use crate::codec::h264::nalu::Header;
use crate::codec::h264::nalu_reader::NaluReader;
use crate::codec::h264::parser::Point;
use crate::codec::h264::parser::Rect;
// Given the max VPS id.
const MAX_VPS_COUNT: usize = 16;
// Given the max SPS id.
const MAX_SPS_COUNT: usize = 16;
// Given the max PPS id.
const MAX_PPS_COUNT: usize = 64;
// 7.4.7.1
const MAX_REF_IDX_ACTIVE: u32 = 15;
// 7.4.3.2.1:
// num_short_term_ref_pic_sets specifies the number of st_ref_pic_set( ) syntax
// structures included in the SPS. The value of num_short_term_ref_pic_sets
// shall be in the range of 0 to 64, inclusive.
// NOTE 5 – A decoder should allocate memory for a total number of
// num_short_term_ref_pic_sets + 1 st_ref_pic_set( ) syntax structures since
// there may be a st_ref_pic_set( ) syntax structure directly signalled in the
// slice headers of a current picture. A st_ref_pic_set( ) syntax structure
// directly signalled in the slice headers of a current picture has an index
// equal to num_short_term_ref_pic_sets.
const MAX_SHORT_TERM_REF_PIC_SETS: usize = 65;
// 7.4.3.2.1:
const MAX_LONG_TERM_REF_PIC_SETS: usize = 32;
// From table 7-5.
const DEFAULT_SCALING_LIST_0: [u8; 16] = [16; 16];
// From Table 7-6.
const DEFAULT_SCALING_LIST_1: [u8; 64] = [
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 17, 16, 17, 18, 17, 18, 18, 17, 18, 21, 19, 20,
21, 20, 19, 21, 24, 22, 22, 24, 24, 22, 22, 24, 25, 25, 27, 30, 27, 25, 25, 29, 31, 35, 35, 31,
29, 36, 41, 44, 41, 36, 47, 54, 54, 47, 65, 70, 65, 88, 88, 115,
];
// From Table 7-6.
const DEFAULT_SCALING_LIST_2: [u8; 64] = [
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 20, 20, 20,
20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 28, 28, 28, 28, 28,
28, 33, 33, 33, 33, 33, 41, 41, 41, 41, 54, 54, 54, 71, 71, 91,
];
/// Table 7-1 – NAL unit type codes and NAL unit type classes
#[derive(N, Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum NaluType {
#[default]
TrailN = 0,
TrailR = 1,
TsaN = 2,
TsaR = 3,
StsaN = 4,
StsaR = 5,
RadlN = 6,
RadlR = 7,
RaslN = 8,
RaslR = 9,
RsvVclN10 = 10,
RsvVclR11 = 11,
RsvVclN12 = 12,
RsvVclR13 = 13,
RsvVclN14 = 14,
RsvVclR15 = 15,
BlaWLp = 16,
BlaWRadl = 17,
BlaNLp = 18,
IdrWRadl = 19,
IdrNLp = 20,
CraNut = 21,
RsvIrapVcl22 = 22,
RsvIrapVcl23 = 23,
RsvVcl24 = 24,
RsvVcl25 = 25,
RsvVcl26 = 26,
RsvVcl27 = 27,
RsvVcl28 = 28,
RsvVcl29 = 29,
RsvVcl30 = 30,
RsvVcl31 = 31,
VpsNut = 32,
SpsNut = 33,
PpsNut = 34,
AudNut = 35,
EosNut = 36,
EobNut = 37,
FdNut = 38,
PrefixSeiNut = 39,
SuffixSeiNut = 40,
RsvNvcl41 = 41,
RsvNvcl42 = 42,
RsvNvcl43 = 43,
RsvNvcl44 = 44,
RsvNvcl45 = 45,
RsvNvcl46 = 46,
RsvNvcl47 = 47,
}
impl NaluType {
/// Whether this is an IDR NALU.
pub fn is_idr(&self) -> bool {
matches!(self, Self::IdrWRadl | Self::IdrNLp)
}
/// Whether this is an IRAP NALU.
pub fn is_irap(&self) -> bool {
let type_ = *self as u32;
type_ >= Self::BlaWLp as u32 && type_ <= Self::RsvIrapVcl23 as u32
}
/// Whether this is a BLA NALU.
pub fn is_bla(&self) -> bool {
let type_ = *self as u32;
type_ >= Self::BlaWLp as u32 && type_ <= Self::BlaNLp as u32
}
/// Whether this is a CRA NALU.
pub fn is_cra(&self) -> bool {
matches!(self, Self::CraNut)
}
/// Whether this is a RADL NALU.
pub fn is_radl(&self) -> bool {
matches!(self, Self::RadlN | Self::RadlR)
}
/// Whether this is a RASL NALU.
pub fn is_rasl(&self) -> bool {
matches!(self, Self::RaslN | Self::RaslR)
}
//// Whether this is a SLNR NALU.
pub fn is_slnr(&self) -> bool {
// From the specification:
// If a picture has nal_unit_type equal to TRAIL_N, TSA_N, STSA_N,
// RADL_N, RASL_N, RSV_VCL_N10, RSV_VCL_N12 or RSV_VCL_N14, the picture
// is an SLNR picture. Otherwise, the picture is a sub-layer reference
// picture.
matches!(
self,
Self::TrailN
| Self::TsaN
| Self::StsaN
| Self::RadlN
| Self::RaslN
| Self::RsvVclN10
| Self::RsvVclN12
| Self::RsvVclN14
)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct NaluHeader {
/// The NALU type.
pub type_: NaluType,
/// Specifies the identifier of the layer to which a VCL NAL unit belongs or
/// the identifier of a layer to which a non-VCL NAL unit applies.
pub nuh_layer_id: u8,
/// Minus 1 specifies a temporal identifier for the NAL unit. The value of
/// nuh_temporal_id_plus1 shall not be equal to 0.
pub nuh_temporal_id_plus1: u8,
}
impl Header for NaluHeader {
fn parse<T: AsRef<[u8]>>(cursor: &std::io::Cursor<T>) -> anyhow::Result<Self> {
let data = &cursor.chunk()[0..2];
let mut r = BitReader::new(data);
// Skip forbidden_zero_bit
r.skip(1)?;
Ok(Self {
type_: NaluType::n(r.read_u32(6)?).ok_or(anyhow!("Invalid NALU type"))?,
nuh_layer_id: r.read_u8(6)?,
nuh_temporal_id_plus1: r.read_u8(3)?,
})
}
fn is_end(&self) -> bool {
matches!(self.type_, NaluType::EosNut | NaluType::EobNut)
}
fn len(&self) -> usize {
// 7.3.1.2
2
}
}
pub type Nalu<'a> = nalu::Nalu<'a, NaluHeader>;
/// H265 levels as defined by table A.8.
/// `general_level_idc` and `sub_layer_level_idc[ OpTid ]` shall be set equal to a
/// value of 30 times the level number specified in Table A.8
#[derive(N, Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
#[default]
L1 = 30,
L2 = 60,
L2_1 = 63,
L3 = 90,
L3_1 = 93,
L4 = 120,
L4_1 = 123,
L5 = 150,
L5_1 = 153,
L5_2 = 156,
L6 = 180,
L6_1 = 183,
L6_2 = 186,
}
/// H265 profiles. See A.3.
#[derive(N, Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum Profile {
#[default]
Main = 1,
Main10 = 2,
MainStill = 3,
RangeExtensions = 4,
HighThroughput = 5,
MultiviewMain = 6,
ScalableMain = 7,
ThreeDMain = 8,
ScreenContentCoding = 9,
ScalableRangeExtensions = 10,
HighThroughputScreenContentCoding = 11,
}
/// A H.265 Video Parameter Set.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Vps {
/// Identifies the VPS for reference by other syntax elements.
pub video_parameter_set_id: u8,
/// If vps_base_layer_internal_flag is equal to 1 and
/// vps_base_layer_available_flag is equal to 1, the base layer is present
/// in the bitstream.
pub base_layer_internal_flag: bool,
/// See `base_layer_internal_flag`.
pub base_layer_available_flag: bool,
/// Plus 1 specifies the maximum allowed number of layers in each CVS
/// referring to the VPS.
pub max_layers_minus1: u8,
/// Plus 1 specifies the maximum number of temporal sub-layers that may be
/// present in each CVS referring to the VPS.
pub max_sub_layers_minus1: u8,
/// When vps_max_sub_layers_minus1 is greater than 0, specifies whether
/// inter prediction is additionally restricted for CVSs referring to the
/// VPS.
pub temporal_id_nesting_flag: bool,
/// ProfileTierLevel() data.
pub profile_tier_level: ProfileTierLevel,
/// When true, specifies that `vps_max_dec_pic_buffering_minus1[ i ]`,
/// `vps_max_num_reorder_pics[ i ]` and `vps_max_latency_increase_plus1[ i ]`
/// are present for vps_max_sub_layers_ minus1 + 1 sub-layers.
/// vps_sub_layer_ordering_info_present_flag equal to 0 specifies that the
/// values of `vps_max_dec_pic_buffering_minus1[ vps_max_sub_layers_minus1 ]`,
/// vps_max_num_reorder_pics[ vps_max_sub_ layers_minus1 ] and
/// `vps_max_latency_increase_plus1[ vps_max_sub_layers_minus1 ]` apply to all
/// sub-layers
pub sub_layer_ordering_info_present_flag: bool,
/// `max_dec_pic_buffering_minus1[i]` plus 1 specifies the maximum required
/// size of the decoded picture buffer for the CVS in units of picture
/// storage buffers when HighestTid is equal to i.
pub max_dec_pic_buffering_minus1: [u32; 7],
/// Indicates the maximum allowed number of pictures with PicOutputFlag
/// equal to 1 that can precede any picture with PicOutputFlag equal to 1 in
/// the CVS in decoding order and follow that picture with PicOutputFlag
/// equal to 1 in output order when HighestTid is equal to i.
pub max_num_reorder_pics: [u32; 7],
/// When true, `max_latency_increase_plus1[i]` is used to compute the value of
/// `VpsMaxLatencyPictures[ i ]`, which specifies the maximum number of
/// pictures with PicOutputFlag equal to 1 that can precede any picture with
/// PicOutputFlag equal to 1 in the CVS in output order and follow that
/// picture with PicOutputFlag equal to 1 in decoding order when HighestTid
/// is equal to i.
pub max_latency_increase_plus1: [u32; 7],
/// Specifies the maximum allowed value of nuh_layer_id of all NAL units in
/// each CVS referring to the VPS.
pub max_layer_id: u8,
/// num_layer_sets_minus1 plus 1 specifies the number of layer sets that are
/// specified by the VPS.
pub num_layer_sets_minus1: u32,
/// When true, specifies that num_units_in_tick, time_scale,
/// poc_proportional_to_timing_flag and num_hrd_parameters are present in
/// the VPS.
pub timing_info_present_flag: bool,
/// The number of time units of a clock operating at the frequency
/// vps_time_scale Hz that corresponds to one increment (called a clock
/// tick) of a clock tick counter. The value of vps_num_units_in_tick shall
/// be greater than 0. A clock tick, in units of seconds, is equal to the
/// quotient of vps_num_units_in_tick divided by vps_time_scale. For
/// example, when the picture rate of a video signal is 25 Hz,
/// vps_time_scale may be equal to 27 000 000 and vps_num_units_in_tick may
/// be equal to 1 080 000, and consequently a clock tick may be 0.04
/// seconds.
pub num_units_in_tick: u32,
/// The number of time units that pass in one second. For example, a time
/// coordinate system that measures time using a 27 MHz clock has a
/// vps_time_scale of 27 000 000.
pub time_scale: u32,
/// When true, indicates that the picture order count value for each picture
/// in the CVS that is not the first picture in the CVS, in decoding order,
/// is proportional to the output time of the picture relative to the output
/// time of the first picture in the CVS. When false, indicates that the
/// picture order count value for each picture in the CVS that is not the
/// first picture in the CVS, in decoding order, may or may not be
/// proportional to the output time of the picture relative to the output
/// time of the first picture in the CVS.
pub poc_proportional_to_timing_flag: bool,
/// num_ticks_poc_diff_one_minus1 plus 1 specifies the number of clock ticks
/// corresponding to a difference of picture order count values equal to 1.
pub num_ticks_poc_diff_one_minus1: u32,
/// Specifies the number of hrd_parameters( ) syntax structures present in
/// the VPS RBSP before the vps_extension_flag syntax element.
pub num_hrd_parameters: u32,
/// `hrd_layer_set_idx[ i ]` specifies the index, into the list of layer sets
/// specified by the VPS, of the layer set to which the i-th hrd_parameters(
/// ) syntax structure in the VPS applies.
pub hrd_layer_set_idx: Vec<u16>,
/// `cprms_present_flag[ i ]` equal to true specifies that the HRD parameters
/// that are common for all sub-layers are present in the i-th
/// hrd_parameters( ) syntax structure in the VPS. `cprms_present_flag[ i ]`
/// equal to false specifies that the HRD parameters that are common for all
/// sub-layers are not present in the i-th hrd_parameters( ) syntax
/// structure in the VPS and are derived to be the same as the ( i − 1 )-th
/// hrd_parameters( ) syntax structure in the VPS. `cprms_present_flag[ 0 ]`
/// is inferred to be equal to true.
pub cprms_present_flag: Vec<bool>,
/// The hrd_parameters() data.
pub hrd_parameters: Vec<HrdParams>,
/// When false, specifies that no vps_extension_data_flag syntax elements
/// are present in the VPS RBSP syntax structure. When true, specifies that
/// there are vps_extension_data_flag syntax elements present in the VPS
/// RBSP syntax structure. Decoders conforming to a profile specified in
/// Annex A but not supporting the INBLD capability specified in Annex F
/// shall ignore all data that follow the value 1 for vps_extension_flag in
/// a VPS NAL unit.
pub extension_flag: bool,
}
impl Default for Vps {
fn default() -> Self {
Self {
video_parameter_set_id: Default::default(),
base_layer_internal_flag: Default::default(),
base_layer_available_flag: Default::default(),
max_layers_minus1: Default::default(),
max_sub_layers_minus1: Default::default(),
temporal_id_nesting_flag: Default::default(),
profile_tier_level: Default::default(),
sub_layer_ordering_info_present_flag: Default::default(),
max_dec_pic_buffering_minus1: Default::default(),
max_num_reorder_pics: Default::default(),
max_latency_increase_plus1: Default::default(),
max_layer_id: Default::default(),
num_layer_sets_minus1: Default::default(),
timing_info_present_flag: Default::default(),
num_units_in_tick: Default::default(),
time_scale: Default::default(),
poc_proportional_to_timing_flag: Default::default(),
num_ticks_poc_diff_one_minus1: Default::default(),
num_hrd_parameters: Default::default(),
hrd_layer_set_idx: Default::default(),
cprms_present_flag: vec![true],
hrd_parameters: Default::default(),
extension_flag: Default::default(),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ProfileTierLevel {
/// Specifies the context for the interpretation of general_profile_idc and
/// `general_profile_compatibility_flag[ j ]` for all values of j in the range
/// of 0 to 31, inclusive.
pub general_profile_space: u8,
/// Specifies the tier context for the interpretation of general_level_idc
/// as specified in Annex A.
pub general_tier_flag: bool,
/// When general_profile_space is equal to 0, indicates a profile to which
/// the CVS conforms as specified in Annex A. Bitstreams shall not contain
/// values of general_profile_idc other than those specified in Annex A.
/// Other values of general_profile_idc are reserved for future use by ITU-T
/// | ISO/IEC.
pub general_profile_idc: u8,
/// `general_profile_compatibility_flag[ j ]` equal to true, when
/// general_profile_space is false, indicates that the CVS conforms to the
/// profile indicated by general_profile_idc equal to j as specified in
/// Annex A.
pub general_profile_compatibility_flag: [bool; 32],
/// general_progressive_source_flag and general_interlaced_source_flag are
/// interpreted as follows:
///
/// –If general_progressive_source_flag is true and
/// general_interlaced_source_flag is false, the source scan type of the
/// pictures in the CVS should be interpreted as progressive only.
///
/// –Otherwise, if general_progressive_source_flag is false and
/// general_interlaced_source_flag is true, the source scan type of the
/// pictures in the CVS should be interpreted as interlaced only.
///
/// –Otherwise, if general_progressive_source_flag is false and
/// general_interlaced_source_flag is false, the source scan type of the
/// pictures in the CVS should be interpreted as unknown or unspecified.
///
/// –Otherwise (general_progressive_source_flag is true and
/// general_interlaced_source_flag is true), the source scan type of each
/// picture in the CVS is indicated at the picture level using the syntax
/// element source_scan_type in a picture timing SEI message.
pub general_progressive_source_flag: bool,
/// See `general_progressive_source_flag`.
pub general_interlaced_source_flag: bool,
/// If true, specifies that there are no frame packing arrangement SEI
/// messages, segmented rectangular frame packing arrangement SEI messages,
/// equirectangular projection SEI messages, or cubemap projection SEI
/// messages present in the CVS. If false, indicates that there may or may
/// not be one or more frame packing arrangement SEI messages, segmented
/// rectangular frame packing arrangement SEI messages, equirectangular
/// projection SEI messages, or cubemap projection SEI messages present in
/// the CVS.
pub general_non_packed_constraint_flag: bool,
/// When true, specifies that field_seq_flag is false. When false, indicates
/// that field_seq_flag may or may not be false.
pub general_frame_only_constraint_flag: bool,
/// See Annex A.
pub general_max_12bit_constraint_flag: bool,
/// See Annex A.
pub general_max_10bit_constraint_flag: bool,
/// See Annex A.
pub general_max_8bit_constraint_flag: bool,
/// See Annex A.
pub general_max_422chroma_constraint_flag: bool,
/// See Annex A.
pub general_max_420chroma_constraint_flag: bool,
/// See Annex A.
pub general_max_monochrome_constraint_flag: bool,
/// See Annex A.
pub general_intra_constraint_flag: bool,
/// See Annex A.
pub general_lower_bit_rate_constraint_flag: bool,
/// See Annex A.
pub general_max_14bit_constraint_flag: bool,
/// See Annex A.
pub general_one_picture_only_constraint_flag: bool,
/// When true, specifies that the INBLD capability as specified in Annex F
/// is required for decoding of the layer to which the profile_tier_level( )
/// syntax structure applies. When false, specifies that the INBLD
/// capability as specified in Annex F is not required for decoding of the
/// layer to which the profile_tier_level( ) syntax structure applies.
pub general_inbld_flag: bool,
/// Indicates a level to which the CVS conforms as specified in Annex A.
pub general_level_idc: Level,
/// Sub-layer syntax element.
pub sub_layer_profile_present_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_level_present_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_profile_space: [u8; 6],
/// Sub-layer syntax element.
pub sub_layer_tier_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_profile_idc: [u8; 6],
/// Sub-layer syntax element.
pub sub_layer_profile_compatibility_flag: [[bool; 32]; 6],
/// Sub-layer syntax element.
pub sub_layer_progressive_source_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_interlaced_source_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_non_packed_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_frame_only_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_max_12bit_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_max_10bit_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_max_8bit_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_max_422chroma_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_max_420chroma_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_max_monochrome_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_intra_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_one_picture_only_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_lower_bit_rate_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_max_14bit_constraint_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_inbld_flag: [bool; 6],
/// Sub-layer syntax element.
pub sub_layer_level_idc: [Level; 6],
}
impl ProfileTierLevel {
pub fn max_luma_ps(&self) -> u32 {
// See Table A.8.
match self.general_level_idc {
Level::L1 => 36864,
Level::L2 => 122880,
Level::L2_1 => 245760,
Level::L3 => 552960,
Level::L3_1 => 983040,
Level::L4 | Level::L4_1 => 2228224,
Level::L5 | Level::L5_1 | Level::L5_2 => 8912896,
_ => 35651584,
}
}
pub fn max_dpb_pic_buf(&self) -> u32 {
if self.general_profile_idc >= 1 && self.general_profile_idc <= 5 {
6
} else {
7
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SpsRangeExtension {
pub transform_skip_rotation_enabled_flag: bool,
pub transform_skip_context_enabled_flag: bool,
pub implicit_rdpcm_enabled_flag: bool,
pub explicit_rdpcm_enabled_flag: bool,
pub extended_precision_processing_flag: bool,
pub intra_smoothing_disabled_flag: bool,
pub high_precision_offsets_enabled_flag: bool,
pub persistent_rice_adaptation_enabled_flag: bool,
pub cabac_bypass_alignment_enabled_flag: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SpsSccExtension {
/// When set, specifies that a picture in the CVS may be included in a
/// reference picture list of a slice of the picture itself. When not set,
/// specifies that a picture in the CVS is never included in a reference
/// picture list of a slice of the picture itself.
pub curr_pic_ref_enabled_flag: bool,
/// When set, specifies that the decoding process for palette mode may be
/// used for intra blocks. When not set, specifies that the decoding process
/// for palette mode is not applied.
pub palette_mode_enabled_flag: bool,
/// Specifies the maximum allowed palette size.
pub palette_max_size: u8,
/// Specifies the difference between the maximum allowed palette predictor
/// size and the maximum allowed palette size.
pub delta_palette_max_predictor_size: u8,
/// When set, specifies that the sequence palette predictors are initialized
/// using the sps_palette_predictor_initializers. When not set, specifies
/// that the entries in the sequence palette predictor are initialized to 0.
pub palette_predictor_initializers_present_flag: bool,
/// num_palette_predictor_initializers_minus1 plus 1 specifies the number of
/// entries in the sequence palette predictor initializer.
pub num_palette_predictor_initializer_minus1: u8,
/// `palette_predictor_initializer[ comp ][ i ]` specifies the value of the
/// comp-th component of the i-th palette entry in the SPS that is used to
/// initialize the array PredictorPaletteEntries.
pub palette_predictor_initializer: [[u32; 128]; 3],
/// Controls the presence and inference of the use_integer_mv_flag that
/// specifies the resolution of motion vectors for inter prediction.
pub motion_vector_resolution_control_idc: u8,
/// When set, specifies that the intra boundary filtering process is
/// unconditionally disabled for intra prediction. If not set, specifies
/// that the intra boundary filtering process may be used.
pub intra_boundary_filtering_disabled_flag: bool,
}
impl Default for SpsSccExtension {
fn default() -> Self {
Self {
curr_pic_ref_enabled_flag: Default::default(),
palette_mode_enabled_flag: Default::default(),
palette_max_size: Default::default(),
delta_palette_max_predictor_size: Default::default(),
palette_predictor_initializers_present_flag: Default::default(),
num_palette_predictor_initializer_minus1: Default::default(),
palette_predictor_initializer: [[0; 128]; 3],
motion_vector_resolution_control_idc: Default::default(),
intra_boundary_filtering_disabled_flag: Default::default(),
}
}
}
/// A H.265 Sequence Parameter Set.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Sps {
/// Specifies the value of the vps_video_parameter_set_id of the active VPS.
pub video_parameter_set_id: u8,
/// `max_sub_layers_minus1` plus 1 specifies the maximum number of temporal
/// sub-layers that may be present in each CVS referring to the SPS.
pub max_sub_layers_minus1: u8,
/// When sps_max_sub_layers_minus1 is greater than 0, specifies whether
/// inter prediction is additionally restricted for CVSs referring to the
/// SPS.
pub temporal_id_nesting_flag: bool,
/// profile_tier_level() data.
pub profile_tier_level: ProfileTierLevel,
/// Provides an identifier for the SPS for reference by other syntax
/// elements.
pub seq_parameter_set_id: u8,
/// Specifies the chroma sampling relative to the luma sampling as specified
/// in clause 6.2.
pub chroma_format_idc: u8,
/// When true, specifies that the three colour components of the 4:4:4
/// chroma format are coded separately. When false, specifies that the
/// colour components are not coded separately.
pub separate_colour_plane_flag: bool,
/// Specifies the width of each decoded picture in units of luma samples.
pub pic_width_in_luma_samples: u16,
/// Specifies the height of each decoded picture in units of luma samples.
pub pic_height_in_luma_samples: u16,
/// When true, indicates that the conformance cropping window offset
/// parameters follow next in the SPS. When false, indicates that the
/// conformance cropping window offset parameters are not present.
pub conformance_window_flag: bool,
/* if conformance_window_flag */
/// Specify the samples of the pictures in the CVS that are output from the
/// decoding process, in terms of a rectangular region specified in picture
/// coordinates for output.
pub conf_win_left_offset: u32,
pub conf_win_right_offset: u32,
pub conf_win_top_offset: u32,
pub conf_win_bottom_offset: u32,
/// Specifies the bit depth of the samples of the luma array BitDepthY and
/// the value of the luma quantization parameter range offset QpBdOffsetY.
pub bit_depth_luma_minus8: u8,
/// Specifies the bit depth of the samples of the chroma arrays BitDepthC
/// and the value of the chroma quantization parameter range offset
/// QpBdOffsetC.
pub bit_depth_chroma_minus8: u8,
/// Specifies the value of the variable MaxPicOrderCntLsb that is used in
/// the decoding process for picture order count.
pub log2_max_pic_order_cnt_lsb_minus4: u8,
/// When true, specifies that `max_dec_pic_buffering_minus1[ i ]`,
/// `max_num_reorder_pics[ i ]` and `max_latency_increase_plus1[ i ]` are
/// present for max_sub_layers_minus1 + 1 sub- layers. When false, specifies
/// that the values of `max_dec_pic_ buffering_minus1[ max_sub_layers_minus1
/// ]`, `max_num_reorder_pics[ max_sub_layers_minus1 ]` and max_
/// `latency_increase_plus1[ max_sub_layers_minus1 ]` apply to all sub-layers.
pub sub_layer_ordering_info_present_flag: bool,
/// `max_dec_pic_buffering_minus1[ i ]` plus 1 specifies the maximum required
/// size of the decoded picture buffer for the CVS in units of picture
/// storage buffers when HighestTid is equal to i.
pub max_dec_pic_buffering_minus1: [u8; 7],
/// `max_num_reorder_pics[ i ]` indicates the maximum allowed number of
/// pictures with PicOutputFlag equal to 1 that can precede any picture with
/// PicOutputFlag equal to 1 in the CVS in decoding order and follow that
/// picture with PicOutputFlag equal to 1 in output order when HighestTid is
/// equal to i.
pub max_num_reorder_pics: [u8; 7],
/// `max_latency_increase_plus1[ i ]` not equal to 0 is used to compute the
/// value of `SpsMaxLatencyPictures[ i ]`, which specifies the maximum number
/// of pictures with PicOutputFlag equal to 1 that can precede any picture
/// with PicOutputFlag equal to 1 in the CVS in output order and follow that
/// picture with PicOutputFlag equal to 1 in decoding order when HighestTid
/// is equal to i.
pub max_latency_increase_plus1: [u8; 7],
/// min_luma_coding_block_size_minus3 plus 3 specifies the minimum luma
/// coding block size.
pub log2_min_luma_coding_block_size_minus3: u8,
/// Specifies the difference between the maximum and minimum luma coding
/// block size.
pub log2_diff_max_min_luma_coding_block_size: u8,
/// min_luma_transform_block_size_minus2 plus 2 specifies the minimum luma
/// transform block size.
pub log2_min_luma_transform_block_size_minus2: u8,
/// Specifies the difference between the maximum and minimum luma transform
/// block size.
pub log2_diff_max_min_luma_transform_block_size: u8,
/// Specifies the maximum hierarchy depth for transform units of coding
/// units coded in inter prediction mode.
pub max_transform_hierarchy_depth_inter: u8,
/// Specifies the maximum hierarchy depth for transform units of coding
/// units coded in intra prediction mode.
pub max_transform_hierarchy_depth_intra: u8,
/// When true, specifies that a scaling list is used for the scaling process
/// for transform coefficients. When false, specifies that scaling list is
/// not used for the scaling process for transform coefficients.
pub scaling_list_enabled_flag: bool,
/* if scaling_list_enabled_flag */
/// When true, specifies that the scaling_list_data( ) syntax structure is
/// present in the SPS. When false, specifies that the scaling_list_data( )
/// syntax structure is not present in the SPS.
pub scaling_list_data_present_flag: bool,
/// The scaling_list_data() syntax data.
pub scaling_list: ScalingLists,
/// When true, specifies that asymmetric motion partitions, i.e., PartMode
/// equal to PART_2NxnU, PART_2NxnD, PART_nLx2N or PART_nRx2N, may be used
/// in CTBs. When false, specifies that asymmetric motion partitions cannot
/// be used in CTBs.
pub amp_enabled_flag: bool,
/// When true, specifies that the sample adaptive offset process is applied
/// to the reconstructed picture after the deblocking filter process. When
/// false, specifies that the sample adaptive offset process is not applied
/// to the reconstructed picture after the deblocking filter process.
pub sample_adaptive_offset_enabled_flag: bool,
/// When false, specifies that PCM-related syntax
/// (pcm_sample_bit_depth_luma_minus1, pcm_sample_ bit_depth_chroma_minus1,
/// log2_min_pcm_luma_coding_block_size_minus3, log2_diff_max_min_pcm_luma_
/// coding_block_size, pcm_loop_filter_disabled_flag, pcm_flag,
/// pcm_alignment_zero_bit syntax elements and pcm_sample( ) syntax
/// structure) is not present in the CVS.
pub pcm_enabled_flag: bool,
/* if pcm_enabled_flag */
pub pcm_sample_bit_depth_luma_minus1: u8,
/// Specifies the number of bits used to represent each of PCM sample values
/// of the luma component.
pub pcm_sample_bit_depth_chroma_minus1: u8,
/// Specifies the number of bits used to represent each of PCM sample values
/// of the chroma components.
pub log2_min_pcm_luma_coding_block_size_minus3: u8,
/// Specifies the difference between the maximum and minimum size of coding
/// blocks with pcm_flag equal to true.
pub log2_diff_max_min_pcm_luma_coding_block_size: u8,
/// Specifies whether the loop filter process is disabled on reconstructed
/// samples in a coding unit with pcm_flag equal to true as follows:
///
/// – If pcm_loop_filter_disabled_flag is set, the deblocking filter and
/// sample adaptive offset filter processes on the reconstructed samples in
/// a coding unit with pcm_flag set are disabled.
///
/// – Otherwise (pcm_loop_filter_disabled_flag value is not set), the
/// deblocking filter and sample adaptive offset filter processes on the
/// reconstructed samples in a coding unit with pcm_flag set are not
/// disabled.
pub pcm_loop_filter_disabled_flag: bool,
/// Specifies the number of st_ref_pic_set( ) syntax structures included in
/// the SPS.
pub num_short_term_ref_pic_sets: u8,
/// the st_ref_pic_set() data.
pub short_term_ref_pic_set: Vec<ShortTermRefPicSet>,
/// If unset, specifies that no long-term reference picture is used for
/// inter prediction of any coded picture in the CVS.
/// If set, specifies that long-term reference pictures may be used for
/// inter prediction of one or more coded pictures in the CVS.
pub long_term_ref_pics_present_flag: bool,
/* if long_term_ref_pics_present_flag */
/// Specifies the number of candidate long-term reference pictures that are
/// specified in the SPS.
pub num_long_term_ref_pics_sps: u8,
/// `lt_ref_pic_poc_lsb_sps[ i ]` specifies the picture order count modulo
/// MaxPicOrderCntLsb of the i-th candidate long-term reference picture
/// specified in the SPS.
pub lt_ref_pic_poc_lsb_sps: [u32; MAX_LONG_TERM_REF_PIC_SETS],
/// `used_by_curr_pic_lt_sps_flag[ i ]` equal to false specifies that the i-th
/// candidate long-term reference picture specified in the SPS is not used
/// for reference by a picture that includes in its long-term reference
/// picture set (RPS) the i-th candidate long-term reference picture
/// specified in the SPS.
pub used_by_curr_pic_lt_sps_flag: [bool; MAX_LONG_TERM_REF_PIC_SETS],
/// When set, specifies that slice_temporal_mvp_enabled_flag is present in
/// the slice headers of non-IDR pictures in the CVS. When not set,
/// specifies that slice_temporal_mvp_enabled_flag is not present in slice
/// headers and that temporal motion vector predictors are not used in the
/// CVS.
pub temporal_mvp_enabled_flag: bool,
/// When set, specifies that bi-linear interpolation is conditionally used
/// in the intraprediction filtering process in the CVS as specified in
/// clause 8.4.4.2.3.
pub strong_intra_smoothing_enabled_flag: bool,
/// When set, specifies that the vui_parameters( ) syntax structure as
/// specified in Annex E is present. When not set, specifies that the
/// vui_parameters( ) syntax structure as specified in Annex E is not
/// present.
pub vui_parameters_present_flag: bool,
/// The vui_parameters() data.
pub vui_parameters: VuiParams,
/// When set, specifies that the syntax elements sps_range_extension_flag,
/// sps_multilayer_extension_flag, sps_3d_extension_flag,
/// sps_scc_extension_flag, and sps_extension_4bits are present in the SPS
/// RBSP syntax structure. When not set, specifies that these syntax
/// elements are not present.
pub extension_present_flag: bool,
pub range_extension_flag: bool,
/// The sps_range_extension() data.
pub range_extension: SpsRangeExtension,
/// When set, specifies that the sps_scc_extension( ) syntax structure is
/// present in the SPS RBSP syntax structure. When not set, specifies that
/// this syntax structure is not present
pub scc_extension_flag: bool,
/// The sps_scc_extension() data.
pub scc_extension: SpsSccExtension,
// Internal H265 variables. Computed from the bitstream.
/// Equivalent to MinCbLog2SizeY in the specification.
pub min_cb_log2_size_y: u32,
/// Equivalent to CtbLog2SizeY in the specification.
pub ctb_log2_size_y: u32,
/// Equivalent to CtbSizeY in the specification.
pub ctb_size_y: u32,
/// Equivalent to PicHeightInCtbsY in the specification.
pub pic_height_in_ctbs_y: u32,
/// Equivalent to PicWidthInCtbsY in the specification.
pub pic_width_in_ctbs_y: u32,
/// Equivalent to PicSizeInCtbsY in the specification.
pub pic_size_in_ctbs_y: u32,
/// Equivalent to ChromaArrayType in the specification.
pub chroma_array_type: u8,
/// Equivalent to WpOffsetHalfRangeY in the specification.
pub wp_offset_half_range_y: u32,
/// Equivalent to WpOffsetHalfRangeC in the specification.
pub wp_offset_half_range_c: u32,
/// Equivalent to MaxTbLog2SizeY in the specification.
pub max_tb_log2_size_y: u32,
/// Equivalent to PicSizeInSamplesY in the specification.
pub pic_size_in_samples_y: u32,
}
impl Sps {
pub fn max_dpb_size(&self) -> usize {
let max_luma_ps = self.profile_tier_level.max_luma_ps();
let max_dpb_pic_buf = self.profile_tier_level.max_dpb_pic_buf();
// Equation A-2
let max = if self.pic_size_in_samples_y <= (max_luma_ps >> 2) {
std::cmp::min(4 * max_dpb_pic_buf, 16)
} else if self.pic_size_in_samples_y <= (max_luma_ps >> 1) {
std::cmp::min(2 * max_dpb_pic_buf, 16)
} else if self.pic_size_in_samples_y <= ((3 * max_luma_ps) >> 2) {
std::cmp::min(4 * max_dpb_pic_buf / 3, 16)
} else {
max_dpb_pic_buf
};
max as usize
}
pub fn width(&self) -> u16 {
self.pic_width_in_luma_samples
}
pub fn height(&self) -> u16 {
self.pic_height_in_luma_samples
}
pub fn visible_rectangle(&self) -> Rect<u32> {
// From the specification:
// NOTE 3 – The conformance cropping window offset parameters are
// only applied at the output. All internal decoding processes are
// applied to the uncropped picture size.
if !self.conformance_window_flag {
return Rect {
min: Point { x: 0, y: 0 },
max: Point {
x: u32::from(self.width()),
y: u32::from(self.height()),
},
};
}
const SUB_HEIGHT_C: [u32; 5] = [1, 2, 1, 1, 1];
const SUB_WIDTH_C: [u32; 5] = [1, 2, 2, 1, 1];
let crop_unit_y = SUB_HEIGHT_C[usize::from(self.chroma_array_type)];
let crop_unit_x = SUB_WIDTH_C[usize::from(self.chroma_array_type)];
let crop_left = crop_unit_x * self.conf_win_left_offset;
let crop_right = crop_unit_x * self.conf_win_right_offset;
let crop_top = crop_unit_y * self.conf_win_top_offset;
let crop_bottom = crop_unit_y * self.conf_win_bottom_offset;
Rect {
min: Point {
x: crop_left,
y: crop_top,
},
max: Point {
x: u32::from(self.width()) - crop_left - crop_right,
y: u32::from(self.height()) - crop_top - crop_bottom,
},
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PpsSccExtension {
/// When set, specifies that a picture referring to the PPS may be included
/// in a reference picture list of a slice of the picture itself. If not
/// set, specifies that a picture referring to the PPS is never included in
/// a reference picture list of a slice of the picture itself.
pub curr_pic_ref_enabled_flag: bool,
/// When set, specifies that an adaptive colour transform may be applied to
/// the residual in the decoding process. When not set, specifies that
/// adaptive colour transform is not applied to the residual.
pub residual_adaptive_colour_transform_enabled_flag: bool,
/// When set, specifies that slice_act_y_qp_offset, slice_act_cb_qp_offset,
/// slice_act_cr_qp_offset are present in the slice header. When not set,
/// specifies that slice_act_y_qp_offset, slice_act_cb_qp_offset,
/// slice_act_cr_qp_offset are not present in the slice header.
pub slice_act_qp_offsets_present_flag: bool,
/// See the specificartion for more details.
pub act_y_qp_offset_plus5: i8,
/// See the specificartion for more details.
pub act_cb_qp_offset_plus5: i8,
/// See the specificartion for more details.
pub act_cr_qp_offset_plus3: i8,
/// When set, specifies that the palette predictor initializers used for the
/// pictures referring to the PPS are derived based on the palette predictor
/// initializers specified by the PPS. If not set, specifies that the
/// palette predictor initializers used for the pictures referring to the
/// PPS are inferred to be equal to those specified by the active SPS.
pub palette_predictor_initializers_present_flag: bool,
/// Specifies the number of entries in the picture palette predictor
/// initializer.
pub num_palette_predictor_initializers: u8,
/// When set, specifies that the pictures that refer to this PPS are
/// monochrome. If not set, specifies that the pictures that refer to this
/// PPS have multiple components.
pub monochrome_palette_flag: bool,
/// luma_bit_depth_entry_minus8 plus 8 specifies the bit depth of the luma
/// component of the entries of the palette predictor initializer.
pub luma_bit_depth_entry_minus8: u8,
/// chroma_bit_depth_entry_minus8 plus 8 specifies the bit depth of the
/// chroma components of the entries of the palette predictor initializer.
pub chroma_bit_depth_entry_minus8: u8,
/// `pps_palette_predictor_initializer[ comp ][ i ]` specifies the value of
/// the comp-th component of the i-th palette entry in the PPS that is used
/// to initialize the array PredictorPaletteEntries.
pub palette_predictor_initializer: [[u8; 128]; 3],
}
impl Default for PpsSccExtension {
fn default() -> Self {
Self {
curr_pic_ref_enabled_flag: Default::default(),
residual_adaptive_colour_transform_enabled_flag: Default::default(),
slice_act_qp_offsets_present_flag: Default::default(),
act_y_qp_offset_plus5: Default::default(),
act_cb_qp_offset_plus5: Default::default(),
act_cr_qp_offset_plus3: Default::default(),
palette_predictor_initializers_present_flag: Default::default(),
num_palette_predictor_initializers: Default::default(),
monochrome_palette_flag: Default::default(),
luma_bit_depth_entry_minus8: Default::default(),
chroma_bit_depth_entry_minus8: Default::default(),
palette_predictor_initializer: [[0; 128]; 3],
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct PpsRangeExtension {
/// log2_max_transform_skip_block_size_minus2 plus 2 specifies the maximum
/// transform block size for which transform_skip_flag may be present in
/// coded pictures referring to the PPS. When not present, the value of
/// log2_max_transform_skip_block_size_minus2 is inferred to be equal to 0.
/// When present, the value of log2_max_transform_skip_block_size_minus2
/// shall be less than or equal to MaxTbLog2SizeY − 2.
pub log2_max_transform_skip_block_size_minus2: u32,
/// When set, specifies that log2_res_scale_abs_plus1 and
/// res_scale_sign_flag may be present in the transform unit syntax for
/// pictures referring to the PPS. When not set, specifies that
/// log2_res_scale_abs_plus1 and res_scale_sign_flag are not present for
/// pictures referring to the PPS.
pub cross_component_prediction_enabled_flag: bool,
/// When set, specifies that the cu_chroma_qp_offset_flag may be present in
/// the transform unit syntax. When not set, specifies that the
/// cu_chroma_qp_offset_flag is not present in the transform unit syntax.
pub chroma_qp_offset_list_enabled_flag: bool,
/// Specifies the difference between the luma CTB size and the minimum luma
/// coding block size of coding units that convey cu_chroma_qp_offset_flag.
pub diff_cu_chroma_qp_offset_depth: u32,
/// chroma_qp_offset_list_len_minus1 plus 1 specifies the number of
/// `cb_qp_offset_list[ i ]` and `cr_qp_offset_list[ i ]` syntax elements that
/// are present in the PPS.
pub chroma_qp_offset_list_len_minus1: u32,
/// Specify offsets used in the derivation of Qp′Cb and Qp′Cr, respectively.
pub cb_qp_offset_list: [i32; 6],
/// Specify offsets used in the derivation of Qp′Cb and Qp′Cr, respectively.
pub cr_qp_offset_list: [i32; 6],
/// The base 2 logarithm of the scaling parameter that is used to scale
/// sample adaptive offset (SAO) offset values for luma samples.
pub log2_sao_offset_scale_luma: u32,
/// The base 2 logarithm of the scaling parameter that is used to scale SAO
/// offset values for chroma samples.
pub log2_sao_offset_scale_chroma: u32,
}
/// A H.265 Picture Parameter Set.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Pps {
/// Identifies the PPS for reference by other syntax elements.
pub pic_parameter_set_id: u8,
/// Specifies the value of sps_seq_parameter_set_id for the active SPS.
pub seq_parameter_set_id: u8,
/// When set, specifies the presence of the syntax element
/// dependent_slice_segment_flag in the slice segment headers for coded
/// pictures referring to the PPS. When not set, specifies the absence of
/// the syntax element dependent_slice_segment_flag in the slice segment
/// headers for coded pictures referring to the PPS.
pub dependent_slice_segments_enabled_flag: bool,
/// When set, indicates that the pic_output_flag syntax element is present
/// in the associated slice headers. When not set, indicates that the
/// pic_output_flag syntax element is not present in the associated slice
/// headers.
pub output_flag_present_flag: bool,
/// Specifies the number of extra slice header bits that are present in the
/// slice header RBSP for coded pictures referring to the PPS.
pub num_extra_slice_header_bits: u8,
/// When not set, specifies that sign bit hiding is disabled. Whens set,
/// specifies that sign bit hiding is enabled.
pub sign_data_hiding_enabled_flag: bool,
/// When set, specifies that cabac_init_flag is present in slice headers
/// referring to the PPS. When not set, specifies that cabac_init_flag is
/// not present in slice headers referring to the PPS.
pub cabac_init_present_flag: bool,
/// Specifies the inferred value of num_ref_idx_l0_active_minus1 for P and B
/// slices with num_ref_idx_active_override_flag not set.
pub num_ref_idx_l0_default_active_minus1: u8,
/// Specifies the inferred value of num_ref_idx_l1_active_minus1 for B
/// slices with num_ref_idx_active_override_flag not set.
pub num_ref_idx_l1_default_active_minus1: u8,
/// init_qp_minus26 plus 26 specifies the initial value of SliceQpY for each
/// slice referring to the PPS. The initial value of SliceQpY is modified at
/// the slice segment layer when a non-zero value of slice_qp_delta is
/// decoded.
pub init_qp_minus26: i8,
/// When not set, specifies that intra prediction allows usage of residual
/// data and decoded samples of neighbouring coding blocks coded using
/// either intra or inter prediction modes. When set, specifies constrained
/// intra prediction, in which case intra prediction only uses residual data
/// and decoded samples from neighbouring coding blocks coded using intra
/// prediction modes.
pub constrained_intra_pred_flag: bool,
/// When set, specifies that transform_skip_flag may be present in the
/// residual coding syntax. When not set, specifies that transform_skip_flag
/// is not present in the residual coding syntax.
pub transform_skip_enabled_flag: bool,
/// When set, specifies that the diff_cu_qp_delta_depth syntax element is
/// present in the PPS and that cu_qp_delta_abs may be present in the
/// transform unit syntax and the palette syntax. When not set, specifies
/// that the diff_cu_qp_delta_depth syntax element is not present in the PPS
/// and that cu_qp_delta_abs is not present in the transform unit syntax and
/// the palette syntax.
pub cu_qp_delta_enabled_flag: bool,
/*if cu_qp_delta_enabled_flag */
/// Specifies the difference between the luma CTB size and the minimum luma
/// coding block size of coding units that convey cu_qp_delta_abs and
/// cu_qp_delta_sign_flag.
pub diff_cu_qp_delta_depth: u8,
/// Specifies the offsets to the luma quantization parameter Qp′Y used for
/// deriving Qp′Cb and Qp′Cr, respectively.
pub cb_qp_offset: i8,
/// Specifies the offsets to the luma quantization parameter Qp′Y used for
/// deriving Qp′Cb and Qp′Cr, respectively.
pub cr_qp_offset: i8,
/// When set, indicates that the slice_cb_qp_offset and slice_cr_qp_offset
/// syntax elements are present in the associated slice headers. When not
/// set, indicates that these syntax elements are not present in the
/// associated slice headers. When ChromaArrayType is equal to 0,
/// pps_slice_chroma_qp_offsets_present_flag shall be equal to 0
pub slice_chroma_qp_offsets_present_flag: bool,
/// When not set, specifies that weighted prediction is not applied to P
/// slices. When set, specifies that weighted prediction is applied to P
/// slices.
pub weighted_pred_flag: bool,
/// When not set, specifies that the default weighted prediction is applied
/// to B slices. When set, specifies that weighted prediction is applied to
/// B slices.
pub weighted_bipred_flag: bool,
/// When set, specifies that `cu_transquant_bypass_flag` is present, When
/// not set, specifies that `cu_transquant_bypass_flag` is not present.
pub transquant_bypass_enabled_flag: bool,
/// When set, specifies that there is more than one tile in each picture
/// referring to the PPS. When not set, specifies that there is only one
/// tile in each picture referring to the PPS.
pub tiles_enabled_flag: bool,
/// When set, specifies that a specific synchronization process for context
/// variables, and when applicable, Rice parameter initialization states and
/// palette predictor variables, is invoked before decoding the CTU which
/// includes the first CTB of a row of CTBs in each tile in each picture
/// referring to the PPS, and a specific storage process for context
/// variables, and when applicable, Rice parameter initialization states and
/// palette predictor variables, is invoked after decoding the CTU which
/// includes the second CTB of a row of CTBs in each tile in each picture
/// referring to the PPS. When not set, specifies that no specific
/// synchronization process for context variables, and when applicable, Rice
/// parameter initialization states and palette predictor variables, is
/// required to be invoked before decoding the CTU which includes the first
/// CTB of a row of CTBs in each tile in each picture referring to the PPS,
/// and no specific storage process for context variables, and when
/// applicable, Rice parameter initialization states and palette predictor
/// variables, is required to be invoked after decoding the CTU which
/// includes the second CTB of a row of CTBs in each tile in each picture
/// referring to the PPS.
pub entropy_coding_sync_enabled_flag: bool,
/// num_tile_columns_minus1 plus 1 specifies the number of tile columns
/// partitioning the picture.
pub num_tile_columns_minus1: u8,
/// num_tile_rows_minus1 plus 1 specifies the number of tile rows
/// partitioning the picture.
pub num_tile_rows_minus1: u8,
/// When set, specifies that tile column boundaries and likewise tile row
/// boundaries are distributed uniformly across the picture. When not set,
/// specifies that tile column boundaries and likewise tile row boundaries
/// are not distributed uniformly across the picture but signalled
/// explicitly using the syntax elements `column_width_minus1[ i ]` and
/// `row_height_minus1[ i ]`.
pub uniform_spacing_flag: bool,
/// `column_width_minus1[ i ]` plus 1 specifies the width of the i-th tile
/// column in units of CTBs.
pub column_width_minus1: [u32; 19],
/// `row_height_minus1[ i ]` plus 1 specifies the height of the i-th tile row
/// in units of CTBs.
pub row_height_minus1: [u32; 21],
/// When set, specifies that in-loop filtering operations may be performed
/// across tile boundaries in pictures referring to the PPS. When not set,
/// specifies that in-loop filtering operations are not performed across
/// tile boundaries in pictures referring to the PPS. The in-loop filtering
/// operations include the deblocking filter and sample adaptive offset
/// filter operations.
pub loop_filter_across_tiles_enabled_flag: bool,
/// When set, specifies that in-loop filtering operations may be performed
/// across left and upper boundaries of slices referring to the PPS. When
/// not set, specifies that in-loop filtering operations are not performed
/// across left and upper boundaries of slices referring to the PPS. The in-
/// loop filtering operations include the deblocking filter and sample
/// adaptive offset filter operations.
pub loop_filter_across_slices_enabled_flag: bool,
/// When set, specifies the presence of deblocking filter control syntax
/// elements in the PPS. When not set, specifies the absence of deblocking
/// filter control syntax elements in the PPS.
pub deblocking_filter_control_present_flag: bool,
/// When set, specifies the presence of deblocking_filter_override_flag in
/// the slice headers for pictures referring to the PPS. When not set,
/// specifies the absence of deblocking_filter_override_flag in the slice
/// headers for pictures referring to the PPS.
pub deblocking_filter_override_enabled_flag: bool,
/// When set, specifies that the operation of deblocking filter is not
/// applied for slices referring to the PPS in which
/// slice_deblocking_filter_disabled_flag is not present. When not set,
/// specifies that the operation of the deblocking filter is applied for
/// slices referring to the PPS in which
/// slice_deblocking_filter_disabled_flag is not present.
pub deblocking_filter_disabled_flag: bool,
/// Specify the default deblocking parameter offsets for β and tC (divided
/// by 2) that are applied for slices referring to the PPS, unless the
/// default deblocking parameter offsets are overridden by the deblocking
/// parameter offsets present in the slice headers of the slices referring
/// to the PPS.
pub beta_offset_div2: i8,
/// Specify the default deblocking parameter offsets for β and tC (divided
/// by 2) that are applied for slices referring to the PPS, unless the
/// default deblocking parameter offsets are overridden by the deblocking
/// parameter offsets present in the slice headers of the slices referring
/// to the PPS.
pub tc_offset_div2: i8,
/// When set, specifies that the scaling list data used for the pictures
/// referring to the PPS are derived based on the scaling lists specified by
/// the active SPS and the scaling lists specified by the PPS.
/// pps_scaling_list_data_present_flag equal to 0 specifies that the scaling
/// list data used for the pictures referring to the PPS are inferred to be
/// equal to those specified by the active SPS.
pub scaling_list_data_present_flag: bool,
/// The scaling list data.
pub scaling_list: ScalingLists,
/// When set, specifies that the syntax structure
/// ref_pic_lists_modification( ) is present in the slice segment header.
/// When not set, specifies that the syntax structure
/// ref_pic_lists_modification( ) is not present in the slice segment header
pub lists_modification_present_flag: bool,
/// log2_parallel_merge_level_minus2 plus 2 specifies the value of the
/// variable Log2ParMrgLevel, which is used in the derivation process for
/// luma motion vectors for merge mode as specified in clause 8.5.3.2.2 and
/// the derivation process for spatial merging candidates as specified in
/// clause 8.5.3.2.3.
pub log2_parallel_merge_level_minus2: u8,
/// When not set, specifies that no slice segment header extension syntax
/// elements are present in the slice segment headers for coded pictures
/// referring to the PPS. When set, specifies that slice segment header
/// extension syntax elements are present in the slice segment headers for
/// coded pictures referring to the PPS.
pub slice_segment_header_extension_present_flag: bool,
/// When set, specifies that the syntax elements pps_range_extension_flag,
/// pps_multilayer_extension_flag, pps_3d_extension_flag,
/// pps_scc_extension_flag, and pps_extension_4bits are present in the
/// picture parameter set RBSP syntax structure. When not set, specifies
/// that these syntax elements are not present.
pub extension_present_flag: bool,
/// When setspecifies that the pps_range_extension( ) syntax structure is
/// present in the PPS RBSP syntax structure. When not set, specifies that
/// this syntax structure is not present.
pub range_extension_flag: bool,
/// The range extension data.
pub range_extension: PpsRangeExtension,
pub scc_extension_flag: bool,
/// The SCC extension data.
pub scc_extension: PpsSccExtension,
// Internal variables.
/// Equivalent to QpBdOffsetY in the specification.
pub qp_bd_offset_y: u32,
/// The nuh_temporal_id_plus1 - 1 of the associated NALU.
pub temporal_id: u8,
}
impl Default for Pps {
fn default() -> Self {
Self {
pic_parameter_set_id: Default::default(),
seq_parameter_set_id: Default::default(),
dependent_slice_segments_enabled_flag: Default::default(),
output_flag_present_flag: Default::default(),
num_extra_slice_header_bits: Default::default(),
sign_data_hiding_enabled_flag: Default::default(),
cabac_init_present_flag: Default::default(),
num_ref_idx_l0_default_active_minus1: Default::default(),
num_ref_idx_l1_default_active_minus1: Default::default(),
init_qp_minus26: Default::default(),
constrained_intra_pred_flag: Default::default(),
transform_skip_enabled_flag: Default::default(),
cu_qp_delta_enabled_flag: Default::default(),
diff_cu_qp_delta_depth: Default::default(),
cb_qp_offset: Default::default(),
cr_qp_offset: Default::default(),
slice_chroma_qp_offsets_present_flag: Default::default(),
weighted_pred_flag: Default::default(),
weighted_bipred_flag: Default::default(),
transquant_bypass_enabled_flag: Default::default(),
tiles_enabled_flag: Default::default(),
entropy_coding_sync_enabled_flag: Default::default(),
num_tile_columns_minus1: Default::default(),
num_tile_rows_minus1: Default::default(),
uniform_spacing_flag: true,
column_width_minus1: Default::default(),
row_height_minus1: Default::default(),
loop_filter_across_tiles_enabled_flag: true,
loop_filter_across_slices_enabled_flag: Default::default(),
deblocking_filter_control_present_flag: Default::default(),
deblocking_filter_override_enabled_flag: Default::default(),
deblocking_filter_disabled_flag: Default::default(),
beta_offset_div2: Default::default(),
tc_offset_div2: Default::default(),
scaling_list_data_present_flag: Default::default(),
scaling_list: Default::default(),
lists_modification_present_flag: Default::default(),
log2_parallel_merge_level_minus2: Default::default(),
slice_segment_header_extension_present_flag: Default::default(),
extension_present_flag: Default::default(),
range_extension_flag: Default::default(),
range_extension: Default::default(),
qp_bd_offset_y: Default::default(),
scc_extension: Default::default(),
scc_extension_flag: Default::default(),
temporal_id: Default::default(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScalingLists {
/// plus 8 specifies the value of the variable `ScalingFactor[ 2 ][ matrixId
/// ] [ 0 ][ 0 ]` for the scaling list for the 16x16 size.
pub scaling_list_dc_coef_minus8_16x16: [i16; 6],
/// plus 8 specifies the value of the variable `ScalingFactor[ 3 ][ matrixId
/// ][ 0 ][ 0 ]` for the scaling list for the 32x32 size.
pub scaling_list_dc_coef_minus8_32x32: [i16; 6],
/// The 4x4 scaling list.
pub scaling_list_4x4: [[u8; 16]; 6],
/// The 8x8 scaling list.
pub scaling_list_8x8: [[u8; 64]; 6],
/// The 16x16 scaling list.
pub scaling_list_16x16: [[u8; 64]; 6],
/// The 32x32 scaling list.
pub scaling_list_32x32: [[u8; 64]; 6],
}
impl Default for ScalingLists {
fn default() -> Self {
Self {
scaling_list_dc_coef_minus8_16x16: Default::default(),
scaling_list_dc_coef_minus8_32x32: Default::default(),
scaling_list_4x4: Default::default(),
scaling_list_8x8: [[0; 64]; 6],
scaling_list_16x16: [[0; 64]; 6],
scaling_list_32x32: [[0; 64]; 6],
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RefPicListModification {
/// Whenset, indicates that reference picture list 0 is specified explicitly
/// by a list of `list_entry_l0[ i ]` values. When not set, indicates that
/// reference picture list 0 is determined implicitly.
pub ref_pic_list_modification_flag_l0: bool,
/// `list_entry_l0[ i ]` specifies the index of the reference picture in
/// RefPicListTemp0 to be placed at the current position of reference
/// picture list 0.
pub list_entry_l0: Vec<u32>,
/// Whenset, indicates that reference picture list 1 is specified explicitly
/// by a list of `list_entry_l1[ i ]` values. When not set, indicates that
/// reference picture list 1 is determined implicitly.
pub ref_pic_list_modification_flag_l1: bool,
/// `list_entry_l1[ i ]` specifies the index of the reference picture in
/// RefPicListTemp1 to be placed at the current position of reference
/// picture list 1.
pub list_entry_l1: Vec<u32>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct PredWeightTable {
/// The base 2 logarithm of the denominator for all luma weighting factors.
pub luma_log2_weight_denom: u8,
/// The difference of the base 2 logarithm of the denominator for all chroma
/// weighting factors.
pub delta_chroma_log2_weight_denom: i8,
/// `luma_weight_l0_flag[ i ]` set specifies that weighting factors for the
/// luma component of list 0 prediction using `RefPicList0[ i ]` are present.
/// `luma_weight_l0_flag[ i ]` not set specifies that these weighting factors
/// are not present.
pub luma_weight_l0_flag: [bool; 15],
/// `chroma_weight_l0_flag[ i ]` set specifies that weighting factors for the
/// chroma prediction values of list 0 prediction using `RefPicList0[ i ]` are
/// present. `chroma_weight_l0_flag[ i ]` not set specifies that these
/// weighting factors are not present.
pub chroma_weight_l0_flag: [bool; 15],
/// `delta_luma_weight_l0[ i ]` is the difference of the weighting factor
/// applied to the luma prediction value for list 0 prediction using
/// `RefPicList0[ i ]`.
pub delta_luma_weight_l0: [i8; 15],
/// `luma_offset_l0[ i ]` is the additive offset applied to the luma
/// prediction value for list 0 prediction using `RefPicList0[ i ]`.
pub luma_offset_l0: [i8; 15],
/// `delta_chroma_weight_l0[ i ][ j ]` is the difference of the weighting
/// factor applied to the chroma prediction values for list 0 prediction
/// using `RefPicList0[ i ]` with j equal to 0 for Cb and j equal to 1 for Cr.
pub delta_chroma_weight_l0: [[i8; 2]; 15],
/// `delta_chroma_offset_l0[ i ][ j ]` is the difference of the additive
/// offset applied to the chroma prediction values for list 0 prediction
/// using `RefPicList0[ i ]` with j equal to 0 for Cb and j equal to 1 for Cr.
pub delta_chroma_offset_l0: [[i16; 2]; 15],
// `luma_weight_l1_flag[ i ]`, `chroma_weight_l1_flag[ i ]`,
// `delta_luma_weight_l1[ i ]`, `luma_offset_l1[ i ]`, delta_chroma_weight_l1[ i
// `][ j ]` and `delta_chroma_offset_l1[ i ]`[ j ] have the same
// `semanticsasluma_weight_l0_flag[ i ]`, `chroma_weight_l0_flag[ i ]`,
// `delta_luma_weight_l0[ i ]`, `luma_offset_l0[ i ]`, `delta_chroma_weight_l0[ i
// ][ j ]` and `delta_chroma_offset_l0[ i ][ j ]`, respectively, with `l0`, `L0`,
// `list 0` and `List0` replaced by `l1`, `L1`, `list 1` and `List1`, respectively.
pub luma_weight_l1_flag: [bool; 15],
pub chroma_weight_l1_flag: [bool; 15],
pub delta_luma_weight_l1: [i8; 15],
pub luma_offset_l1: [i8; 15],
pub delta_chroma_weight_l1: [[i8; 2]; 15],
pub delta_chroma_offset_l1: [[i16; 2]; 15],
// Calculated.
/// Same as ChromaLog2WeightDenom in the specification.
pub chroma_log2_weight_denom: u8,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ShortTermRefPicSet {
/// When set, specifies that the stRpsIdx-th candidate short-term RPS is
/// predicted from another candidate short-term RPS, which is referred to as
/// the source candidate short-term RPS.
pub inter_ref_pic_set_prediction_flag: bool,
/// delta_idx_minus1 plus 1 specifies the difference between the value of
/// stRpsIdx and the index, into the list of the candidate short-term RPSs
/// specified in the SPS, of the source candidate short-term RPS.
pub delta_idx_minus1: u8,
/// delta_rps_sign and abs_delta_rps_minus1 together specify the value of
/// the variable deltaRps.
pub delta_rps_sign: bool,
/// delta_rps_sign and abs_delta_rps_minus1 together specify the value of
/// the variable deltaRps.
pub abs_delta_rps_minus1: u16,
/// specifies the number of entries in the stRpsIdx-th candidate short-term
/// RPS that have picture order count values less than the picture order
/// count value of the current picture.
pub num_negative_pics: u8,
/// specifies the number of entries in the stRpsIdx-th candidate short-term
/// RPS that have picture order count values greater than the picture order
/// count value of the current picture.
pub num_positive_pics: u8,
/// Same as UsedByCurrPicS0 in the specification.
pub used_by_curr_pic_s0: [bool; MAX_SHORT_TERM_REF_PIC_SETS],
/// Same as UsedByCurrPicS1 in the specification.
pub used_by_curr_pic_s1: [bool; MAX_SHORT_TERM_REF_PIC_SETS],
/// Same as DeltaPocS0 in the specification.
pub delta_poc_s0: [i32; MAX_SHORT_TERM_REF_PIC_SETS],
/// Same as DeltaPocS1 in the specification.
pub delta_poc_s1: [i32; MAX_SHORT_TERM_REF_PIC_SETS],
/// Same as NumDeltaPocs in the specification.
pub num_delta_pocs: u32,
}
impl Default for ShortTermRefPicSet {
fn default() -> Self {
Self {
inter_ref_pic_set_prediction_flag: Default::default(),
delta_idx_minus1: Default::default(),
delta_rps_sign: Default::default(),
abs_delta_rps_minus1: Default::default(),
num_negative_pics: Default::default(),
num_positive_pics: Default::default(),
used_by_curr_pic_s0: [false; MAX_SHORT_TERM_REF_PIC_SETS],
used_by_curr_pic_s1: [false; MAX_SHORT_TERM_REF_PIC_SETS],
delta_poc_s0: [0; MAX_SHORT_TERM_REF_PIC_SETS],
delta_poc_s1: [0; MAX_SHORT_TERM_REF_PIC_SETS],
num_delta_pocs: Default::default(),
}
}
}
#[derive(N, Clone, Copy, Debug, PartialEq, Eq)]
/// See table 7-7 in the specification.
pub enum SliceType {
B = 0,
P = 1,
I = 2,
}
impl SliceType {
/// Whether this is a P slice. See table 7-7 in the specification.
pub fn is_p(&self) -> bool {
matches!(self, SliceType::P)
}
/// Whether this is a B slice. See table 7-7 in the specification.
pub fn is_b(&self) -> bool {
matches!(self, SliceType::B)
}
/// Whether this is an I slice. See table 7-7 in the specification.
pub fn is_i(&self) -> bool {
matches!(self, SliceType::I)
}
}
impl Default for SliceType {
fn default() -> Self {
Self::P
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SliceHeader {
/// When set, specifies that the slice segment is the first slice segment of
/// the picture in decoding order. When not set, specifies that the slice
/// segment is not the first slice segment of the picture in decoding order.
pub first_slice_segment_in_pic_flag: bool,
/// Affects the output of previously-decoded pictures in the decoded picture
/// buffer after the decoding of an IDR or a BLA picture that is not the
/// first picture in the bitstream as specified in Annex C.
pub no_output_of_prior_pics_flag: bool,
/// Specifies the value of pps_pic_parameter_set_id for the PPS in use.
pub pic_parameter_set_id: u8,
/// When set, specifies that the value of each slice segment header syntax
/// element that is not present is inferred to be equal to the value of the
/// corresponding slice segment header syntax element in the slice header.
pub dependent_slice_segment_flag: bool,
/// Specifies the address of the first CTB in the slice segment, in CTB
/// raster scan of a picture.
pub segment_address: u32,
/// Specifies the coding type of the slice according to Table 7-7.
pub type_: SliceType,
/// Affects the decoded picture output and removal processes as specified in
/// Annex C.
pub pic_output_flag: bool,
/// Specifies the colour plane associated with the current slice RBSP when
/// separate_colour_plane_flag is set. The value of colour_plane_id shall be
/// in the range of 0 to 2, inclusive. colour_plane_id values 0, 1 and 2
/// correspond to the Y, Cb and Cr planes, respectively.
pub colour_plane_id: u8,
/// Specifies the picture order count modulo MaxPicOrderCntLsb for the
/// current picture. The length of the slice_pic_order_cnt_lsb syntax
/// element is log2_max_pic_order_cnt_lsb_minus4 + 4 bits.
pub pic_order_cnt_lsb: u16,
/// When set, specifies that the short-term RPS of the current picture is
/// derived based on one of the st_ref_pic_set( ) syntax structures in the
/// active SPS that is identified by the syntax element
/// short_term_ref_pic_set_idx in the slice header. When not set, specifies
/// that the short-term RPS of the current picture is derived based on the
/// st_ref_pic_set( ) syntax structure that is directly included in the
/// slice headers of the current picture.
pub short_term_ref_pic_set_sps_flag: bool,
/// The st_ref_pic_set() data.
pub short_term_ref_pic_set: ShortTermRefPicSet,
/// Specifies the index, into the list of the st_ref_pic_set( ) syntax
/// structures included in the active SPS, of the st_ref_pic_set( ) syntax
/// structure that is used for derivation of the short-term RPS of the
/// current picture.
pub short_term_ref_pic_set_idx: u8,
/// Specifies the number of entries in the long-term RPS of the current
/// picture that are derived based on the candidate long-term reference
/// pictures specified in the active SPS.
pub num_long_term_sps: u8,
/// Specifies the number of entries in the long-term RPS of the current
/// picture that are directly signalled in the slice header.
pub num_long_term_pics: u8,
/// `lt_idx_sps[ i ]` specifies an index, into the list of candidate long-term
/// reference pictures specified in the active SPS, of the i-th entry in the
/// long-term RPS of the current picture.
pub lt_idx_sps: [u8; 16],
/// Same as PocLsbLt in the specification.
pub poc_lsb_lt: [u32; 16],
/// Same as UsedByCurrPicLt in the specification.
pub used_by_curr_pic_lt: [bool; 16],
/// When set, specifies that that `delta_poc_msb_cycle_lt[i]` is present.
pub delta_poc_msb_present_flag: [bool; 16],
/// Same as DeltaPocMsbCycleLt in the specification.
pub delta_poc_msb_cycle_lt: [u32; 16],
/// Specifies whether temporal motion vector predictors can be used for
/// inter prediction. If slice_temporal_mvp_enabled_flag is not set, the
/// syntax elements of the current picture shall be constrained such that no
/// temporal motion vector predictor is used in decoding of the current
/// picture. Otherwise (slice_temporal_mvp_enabled_flag is set), temporal
/// motion vector predictors may be used in decoding of the current picture.
pub temporal_mvp_enabled_flag: bool,
/// When set, specifies that SAO is enabled for the luma component in the
/// current slice; slice_sao_luma_flag not set specifies that SAO is
/// disabled for the luma component in the current slice.
pub sao_luma_flag: bool,
/// When set, specifies that SAO is enabled for the chroma component in the
/// current slice; When not set, specifies that SAO is disabled for the
/// chroma component in the current slice.
pub sao_chroma_flag: bool,
/// When set, specifies that the syntax element num_ref_idx_l0_active_minus1
/// is present for P and B slices and that the syntax element
/// num_ref_idx_l1_active_minus1 is present for B slices. When not set,
/// specifies that the syntax elements num_ref_idx_l0_active_minus1 and
/// num_ref_idx_l1_active_minus1 are not present.
pub num_ref_idx_active_override_flag: bool,
/// Specifies the maximum reference index for
/// reference picture list 0 that may be used to decode the slice.
pub num_ref_idx_l0_active_minus1: u8,
/// Specifies the maximum reference index for reference picture list 1 that
/// may be used to decode the slice.
pub num_ref_idx_l1_active_minus1: u8,
/// The RefPicListModification data.
pub ref_pic_list_modification: RefPicListModification,
/// When set, indicates that the mvd_coding( x0, y0, 1 ) syntax structure is
/// not parsed and `MvdL1[ x0 ]`[ y0 `][ compIdx ]` is set equal to 0 for
/// compIdx = 0..1. When not set, indicates that the mvd_coding( x0, y0, 1 )
/// syntax structure is parsed.
pub mvd_l1_zero_flag: bool,
/// Specifies the method for determining the initialization table used in
/// the initialization process for context variables.
pub cabac_init_flag: bool,
/// When set, specifies that the collocated picture used for temporal motion
/// vector prediction is derived from reference picture list 0. When not
/// set, specifies that the collocated picture used for temporal motion
/// vector prediction is derived from reference picture list 1.
pub collocated_from_l0_flag: bool,
/// Specifies the reference index of the collocated picture used for
/// temporal motion vector prediction.
pub collocated_ref_idx: u8,
/// The PredWeightTable data.
pub pred_weight_table: PredWeightTable,
/// Specifies the maximum number of merging motion vector prediction (MVP)
/// candidates supported in the slice subtracted from 5.
pub five_minus_max_num_merge_cand: u8,
/// Specifies that the resolution of motion vectors for inter prediction in
/// the current slice is integer. When not set, specifies
/// that the resolution of motion vectors for inter prediction in the
/// current slice that refer to pictures other than the current picture is
/// fractional with quarter-sample precision in units of luma samples.
pub use_integer_mv_flag: bool,
/// Specifies the initial value of QpY to be used for the coding blocks in
/// the slice until modified by the value of CuQpDeltaVal in the coding unit
/// layer.
pub qp_delta: i8,
/// Specifies a difference to be added to the value of pps_cb_qp_offset when
/// determining the value of the Qp′Cb quantization parameter.
pub cb_qp_offset: i8,
/// Specifies a difference to be added to the value of pps_cb_qr_offset when
/// determining the value of the Qp′Cr quantization parameter.
pub cr_qp_offset: i8,
/// Specifies offsets to the quantization parameter values qP derived in
/// clause 8.6.2 for luma, Cb, and Cr components, respectively.
pub slice_act_y_qp_offset: i8,
/// Specifies offsets to the quantization parameter values qP derived in
/// clause 8.6.2 for luma, Cb, and Cr components, respectively.
pub slice_act_cb_qp_offset: i8,
/// Specifies offsets to the quantization parameter values qP derived in
/// clause 8.6.2 for luma, Cb, and Cr components, respectively.
pub slice_act_cr_qp_offset: i8,
/// When set, specifies that the cu_chroma_qp_offset_flag may be present in
/// the transform unit syntax. When not set, specifies that the
/// cu_chroma_qp_offset_flag is not present in the transform unit syntax.
pub cu_chroma_qp_offset_enabled_flag: bool,
/// When set, specifies that deblocking parameters are present in the slice
/// header. When not set, specifies that deblocking parameters are not
/// present in the slice header.
pub deblocking_filter_override_flag: bool,
/// When set, specifies that the operation of the deblocking filter is not
/// applied for the current slice. When not set, specifies that the
/// operation of the deblocking filter is applied for the current slice.
pub deblocking_filter_disabled_flag: bool,
/// Specifies the deblocking parameter offsets for β and tC (divided by 2)
/// for the current slice.
pub beta_offset_div2: i8,
/// Specifies the deblocking parameter offsets for β and tC (divided by 2)
/// for the current slice.
pub tc_offset_div2: i8,
/// When set, specifies that in-loop filtering operations may be performed
/// across the left and upper boundaries of the current slice. When not
/// set, specifies that in-loop operations are not performed across left and
/// upper boundaries of the current slice. The in-loop filtering operations
/// include the deblocking filter and sample adaptive offset filter.
pub loop_filter_across_slices_enabled_flag: bool,
/// Specifies the number of `entry_point_offset_minus1[ i ]` syntax elements
/// in the slice header.
pub num_entry_point_offsets: u32,
/// offset_len_minus1 plus 1 specifies the length, in bits, of the
/// `entry_point_offset_minus1[ i ]` syntax elements.
pub offset_len_minus1: u8,
/// `entry_point_offset_minus1[ i ]` plus 1 specifies the i-th entry point
/// offset in bytes, and is represented by offset_len_minus1 plus 1 bits.
/// The slice segment data that follow the slice segment header consists of
/// num_entry_point_offsets + 1 subsets, with subset index values ranging
/// from 0 to num_entry_point_offsets, inclusive. See the specification for
/// more details.
pub entry_point_offset_minus1: [u32; 32],
/// Same as NumPicTotalCurr in the specification.
pub num_pic_total_curr: u32,
// Size of slice_header() in bits.
pub header_bit_size: u32,
// Number of emulation prevention bytes (EPB) in this slice_header().
pub n_emulation_prevention_bytes: u32,
/// Same as CurrRpsIdx in the specification.
pub curr_rps_idx: u8,
/// Number of bits taken by st_ref_pic_set minus Emulation Prevention Bytes.
pub st_rps_bits: u32,
}
impl Default for SliceHeader {
fn default() -> Self {
Self {
first_slice_segment_in_pic_flag: Default::default(),
no_output_of_prior_pics_flag: Default::default(),
pic_parameter_set_id: Default::default(),
dependent_slice_segment_flag: Default::default(),
segment_address: Default::default(),
type_: Default::default(),
pic_output_flag: true,
colour_plane_id: Default::default(),
pic_order_cnt_lsb: Default::default(),
short_term_ref_pic_set_sps_flag: Default::default(),
short_term_ref_pic_set: Default::default(),
short_term_ref_pic_set_idx: Default::default(),
num_long_term_sps: Default::default(),
num_long_term_pics: Default::default(),
lt_idx_sps: Default::default(),
poc_lsb_lt: Default::default(),
used_by_curr_pic_lt: Default::default(),
delta_poc_msb_present_flag: Default::default(),
delta_poc_msb_cycle_lt: Default::default(),
temporal_mvp_enabled_flag: Default::default(),
sao_luma_flag: Default::default(),
sao_chroma_flag: Default::default(),
num_ref_idx_active_override_flag: Default::default(),
num_ref_idx_l0_active_minus1: Default::default(),
num_ref_idx_l1_active_minus1: Default::default(),
ref_pic_list_modification: Default::default(),
mvd_l1_zero_flag: Default::default(),
cabac_init_flag: Default::default(),
collocated_from_l0_flag: true,
collocated_ref_idx: Default::default(),
pred_weight_table: Default::default(),
five_minus_max_num_merge_cand: Default::default(),
use_integer_mv_flag: Default::default(),
qp_delta: Default::default(),
cb_qp_offset: Default::default(),
cr_qp_offset: Default::default(),
slice_act_y_qp_offset: Default::default(),
slice_act_cb_qp_offset: Default::default(),
slice_act_cr_qp_offset: Default::default(),
cu_chroma_qp_offset_enabled_flag: Default::default(),
deblocking_filter_override_flag: Default::default(),
deblocking_filter_disabled_flag: Default::default(),
beta_offset_div2: Default::default(),
tc_offset_div2: Default::default(),
loop_filter_across_slices_enabled_flag: Default::default(),
num_entry_point_offsets: Default::default(),
offset_len_minus1: Default::default(),
entry_point_offset_minus1: Default::default(),
num_pic_total_curr: Default::default(),
header_bit_size: Default::default(),
n_emulation_prevention_bytes: Default::default(),
curr_rps_idx: Default::default(),
st_rps_bits: Default::default(),
}
}
}
/// A H265 slice. An integer number of macroblocks or macroblock pairs ordered
/// consecutively in the raster scan within a particular slice group
pub struct Slice<'a> {
/// The slice header.
pub header: SliceHeader,
/// The NAL unit backing this slice.
pub nalu: Nalu<'a>,
}
impl<'a> Slice<'a> {
/// Sets the header for dependent slices by copying from an independent
/// slice.
pub fn replace_header(&mut self, header: SliceHeader) -> anyhow::Result<()> {
if !self.header.dependent_slice_segment_flag {
Err(anyhow!(
"Replacing the slice header is only possible for dependent slices"
))
} else {
let first_slice_segment_in_pic_flag = self.header.first_slice_segment_in_pic_flag;
let no_output_of_prior_pics_flag = self.header.no_output_of_prior_pics_flag;
let pic_parameter_set_id = self.header.pic_parameter_set_id;
let dependent_slice_segment_flag = self.header.dependent_slice_segment_flag;
let segment_address = self.header.segment_address;
let offset_len_minus1 = self.header.offset_len_minus1;
let entry_point_offset_minus1 = self.header.entry_point_offset_minus1;
let num_pic_total_curr = self.header.num_pic_total_curr;
let header_bit_size = self.header.header_bit_size;
let n_emulation_prevention_bytes = self.header.n_emulation_prevention_bytes;
let curr_rps_idx = self.header.curr_rps_idx;
let st_rps_bits = self.header.st_rps_bits;
self.header = header;
self.header.first_slice_segment_in_pic_flag = first_slice_segment_in_pic_flag;
self.header.no_output_of_prior_pics_flag = no_output_of_prior_pics_flag;
self.header.pic_parameter_set_id = pic_parameter_set_id;
self.header.dependent_slice_segment_flag = dependent_slice_segment_flag;
self.header.segment_address = segment_address;
self.header.offset_len_minus1 = offset_len_minus1;
self.header.entry_point_offset_minus1 = entry_point_offset_minus1;
self.header.num_pic_total_curr = num_pic_total_curr;
self.header.header_bit_size = header_bit_size;
self.header.n_emulation_prevention_bytes = n_emulation_prevention_bytes;
self.header.curr_rps_idx = curr_rps_idx;
self.header.st_rps_bits = st_rps_bits;
Ok(())
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SublayerHrdParameters {
// NOTE: The value of CpbCnt is cpb_cnt_minus1[i] + 1, and cpb_cnt_minus1
// ranges from 0..=31
/// `bit_rate_value_minus1[ i ]` (together with bit_rate_scale) specifies the
/// maximum input bit rate for the i-th CPB when the CPB operates at the
/// access unit level
pub bit_rate_value_minus1: [u32; 32],
/// `cpb_size_value_minus1[ i ]` is used together with cpb_size_scale to
/// specify the i-th CPB size when the CPB operates at the access unit
/// level.
pub cpb_size_value_minus1: [u32; 32],
/// `cpb_size_du_value_minus1[ i ]` is used together with cpb_size_du_scale to
/// specify the i-th CPB size when the CPB operates at sub-picture level.
pub cpb_size_du_value_minus1: [u32; 32],
/// `bit_rate_du_value_minus1[ i ]` (together with bit_rate_scale) specifies
/// the maximum input bit rate for the i-th CPB when the CPB operates at the
/// sub-picture level.
pub bit_rate_du_value_minus1: [u32; 32],
/// `cbr_flag[ i ]` not set specifies that to decode this CVS by the HRD using
/// the i-th CPB specification.
pub cbr_flag: [bool; 32],
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HrdParams {
/// When set, specifies that NAL HRD parameters (pertaining to the Type II
/// bitstream conformance point) are present in the hrd_parameters( ) syntax
/// structure. When not set, specifies that NAL HRD parameters are not
/// present in the hrd_parameters( ) syntax structure.
pub nal_hrd_parameters_present_flag: bool,
/// When set, specifies that VCL HRD parameters (pertaining to the Type I
/// bitstream conformance point) are present in the hrd_parameters( ) syntax
/// structure. When not set, specifies that VCL HRD parameters are not
/// present in the hrd_parameters( ) syntax structure.
pub vcl_hrd_parameters_present_flag: bool,
/// When set, specifies that sub-picture level HRD parameters are present
/// and the HRD may operate at access unit level or sub-picture level. When
/// not set, specifies that sub-picture level HRD parameters are not present
/// and the HRD operates at access unit level.
pub sub_pic_hrd_params_present_flag: bool,
/// Used to specify the clock sub-tick. A clock sub-tick is the minimum
/// interval of time that can be represented in the coded data when
/// sub_pic_hrd_params_present_flag is set.
pub tick_divisor_minus2: u8,
/// du_cpb_removal_delay_increment_length_minus1 plus 1 specifies the
/// length, in bits, of the `du_cpb_removal_delay_increment_minus1[ i ]` and
/// du_common_cpb_removal_delay_increment_minus1 syntax elements of the
/// picture timing SEI message and the du_spt_cpb_removal_delay_increment
/// syntax element in the decoding unit information SEI message.
pub du_cpb_removal_delay_increment_length_minus1: u8,
/// When set, specifies that sub-picture level CPB removal delay parameters
/// are present in picture timing SEI messages and no decoding unit
/// information SEI message is available (in the CVS or provided through
/// external means not specified in this Specification). When not set,
/// specifies that sub-picture level CPB removal delay parameters are
/// present in decoding unit information SEI messages and picture timing SEI
/// messages do not include sub-picture level CPB removal delay parameters.
pub sub_pic_cpb_params_in_pic_timing_sei_flag: bool,
/// dpb_output_delay_du_length_minus1 plus 1 specifies the length, in bits,
/// of the pic_dpb_output_du_delay syntax element in the picture timing SEI
/// message and the pic_spt_dpb_output_du_delay syntax element in the
/// decoding unit information SEI message.
pub dpb_output_delay_du_length_minus1: u8,
/// Together with `bit_rate_value_minus1[ i ]`, specifies the maximum input
/// bit rate of the i-th CPB.
pub bit_rate_scale: u8,
/// Together with `cpb_size_du_value_minus1[ i ]`, specifies the CPB size of
/// the i-th CPB when the CPB operates at sub-picture level.
pub cpb_size_scale: u8,
/// Together with `cpb_size_du_value_minus1[ i ]`, specifies the CPB size of
/// the i-th CPB when the CPB operates at sub-picture level.
pub cpb_size_du_scale: u8,
/// initial_cpb_removal_delay_length_minus1 plus 1 specifies the length, in
/// bits, of the `nal_initial_cpb_removal_delay[ i ]`,
/// `nal_initial_cpb_removal_offset[ i ]`, `vcl_initial_cpb_removal_delay[ i ]`
/// and `vcl_initial_cpb_removal_offset[ i ]` syntax elements of the buffering
/// period SEI message.
pub initial_cpb_removal_delay_length_minus1: u8,
/// au_cpb_removal_delay_length_minus1 plus 1 specifies the length, in bits,
/// of the cpb_delay_offset syntax element in the buffering period SEI
/// message and the au_cpb_removal_delay_minus1 syntax element in the
/// picture timing SEI message.
pub au_cpb_removal_delay_length_minus1: u8,
/// dpb_output_delay_length_minus1 plus 1 specifies the length, in bits, of
/// the dpb_delay_offset syntax element in the buffering period SEI message
/// and the pic_dpb_output_delay syntax element in the picture timing SEI
/// message.
pub dpb_output_delay_length_minus1: u8,
/// `fixed_pic_rate_general_flag[ i ]` set indicates that, when HighestTid is
/// equal to i, the temporal distance between the HRD output times of
/// consecutive pictures in output order is constrained as specified in the
/// specification. `fixed_pic_rate_general_flag[ i ]` not set indicates that
/// this constraint may not apply.
pub fixed_pic_rate_general_flag: [bool; 7],
/// `fixed_pic_rate_within_cvs_flag[ i ]` set indicates that, when HighestTid
/// is equal to i, the temporal distance between the HRD output times of
/// consecutive pictures in output order is constrained as specified in the
/// specification. `fixed_pic_rate_within_cvs_flag[ i ]` not set indicates
/// that this constraint may not apply.
pub fixed_pic_rate_within_cvs_flag: [bool; 7],
/// `elemental_duration_in_tc_minus1[ i ]` plus 1 (when present) specifies,
/// when HighestTid is equal to i, the temporal distance, in clock ticks,
/// between the elemental units that specify the HRD output times of
/// consecutive pictures in output order as specified in the specification.
pub elemental_duration_in_tc_minus1: [u32; 7],
/// `low_delay_hrd_flag[ i ]` specifies the HRD operational mode, when
/// HighestTid is equal to i, as specified in Annex C or clause F.13.
pub low_delay_hrd_flag: [bool; 7],
/// `cpb_cnt_minus1[ i ]` plus 1 specifies the number of alternative CPB
/// specifications in the bitstream of the CVS when HighestTid is equal to
/// i.
pub cpb_cnt_minus1: [u32; 7],
/// The NAL HRD data.
pub nal_hrd: [SublayerHrdParameters; 7],
/// The VCL HRD data.
pub vcl_hrd: [SublayerHrdParameters; 7],
}
impl Default for HrdParams {
fn default() -> Self {
Self {
initial_cpb_removal_delay_length_minus1: 23,
au_cpb_removal_delay_length_minus1: 23,
dpb_output_delay_du_length_minus1: 23,
nal_hrd_parameters_present_flag: Default::default(),
vcl_hrd_parameters_present_flag: Default::default(),
sub_pic_hrd_params_present_flag: Default::default(),
tick_divisor_minus2: Default::default(),
du_cpb_removal_delay_increment_length_minus1: Default::default(),
sub_pic_cpb_params_in_pic_timing_sei_flag: Default::default(),
bit_rate_scale: Default::default(),
cpb_size_scale: Default::default(),
cpb_size_du_scale: Default::default(),
dpb_output_delay_length_minus1: Default::default(),
fixed_pic_rate_general_flag: Default::default(),
fixed_pic_rate_within_cvs_flag: Default::default(),
elemental_duration_in_tc_minus1: Default::default(),
low_delay_hrd_flag: Default::default(),
cpb_cnt_minus1: Default::default(),
nal_hrd: Default::default(),
vcl_hrd: Default::default(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VuiParams {
/// When set, specifies that aspect_ratio_idc is present. When not set,
/// specifies that aspect_ratio_idc is not present.
pub aspect_ratio_info_present_flag: bool,
/// Specifies the value of the sample aspect ratio of the luma samples.
pub aspect_ratio_idc: u32,
/// Indicates the horizontal size of the sample aspect ratio (in arbitrary
/// units).
pub sar_width: u32,
/// Indicates the vertical size of the sample aspect ratio (in arbitrary
/// units).
pub sar_height: u32,
/// When set, specifies that the overscan_appropriate_flag is present. When
/// not set, the preferred display method for the video signal is
/// unspecified.
pub overscan_info_present_flag: bool,
/// When set indicates that the cropped decoded pictures output are suitable
/// for display using overscan. When not set, indicates that the cropped
/// decoded pictures output contain visually important information in the
/// entire region out to the edges of the conformance cropping window of the
/// picture, such that the cropped decoded pictures output should not be
/// displayed using overscan.
pub overscan_appropriate_flag: bool,
/// When set, specifies that video_format, video_full_range_flag and
/// colour_description_present_flag are present. When not set, specify that
/// video_format, video_full_range_flag and colour_description_present_flag
/// are not present.
pub video_signal_type_present_flag: bool,
/// Indicates the representation of the pictures as specified in Table E.2,
/// before being coded in accordance with this Specification.
pub video_format: u8,
/// Indicates the black level and range of the luma and chroma signals as
/// derived from E′Y, E′PB, and E′PR or E′R, E′G, and E′B real-valued
/// component signals.
pub video_full_range_flag: bool,
/// When set, specifies that colour_primaries, transfer_characteristics, and
/// matrix_coeffs are present. When not set, specifies that
/// colour_primaries, transfer_characteristics, and matrix_coeffs are not
/// present.
pub colour_description_present_flag: bool,
/// Indicates the chromaticity coordinates of the source primaries as
/// specified in Table E.3 in terms of the CIE 1931 definition of x and y as
/// specified in ISO 11664-1.
pub colour_primaries: u32,
/// See table E.4 in the specification.
pub transfer_characteristics: u32,
/// Describes the matrix coefficients used in deriving luma and chroma
/// signals from the green, blue, and red, or Y, Z, and X primaries, as
/// specified in Table E.5.
pub matrix_coeffs: u32,
/// When true, specifies that chroma_sample_loc_type_top_field and
/// chroma_sample_loc_type_bottom_field are present. When false, specifies
/// that chroma_sample_loc_type_top_field and
/// chroma_sample_loc_type_bottom_field are not present.
pub chroma_loc_info_present_flag: bool,
/// See the specification for more details.
pub chroma_sample_loc_type_top_field: u32,
/// See the specification for more details.
pub chroma_sample_loc_type_bottom_field: u32,
/// When true, indicates that the value of all decoded chroma samples is
/// equal to 1 << ( BitDepthC − 1 ). When false, provides no indication of
/// decoded chroma sample values.
pub neutral_chroma_indication_flag: bool,
/// When true, indicates that the CVS conveys pictures that represent
/// fields, and specifies that a picture timing SEI message shall be present
/// in every access unit of the current CVS. When false, indicates that the
/// CVS conveys pictures that represent frames and that a picture timing SEI
/// message may or may not be present in any access unit of the current CVS.
pub field_seq_flag: bool,
/// When true, specifies that picture timing SEI messages are present for
/// every picture and include the pic_struct, source_scan_type and
/// duplicate_flag syntax elements. When false, specifies that the
/// pic_struct syntax element is not present in picture timing SEI messages.
pub frame_field_info_present_flag: bool,
/// When true, indicates that the default display window parameters follow
/// next in the VUI. When false, indicates that the default display window
/// parameters are not present.
pub default_display_window_flag: bool,
/// Specifies the samples of the pictures in the CVS that are within the
/// default display window, in terms of a rectangular region specified in
/// picture coordinates for display.
pub def_disp_win_left_offset: u32,
/// Specifies the samples of the pictures in the CVS that are within the
/// default display window, in terms of a rectangular region specified in
/// picture coordinates for display.
pub def_disp_win_right_offset: u32,
/// Specifies the samples of the pictures in the CVS that are within the
/// default display window, in terms of a rectangular region specified in
/// picture coordinates for display.
pub def_disp_win_top_offset: u32,
/// Specifies the samples of the pictures in the CVS that are within the
/// default display window, in terms of a rectangular region specified in
/// picture coordinates for display.
pub def_disp_win_bottom_offset: u32,
/// When set, specifies that vui_num_units_in_tick, vui_time_scale,
/// vui_poc_proportional_to_timing_flag and vui_hrd_parameters_present_flag
/// are present in the vui_parameters( ) syntax structure. When not set,
/// specifies that vui_num_units_in_tick, vui_time_scale,
/// vui_poc_proportional_to_timing_flag and vui_hrd_parameters_present_flag
/// are not present in the vui_parameters( ) syntax structure
pub timing_info_present_flag: bool,
/// The number of time units of a clock operating at the frequency
/// vui_time_scale Hz that corresponds to one increment (called a clock
/// tick) of a clock tick counter.
pub num_units_in_tick: u32,
/// Is the number of time units that pass in one second. For example, a time
/// coordinate system that measures time using a 27 MHz clock has a
/// vui_time_scale of 27 000 000.
pub time_scale: u32,
/// When set, indicates that the picture order count value for each picture
/// in the CVS that is not the first picture in the CVS, in decoding order,
/// is proportional to the output time of the picture relative to the output
/// time of the first picture in the CVS. When not set, indicates that the
/// picture order count value for each picture in the CVS that is not the
/// first picture in the CVS, in decoding order, may or may not be
/// proportional to the output time of the picture relative to the output
/// time of the first picture in the CVS.
pub poc_proportional_to_timing_flag: bool,
/// vui_num_ticks_poc_diff_one_minus1 plus 1 specifies the number of clock
/// ticks corresponding to a difference of picture order count values equal
/// to 1.
pub num_ticks_poc_diff_one_minus1: u32,
/// When set, specifies that the syntax structure hrd_parameters( ) is
/// present in the vui_parameters( ) syntax structure. When not set,
/// specifies that the syntax structure hrd_parameters( ) is not present in
/// the vui_parameters( ) syntax structure.
pub hrd_parameters_present_flag: bool,
/// The hrd_parameters() data.
pub hrd: HrdParams,
/// When set, specifies that the bitstream restriction parameters for the
/// CVS are present. When not set, specifies that the bitstream restriction
/// parameters for the CVS are not present.
pub bitstream_restriction_flag: bool,
/// When set, indicates that each PPS that is active in the CVS has the same
/// value of the syntax elements num_tile_columns_minus1,
/// num_tile_rows_minus1, uniform_spacing_flag, `column_width_minus1[ i ]`,
/// `row_height_minus1[ i ]` and loop_filter_across_tiles_enabled_flag, when
/// present. When not set, indicates that tiles syntax elements in different
/// PPSs may or may not have the same value
pub tiles_fixed_structure_flag: bool,
/// When not set, indicates that no sample outside the picture boundaries
/// and no sample at a fractional sample position for which the sample value
/// is derived using one or more samples outside the picture boundaries is
/// used for inter prediction of any sample. When set, indicates that one
/// or more samples outside the picture boundaries may be used in inter
/// prediction.
pub motion_vectors_over_pic_boundaries_flag: bool,
/// When set, indicates that all P and B slices (when present) that belong
/// to the same picture have an identical reference picture list 0 and that
/// all B slices (when present) that belong to the same picture have an
/// identical reference picture list 1.
pub restricted_ref_pic_lists_flag: bool,
/// When not equal to 0, establishes a bound on the maximum possible size of
/// distinct coded spatial segmentation regions in the pictures of the CVS.
pub min_spatial_segmentation_idc: u32,
/// Indicates a number of bytes not exceeded by the sum of the sizes of the
/// VCL NAL units associated with any coded picture in the CVS.
pub max_bytes_per_pic_denom: u32,
/// Indicates an upper bound for the number of coded bits of coding_unit( )
/// data for anycoding block in any picture of the CVS.
pub max_bits_per_min_cu_denom: u32,
/// Indicate the maximum absolute value of a decoded horizontal and vertical
/// motion vector component, respectively, in quarter luma sample units, for
/// all pictures in the CVS.
pub log2_max_mv_length_horizontal: u32,
/// Indicate the maximum absolute value of a decoded horizontal and vertical
/// motion vector component, respectively, in quarter luma sample units, for
/// all pictures in the CVS.
pub log2_max_mv_length_vertical: u32,
}
impl Default for VuiParams {
fn default() -> Self {
Self {
aspect_ratio_info_present_flag: Default::default(),
aspect_ratio_idc: Default::default(),
sar_width: Default::default(),
sar_height: Default::default(),
overscan_info_present_flag: Default::default(),
overscan_appropriate_flag: Default::default(),
video_signal_type_present_flag: Default::default(),
video_format: 5,
video_full_range_flag: Default::default(),
colour_description_present_flag: Default::default(),
colour_primaries: 2,
transfer_characteristics: 2,
matrix_coeffs: 2,
chroma_loc_info_present_flag: Default::default(),
chroma_sample_loc_type_top_field: Default::default(),
chroma_sample_loc_type_bottom_field: Default::default(),
neutral_chroma_indication_flag: Default::default(),
field_seq_flag: Default::default(),
frame_field_info_present_flag: Default::default(),
default_display_window_flag: Default::default(),
def_disp_win_left_offset: Default::default(),
def_disp_win_right_offset: Default::default(),
def_disp_win_top_offset: Default::default(),
def_disp_win_bottom_offset: Default::default(),
timing_info_present_flag: Default::default(),
num_units_in_tick: Default::default(),
time_scale: Default::default(),
poc_proportional_to_timing_flag: Default::default(),
num_ticks_poc_diff_one_minus1: Default::default(),
hrd_parameters_present_flag: Default::default(),
hrd: Default::default(),
bitstream_restriction_flag: Default::default(),
tiles_fixed_structure_flag: Default::default(),
motion_vectors_over_pic_boundaries_flag: true,
restricted_ref_pic_lists_flag: Default::default(),
min_spatial_segmentation_idc: Default::default(),
max_bytes_per_pic_denom: 2,
max_bits_per_min_cu_denom: 1,
log2_max_mv_length_horizontal: 15,
log2_max_mv_length_vertical: 15,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Parser {
active_vpses: BTreeMap<u8, Vps>,
active_spses: BTreeMap<u8, Sps>,
active_ppses: BTreeMap<u8, Pps>,
}
impl Parser {
/// Parse a VPS NALU.
pub fn parse_vps(&mut self, nalu: &Nalu) -> anyhow::Result<&Vps> {
if !matches!(nalu.header.type_, NaluType::VpsNut) {
return Err(anyhow!(
"Invalid NALU type, expected {:?}, got {:?}",
NaluType::VpsNut,
nalu.header.type_
));
}
let data = nalu.as_ref();
let header = &nalu.header;
let hdr_len = header.len();
// Skip the header
let mut r = NaluReader::new(&data[hdr_len..]);
let mut vps = Vps {
video_parameter_set_id: r.read_bits(4)?,
base_layer_internal_flag: r.read_bit()?,
base_layer_available_flag: r.read_bit()?,
max_layers_minus1: r.read_bits(6)?,
max_sub_layers_minus1: r.read_bits(3)?,
temporal_id_nesting_flag: r.read_bit()?,
..Default::default()
};
r.skip_bits(16)?; // vps_reserved_0xffff_16bits
let ptl = &mut vps.profile_tier_level;
Self::parse_profile_tier_level(ptl, &mut r, true, vps.max_sub_layers_minus1)?;
vps.sub_layer_ordering_info_present_flag = r.read_bit()?;
let start = if vps.sub_layer_ordering_info_present_flag {
0
} else {
vps.max_sub_layers_minus1
} as usize;
for i in start..=usize::from(vps.max_sub_layers_minus1) {
vps.max_dec_pic_buffering_minus1[i] = r.read_ue_max(15)?;
vps.max_num_reorder_pics[i] = r.read_ue_max(vps.max_dec_pic_buffering_minus1[i])?;
vps.max_latency_increase_plus1[i] = r.read_ue()?;
if i > 0 {
if vps.max_dec_pic_buffering_minus1[i] < vps.max_dec_pic_buffering_minus1[i - 1] {
return Err(anyhow!(
"Invalid max_dec_pic_buffering_minus1[{}]: {}",
i,
vps.max_dec_pic_buffering_minus1[i]
));
}
if vps.max_num_reorder_pics[i] < vps.max_num_reorder_pics[i - 1] {
return Err(anyhow!(
"Invalid max_num_reorder_pics[{}]: {}",
i,
vps.max_num_reorder_pics[i]
));
}
}
}
// vps_sub_layer_ordering_info_present_flag equal to 0 specifies that
// the values of vps_max_dec_pic_buffering_minus1[
// vps_max_sub_layers_minus1 ], vps_max_num_reorder_pics[ vps_max_sub_
// layers_minus1 ] and vps_max_latency_increase_plus1[
// vps_max_sub_layers_minus1 ] apply to all sub-layers
if !vps.sub_layer_ordering_info_present_flag {
let max_num_sublayers = usize::from(vps.max_sub_layers_minus1);
for i in 0..max_num_sublayers {
vps.max_dec_pic_buffering_minus1[i] =
vps.max_dec_pic_buffering_minus1[max_num_sublayers];
vps.max_num_reorder_pics[i] = vps.max_num_reorder_pics[max_num_sublayers];
vps.max_latency_increase_plus1[i] =
vps.max_latency_increase_plus1[max_num_sublayers];
}
}
vps.max_layer_id = r.read_bits(6)?;
if vps.max_layer_id > 62 {
return Err(anyhow!("Invalid max_layer_id {}", vps.max_layer_id));
}
vps.num_layer_sets_minus1 = r.read_ue_max(1023)?;
for _ in 1..=vps.num_layer_sets_minus1 {
for _ in 0..=vps.max_layer_id {
// Skip layer_id_included_flag[i][j] for now.
r.skip_bits(1)?;
}
}
vps.timing_info_present_flag = r.read_bit()?;
if vps.timing_info_present_flag {
vps.num_units_in_tick = r.read_bits::<u32>(31)? << 1;
vps.num_units_in_tick |= r.read_bits::<u32>(1)?;
vps.time_scale = r.read_bits::<u32>(31)? << 1;
vps.time_scale |= r.read_bits::<u32>(1)?;
vps.poc_proportional_to_timing_flag = r.read_bit()?;
if vps.poc_proportional_to_timing_flag {
vps.num_ticks_poc_diff_one_minus1 = r.read_ue()?;
}
vps.num_hrd_parameters = r.read_ue()?;
for i in 0..vps.num_hrd_parameters as usize {
vps.hrd_layer_set_idx.push(r.read_ue()?);
if i > 0 {
vps.cprms_present_flag.push(r.read_bit()?);
}
let mut hrd = HrdParams::default();
Self::parse_hrd_parameters(
vps.cprms_present_flag[i],
vps.max_sub_layers_minus1,
&mut hrd,
&mut r,
)?;
vps.hrd_parameters.push(hrd);
}
}
vps.extension_flag = r.read_bit()?;
let key = vps.video_parameter_set_id;
self.active_vpses.insert(key, vps);
if self.active_spses.keys().len() > MAX_VPS_COUNT {
return Err(anyhow!(
"Broken data: Number of active SPSs > MAX_SPS_COUNT"
));
}
Ok(self.get_vps(key).unwrap())
}
fn parse_profile_tier_level(
ptl: &mut ProfileTierLevel,
r: &mut NaluReader,
profile_present_flag: bool,
sps_max_sub_layers_minus_1: u8,
) -> anyhow::Result<()> {
if profile_present_flag {
ptl.general_profile_space = r.read_bits(2)?;
ptl.general_tier_flag = r.read_bit()?;
ptl.general_profile_idc = r.read_bits(5)?;
for i in 0..32 {
ptl.general_profile_compatibility_flag[i] = r.read_bit()?;
}
ptl.general_progressive_source_flag = r.read_bit()?;
ptl.general_interlaced_source_flag = r.read_bit()?;
ptl.general_non_packed_constraint_flag = r.read_bit()?;
ptl.general_frame_only_constraint_flag = r.read_bit()?;
if ptl.general_profile_idc == 4
|| ptl.general_profile_compatibility_flag[4]
|| ptl.general_profile_idc == 5
|| ptl.general_profile_compatibility_flag[5]
|| ptl.general_profile_idc == 6
|| ptl.general_profile_compatibility_flag[6]
|| ptl.general_profile_idc == 7
|| ptl.general_profile_compatibility_flag[7]
|| ptl.general_profile_idc == 8
|| ptl.general_profile_compatibility_flag[8]
|| ptl.general_profile_idc == 9
|| ptl.general_profile_compatibility_flag[9]
|| ptl.general_profile_idc == 10
|| ptl.general_profile_compatibility_flag[10]
|| ptl.general_profile_idc == 11
|| ptl.general_profile_compatibility_flag[11]
{
ptl.general_max_12bit_constraint_flag = r.read_bit()?;
ptl.general_max_10bit_constraint_flag = r.read_bit()?;
ptl.general_max_8bit_constraint_flag = r.read_bit()?;
ptl.general_max_422chroma_constraint_flag = r.read_bit()?;
ptl.general_max_420chroma_constraint_flag = r.read_bit()?;
ptl.general_max_monochrome_constraint_flag = r.read_bit()?;
ptl.general_intra_constraint_flag = r.read_bit()?;
ptl.general_one_picture_only_constraint_flag = r.read_bit()?;
ptl.general_lower_bit_rate_constraint_flag = r.read_bit()?;
if ptl.general_profile_idc == 5
|| ptl.general_profile_compatibility_flag[5]
|| ptl.general_profile_idc == 9
|| ptl.general_profile_compatibility_flag[9]
|| ptl.general_profile_idc == 10
|| ptl.general_profile_compatibility_flag[10]
|| ptl.general_profile_idc == 11
|| ptl.general_profile_compatibility_flag[11]
{
ptl.general_max_14bit_constraint_flag = r.read_bit()?;
// Skip general_reserved_zero_33bits
r.skip_bits(31)?;
r.skip_bits(2)?;
} else {
// Skip general_reserved_zero_34bits
r.skip_bits(31)?;
r.skip_bits(3)?;
}
} else if ptl.general_profile_idc == 2 || ptl.general_profile_compatibility_flag[2] {
// Skip general_reserved_zero_7bits
r.skip_bits(7)?;
ptl.general_one_picture_only_constraint_flag = r.read_bit()?;
// Skip general_reserved_zero_35bits
r.skip_bits(31)?;
r.skip_bits(4)?;
} else {
r.skip_bits(31)?;
r.skip_bits(12)?;
}
if ptl.general_profile_idc == 1
|| ptl.general_profile_compatibility_flag[1]
|| ptl.general_profile_idc == 2
|| ptl.general_profile_compatibility_flag[2]
|| ptl.general_profile_idc == 3
|| ptl.general_profile_compatibility_flag[3]
|| ptl.general_profile_idc == 4
|| ptl.general_profile_compatibility_flag[4]
|| ptl.general_profile_idc == 5
|| ptl.general_profile_compatibility_flag[5]
|| ptl.general_profile_idc == 9
|| ptl.general_profile_compatibility_flag[9]
|| ptl.general_profile_idc == 11
|| ptl.general_profile_compatibility_flag[11]
{
ptl.general_inbld_flag = r.read_bit()?;
} else {
r.skip_bits(1)?;
}
}
let level: u8 = r.read_bits(8)?;
ptl.general_level_idc =
Level::n(level).with_context(|| format!("Unsupported level {}", level))?;
for i in 0..sps_max_sub_layers_minus_1 as usize {
ptl.sub_layer_profile_present_flag[i] = r.read_bit()?;
ptl.sub_layer_level_present_flag[i] = r.read_bit()?;
}
if sps_max_sub_layers_minus_1 > 0 {
for _ in sps_max_sub_layers_minus_1..8 {
r.skip_bits(2)?;
}
}
for i in 0..sps_max_sub_layers_minus_1 as usize {
if ptl.sub_layer_level_present_flag[i] {
ptl.sub_layer_profile_space[i] = r.read_bits(2)?;
ptl.sub_layer_tier_flag[i] = r.read_bit()?;
ptl.sub_layer_profile_idc[i] = r.read_bits(5)?;
for j in 0..32 {
ptl.sub_layer_profile_compatibility_flag[i][j] = r.read_bit()?;
}
ptl.sub_layer_progressive_source_flag[i] = r.read_bit()?;
ptl.sub_layer_interlaced_source_flag[i] = r.read_bit()?;
ptl.sub_layer_non_packed_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_frame_only_constraint_flag[i] = r.read_bit()?;
if ptl.sub_layer_profile_idc[i] == 4
|| ptl.sub_layer_profile_compatibility_flag[i][4]
|| ptl.sub_layer_profile_idc[i] == 5
|| ptl.sub_layer_profile_compatibility_flag[i][5]
|| ptl.sub_layer_profile_idc[i] == 6
|| ptl.sub_layer_profile_compatibility_flag[i][6]
|| ptl.sub_layer_profile_idc[i] == 7
|| ptl.sub_layer_profile_compatibility_flag[i][7]
|| ptl.sub_layer_profile_idc[i] == 8
|| ptl.sub_layer_profile_compatibility_flag[i][8]
|| ptl.sub_layer_profile_idc[i] == 9
|| ptl.sub_layer_profile_compatibility_flag[i][9]
|| ptl.sub_layer_profile_idc[i] == 10
|| ptl.sub_layer_profile_compatibility_flag[i][10]
|| ptl.sub_layer_profile_idc[i] == 11
|| ptl.sub_layer_profile_compatibility_flag[i][11]
{
ptl.sub_layer_max_12bit_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_max_10bit_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_max_8bit_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_max_422chroma_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_max_420chroma_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_max_monochrome_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_intra_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_one_picture_only_constraint_flag[i] = r.read_bit()?;
ptl.sub_layer_lower_bit_rate_constraint_flag[i] = r.read_bit()?;
if ptl.sub_layer_profile_idc[i] == 5
|| ptl.sub_layer_profile_compatibility_flag[i][5]
|| ptl.sub_layer_profile_idc[i] == 9
|| ptl.sub_layer_profile_compatibility_flag[i][9]
|| ptl.sub_layer_profile_idc[i] == 10
|| ptl.sub_layer_profile_compatibility_flag[i][10]
|| ptl.sub_layer_profile_idc[i] == 11
|| ptl.sub_layer_profile_compatibility_flag[i][11]
{
ptl.sub_layer_max_14bit_constraint_flag[i] = r.read_bit()?;
r.skip_bits(33)?;
} else {
r.skip_bits(34)?;
}
} else if ptl.sub_layer_profile_idc[i] == 2
|| ptl.sub_layer_profile_compatibility_flag[i][2]
{
r.skip_bits(7)?;
ptl.sub_layer_one_picture_only_constraint_flag[i] = r.read_bit()?;
r.skip_bits(35)?;
} else {
r.skip_bits(43)?;
}
if ptl.sub_layer_profile_idc[i] == 1
|| ptl.sub_layer_profile_compatibility_flag[i][1]
|| ptl.sub_layer_profile_idc[i] == 2
|| ptl.sub_layer_profile_compatibility_flag[i][2]
|| ptl.sub_layer_profile_idc[i] == 3
|| ptl.sub_layer_profile_compatibility_flag[i][3]
|| ptl.sub_layer_profile_idc[i] == 4
|| ptl.sub_layer_profile_compatibility_flag[i][4]
|| ptl.sub_layer_profile_idc[i] == 5
|| ptl.sub_layer_profile_compatibility_flag[i][5]
|| ptl.sub_layer_profile_idc[i] == 9
|| ptl.sub_layer_profile_compatibility_flag[i][9]
|| ptl.sub_layer_profile_idc[i] == 11
|| ptl.sub_layer_profile_compatibility_flag[i][11]
{
ptl.sub_layer_inbld_flag[i] = r.read_bit()?;
} else {
r.skip_bits(1)?;
}
if ptl.sub_layer_level_present_flag[i] {
let level: u8 = r.read_bits(8)?;
ptl.sub_layer_level_idc[i] =
Level::n(level).with_context(|| format!("Unsupported level {}", level))?;
}
}
}
Ok(())
}
fn fill_default_scaling_list(sl: &mut ScalingLists, size_id: i32, matrix_id: i32) {
if size_id == 0 {
sl.scaling_list_4x4[matrix_id as usize] = DEFAULT_SCALING_LIST_0;
return;
}
let dst = match size_id {
1 => &mut sl.scaling_list_8x8[matrix_id as usize],
2 => &mut sl.scaling_list_16x16[matrix_id as usize],
3 => &mut sl.scaling_list_32x32[matrix_id as usize],
_ => panic!("Invalid size_id {}", size_id),
};
let src = if matrix_id < 3 {
&DEFAULT_SCALING_LIST_1
} else if matrix_id <= 5 {
&DEFAULT_SCALING_LIST_2
} else {
panic!("Invalid matrix_id {}", matrix_id);
};
*dst = *src;
// When `scaling_list_pred_mode_flag[ sizeId ]`[ matrixId ] is equal to
// 0, scaling_list_pred_matrix_id_ `delta[ sizeId ]`[ matrixId ] is equal
// to 0 and sizeId is greater than 1, the value of
// scaling_list_dc_coef_minus8[ sizeId − 2 `][ matrixId ]` is inferred to
// be equal to 8.
//
// Since we are using a slightly different layout here, with two
// different field names (i.e. 16x16, and 32x32), we must differentiate
// between size_id == 2 or size_id == 3.
if size_id == 2 {
sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize] = 8;
} else if size_id == 3 {
sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize] = 8;
}
}
fn parse_scaling_list_data(sl: &mut ScalingLists, r: &mut NaluReader) -> anyhow::Result<()> {
// 7.4.5
for size_id in 0..4 {
let mut matrix_id = 0;
while matrix_id < 6 {
let scaling_list_pred_mode_flag = r.read_bit()?;
// If `scaling_list_pred_matrix_id_delta[ sizeId ]`[ matrixId ] is
// equal to 0, the scaling list is inferred from the default
// scaling list `ScalingList[ sizeId ]`[ matrixId `][ i ]` as specified
// in Table 7-5 and Table 7-6 for i = 0..Min( 63, ( 1 << ( 4 + (
// sizeId << 1 ) ) ) − 1 ).
if !scaling_list_pred_mode_flag {
let scaling_list_pred_matrix_id_delta: u32 = r.read_ue()?;
if scaling_list_pred_matrix_id_delta == 0 {
Self::fill_default_scaling_list(sl, size_id, matrix_id);
} else {
// Equation 7-42
let factor = if size_id == 3 { 3 } else { 1 };
let ref_matrix_id =
matrix_id as u32 - scaling_list_pred_matrix_id_delta * factor;
if size_id == 0 {
sl.scaling_list_4x4[matrix_id as usize] =
sl.scaling_list_4x4[ref_matrix_id as usize];
} else {
let src = match size_id {
1 => sl.scaling_list_8x8[ref_matrix_id as usize],
2 => sl.scaling_list_16x16[ref_matrix_id as usize],
3 => sl.scaling_list_32x32[ref_matrix_id as usize],
_ => return Err(anyhow!("Invalid size_id {}", size_id)),
};
let dst = match size_id {
1 => &mut sl.scaling_list_8x8[matrix_id as usize],
2 => &mut sl.scaling_list_16x16[matrix_id as usize],
3 => &mut sl.scaling_list_32x32[matrix_id as usize],
_ => return Err(anyhow!("Invalid size_id {}", size_id)),
};
*dst = src;
if size_id == 2 {
sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize] =
sl.scaling_list_dc_coef_minus8_16x16[ref_matrix_id as usize];
} else if size_id == 3 {
sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize] =
sl.scaling_list_dc_coef_minus8_32x32[ref_matrix_id as usize];
}
}
}
} else {
let mut next_coef = 8i32;
let coef_num = std::cmp::min(64, 1 << (4 + (size_id << 1)));
if size_id > 1 {
if size_id == 2 {
sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize] =
r.read_se_bounded(-7, 247)?;
next_coef =
i32::from(sl.scaling_list_dc_coef_minus8_16x16[matrix_id as usize])
+ 8;
} else if size_id == 3 {
sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize] =
r.read_se_bounded(-7, 247)?;
next_coef =
i32::from(sl.scaling_list_dc_coef_minus8_32x32[matrix_id as usize])
+ 8;
}
}
for i in 0..coef_num as usize {
let scaling_list_delta_coef: i32 = r.read_se_bounded(-128, 127)?;
next_coef = (next_coef + scaling_list_delta_coef + 256) % 256;
match size_id {
0 => sl.scaling_list_4x4[matrix_id as usize][i] = next_coef as _,
1 => sl.scaling_list_8x8[matrix_id as usize][i] = next_coef as _,
2 => sl.scaling_list_16x16[matrix_id as usize][i] = next_coef as _,
3 => sl.scaling_list_32x32[matrix_id as usize][i] = next_coef as _,
_ => return Err(anyhow!("Invalid size_id {}", size_id)),
}
}
}
let step = if size_id == 3 { 3 } else { 1 };
matrix_id += step;
}
}
Ok(())
}
fn parse_short_term_ref_pic_set(
sps: &Sps,
st: &mut ShortTermRefPicSet,
r: &mut NaluReader,
st_rps_idx: u8,
) -> anyhow::Result<()> {
if st_rps_idx != 0 {
st.inter_ref_pic_set_prediction_flag = r.read_bit()?;
}
// (7-59)
if st.inter_ref_pic_set_prediction_flag {
if st_rps_idx == sps.num_short_term_ref_pic_sets {
st.delta_idx_minus1 = r.read_ue_max(st_rps_idx as u32 - 1)?;
}
st.delta_rps_sign = r.read_bit()?;
// The value of abs_delta_rps_minus1 shall be in the range of 0 to
// 2^15 − 1, inclusive.
st.abs_delta_rps_minus1 = r.read_ue_max(32767)?;
let ref_rps_idx = st_rps_idx - (st.delta_idx_minus1 + 1);
let delta_rps =
(1 - 2 * st.delta_rps_sign as i32) * (st.abs_delta_rps_minus1 as i32 + 1);
let ref_st = sps
.short_term_ref_pic_set
.get(usize::from(ref_rps_idx))
.ok_or(anyhow!("Invalid ref_rps_idx"))?;
let mut used_by_curr_pic_flag = [false; 64];
// 7.4.8 - defaults to 1 if not present
let mut use_delta_flag = [true; 64];
for j in 0..=ref_st.num_delta_pocs as usize {
used_by_curr_pic_flag[j] = r.read_bit()?;
if !used_by_curr_pic_flag[j] {
use_delta_flag[j] = r.read_bit()?;
}
}
// (7-61)
let mut i = 0;
// Ranges are [a,b[, but the real loop is [b, a], i.e.
// [num_positive_pics - 1, 0]. Use ..= so that b is included when
// rev() is called.
for j in (0..=isize::from(ref_st.num_positive_pics) - 1)
.rev()
.take_while(|j| *j >= 0)
.map(|j| j as usize)
{
let d_poc = ref_st.delta_poc_s1[j] + delta_rps;
if d_poc < 0 && use_delta_flag[usize::from(ref_st.num_negative_pics) + j] {
st.delta_poc_s0[i] = d_poc;
st.used_by_curr_pic_s0[i] =
used_by_curr_pic_flag[usize::from(ref_st.num_negative_pics) + j];
i += 1;
}
}
if delta_rps < 0 && use_delta_flag[ref_st.num_delta_pocs as usize] {
st.delta_poc_s0[i] = delta_rps;
st.used_by_curr_pic_s0[i] = used_by_curr_pic_flag[ref_st.num_delta_pocs as usize];
i += 1;
}
// Let's *not* change the original algorithm in any way.
#[allow(clippy::needless_range_loop)]
for j in 0..ref_st.num_negative_pics as usize {
let d_poc = ref_st.delta_poc_s0[j] + delta_rps;
if d_poc < 0 && use_delta_flag[j] {
st.delta_poc_s0[i] = d_poc;
st.used_by_curr_pic_s0[i] = used_by_curr_pic_flag[j];
i += 1;
}
}
st.num_negative_pics = i as u8;
// (7-62)
let mut i = 0;
// Ranges are [a,b[, but the real loop is [b, a], i.e.
// [num_negative_pics - 1, 0]. Use ..= so that b is included when
// rev() is called.
for j in (0..=isize::from(ref_st.num_negative_pics) - 1)
.rev()
.take_while(|j| *j >= 0)
.map(|j| j as usize)
{
let d_poc = ref_st.delta_poc_s0[j] + delta_rps;
if d_poc > 0 && use_delta_flag[j] {
st.delta_poc_s1[i] = d_poc;
st.used_by_curr_pic_s1[i] = used_by_curr_pic_flag[j];
i += 1;
}
}
if delta_rps > 0 && use_delta_flag[ref_st.num_delta_pocs as usize] {
st.delta_poc_s1[i] = delta_rps;
st.used_by_curr_pic_s1[i] = used_by_curr_pic_flag[ref_st.num_delta_pocs as usize];
i += 1;
}
for j in 0..usize::from(ref_st.num_positive_pics) {
let d_poc = ref_st.delta_poc_s1[j] + delta_rps;
if d_poc > 0 && use_delta_flag[ref_st.num_negative_pics as usize + j] {
st.delta_poc_s1[i] = d_poc;
st.used_by_curr_pic_s1[i] =
used_by_curr_pic_flag[ref_st.num_negative_pics as usize + j];
i += 1;
}
}
st.num_positive_pics = i as u8;
} else {
st.num_negative_pics = r.read_ue_max(u32::from(
sps.max_dec_pic_buffering_minus1[usize::from(sps.max_sub_layers_minus1)],
))?;
st.num_positive_pics = r.read_ue_max(u32::from(
sps.max_dec_pic_buffering_minus1[usize::from(sps.max_sub_layers_minus1)]
- st.num_negative_pics,
))?;
for i in 0..usize::from(st.num_negative_pics) {
let delta_poc_s0_minus1: u32 = r.read_ue_max(32767)?;
if i == 0 {
st.delta_poc_s0[i] = -(delta_poc_s0_minus1 as i32 + 1);
} else {
st.delta_poc_s0[i] = st.delta_poc_s0[i - 1] - (delta_poc_s0_minus1 as i32 + 1);
}
st.used_by_curr_pic_s0[i] = r.read_bit()?;
}
for i in 0..usize::from(st.num_positive_pics) {
let delta_poc_s1_minus1: u32 = r.read_ue_max(32767)?;
if i == 0 {
st.delta_poc_s1[i] = delta_poc_s1_minus1 as i32 + 1;
} else {
st.delta_poc_s1[i] = st.delta_poc_s1[i - 1] + (delta_poc_s1_minus1 as i32 + 1);
}
st.used_by_curr_pic_s1[i] = r.read_bit()?;
}
}
st.num_delta_pocs = u32::from(st.num_negative_pics + st.num_positive_pics);
Ok(())
}
fn parse_sublayer_hrd_parameters(
h: &mut SublayerHrdParameters,
cpb_cnt: u32,
sub_pic_hrd_params_present_flag: bool,
r: &mut NaluReader,
) -> anyhow::Result<()> {
for i in 0..cpb_cnt as usize {
h.bit_rate_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?;
h.cpb_size_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?;
if sub_pic_hrd_params_present_flag {
h.cpb_size_du_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?;
h.bit_rate_du_value_minus1[i] = r.read_ue_max((2u64.pow(32) - 2) as u32)?;
}
h.cbr_flag[i] = r.read_bit()?;
}
Ok(())
}
fn parse_hrd_parameters(
common_inf_present_flag: bool,
max_num_sublayers_minus1: u8,
hrd: &mut HrdParams,
r: &mut NaluReader,
) -> anyhow::Result<()> {
if common_inf_present_flag {
hrd.nal_hrd_parameters_present_flag = r.read_bit()?;
hrd.vcl_hrd_parameters_present_flag = r.read_bit()?;
if hrd.nal_hrd_parameters_present_flag || hrd.vcl_hrd_parameters_present_flag {
hrd.sub_pic_hrd_params_present_flag = r.read_bit()?;
if hrd.sub_pic_hrd_params_present_flag {
hrd.tick_divisor_minus2 = r.read_bits(8)?;
hrd.du_cpb_removal_delay_increment_length_minus1 = r.read_bits(5)?;
hrd.sub_pic_cpb_params_in_pic_timing_sei_flag = r.read_bit()?;
hrd.dpb_output_delay_du_length_minus1 = r.read_bits(5)?;
}
hrd.bit_rate_scale = r.read_bits(4)?;
hrd.cpb_size_scale = r.read_bits(4)?;
if hrd.sub_pic_hrd_params_present_flag {
hrd.cpb_size_du_scale = r.read_bits(4)?;
}
hrd.initial_cpb_removal_delay_length_minus1 = r.read_bits(5)?;
hrd.au_cpb_removal_delay_length_minus1 = r.read_bits(5)?;
hrd.dpb_output_delay_length_minus1 = r.read_bits(5)?;
}
}
for i in 0..=max_num_sublayers_minus1 as usize {
hrd.fixed_pic_rate_general_flag[i] = r.read_bit()?;
if !hrd.fixed_pic_rate_general_flag[i] {
hrd.fixed_pic_rate_within_cvs_flag[i] = r.read_bit()?;
}
if hrd.fixed_pic_rate_within_cvs_flag[i] {
hrd.elemental_duration_in_tc_minus1[i] = r.read_ue_max(2047)?;
} else {
hrd.low_delay_hrd_flag[i] = r.read_bit()?;
}
if !hrd.low_delay_hrd_flag[i] {
hrd.cpb_cnt_minus1[i] = r.read_ue_max(31)?;
}
if hrd.nal_hrd_parameters_present_flag {
Self::parse_sublayer_hrd_parameters(
&mut hrd.nal_hrd[i],
hrd.cpb_cnt_minus1[i] + 1,
hrd.sub_pic_hrd_params_present_flag,
r,
)?;
}
if hrd.vcl_hrd_parameters_present_flag {
Self::parse_sublayer_hrd_parameters(
&mut hrd.vcl_hrd[i],
hrd.cpb_cnt_minus1[i] + 1,
hrd.sub_pic_hrd_params_present_flag,
r,
)?;
}
}
Ok(())
}
fn parse_vui_parameters(sps: &mut Sps, r: &mut NaluReader) -> anyhow::Result<()> {
let vui = &mut sps.vui_parameters;
vui.aspect_ratio_info_present_flag = r.read_bit()?;
if vui.aspect_ratio_info_present_flag {
vui.aspect_ratio_idc = r.read_bits(8)?;
const EXTENDED_SAR: u32 = 255;
if vui.aspect_ratio_idc == EXTENDED_SAR {
vui.sar_width = r.read_bits(16)?;
vui.sar_height = r.read_bits(16)?;
}
}
vui.overscan_info_present_flag = r.read_bit()?;
if vui.overscan_info_present_flag {
vui.overscan_appropriate_flag = r.read_bit()?;
}
vui.video_signal_type_present_flag = r.read_bit()?;
if vui.video_signal_type_present_flag {
vui.video_format = r.read_bits(3)?;
vui.video_full_range_flag = r.read_bit()?;
vui.colour_description_present_flag = r.read_bit()?;
if vui.colour_description_present_flag {
vui.colour_primaries = r.read_bits(8)?;
vui.transfer_characteristics = r.read_bits(8)?;
vui.matrix_coeffs = r.read_bits(8)?;
}
}
vui.chroma_loc_info_present_flag = r.read_bit()?;
if vui.chroma_loc_info_present_flag {
vui.chroma_sample_loc_type_top_field = r.read_ue_max(5)?;
vui.chroma_sample_loc_type_bottom_field = r.read_ue_max(5)?;
}
vui.neutral_chroma_indication_flag = r.read_bit()?;
vui.field_seq_flag = r.read_bit()?;
vui.frame_field_info_present_flag = r.read_bit()?;
vui.default_display_window_flag = r.read_bit()?;
if vui.default_display_window_flag {
vui.def_disp_win_left_offset = r.read_ue()?;
vui.def_disp_win_right_offset = r.read_ue()?;
vui.def_disp_win_top_offset = r.read_ue()?;
vui.def_disp_win_bottom_offset = r.read_ue()?;
}
vui.timing_info_present_flag = r.read_bit()?;
if vui.timing_info_present_flag {
vui.num_units_in_tick = r.read_bits::<u32>(31)? << 1;
vui.num_units_in_tick |= r.read_bits::<u32>(1)?;
if vui.num_units_in_tick == 0 {
log::warn!(
"Incompliant value for num_units_in_tick {}",
vui.num_units_in_tick
);
}
vui.time_scale = r.read_bits::<u32>(31)? << 1;
vui.time_scale |= r.read_bits::<u32>(1)?;
if vui.time_scale == 0 {
log::warn!("Incompliant value for time_scale {}", vui.time_scale);
}
vui.poc_proportional_to_timing_flag = r.read_bit()?;
if vui.poc_proportional_to_timing_flag {
vui.num_ticks_poc_diff_one_minus1 = r.read_ue_max((2u64.pow(32) - 2) as u32)?;
}
vui.hrd_parameters_present_flag = r.read_bit()?;
if vui.hrd_parameters_present_flag {
let sps_max_sub_layers_minus1 = sps.max_sub_layers_minus1;
Self::parse_hrd_parameters(true, sps_max_sub_layers_minus1, &mut vui.hrd, r)?;
}
}
vui.bitstream_restriction_flag = r.read_bit()?;
if vui.bitstream_restriction_flag {
vui.tiles_fixed_structure_flag = r.read_bit()?;
vui.motion_vectors_over_pic_boundaries_flag = r.read_bit()?;
vui.restricted_ref_pic_lists_flag = r.read_bit()?;
vui.min_spatial_segmentation_idc = r.read_ue_max(4095)?;
vui.max_bytes_per_pic_denom = r.read_ue()?;
vui.max_bits_per_min_cu_denom = r.read_ue()?;
vui.log2_max_mv_length_horizontal = r.read_ue_max(16)?;
vui.log2_max_mv_length_vertical = r.read_ue_max(15)?;
}
Ok(())
}
fn parse_sps_scc_extension(sps: &mut Sps, r: &mut NaluReader) -> anyhow::Result<()> {
let scc = &mut sps.scc_extension;
scc.curr_pic_ref_enabled_flag = r.read_bit()?;
scc.palette_mode_enabled_flag = r.read_bit()?;
if scc.palette_mode_enabled_flag {
scc.palette_max_size = r.read_ue_max(64)?;
scc.delta_palette_max_predictor_size =
r.read_ue_max(128 - u32::from(scc.palette_max_size))?;
scc.palette_predictor_initializers_present_flag = r.read_bit()?;
if scc.palette_predictor_initializers_present_flag {
let max =
u32::from(scc.palette_max_size + scc.delta_palette_max_predictor_size - 1);
scc.num_palette_predictor_initializer_minus1 = r.read_ue_max(max)?;
let num_comps = if sps.chroma_format_idc == 0 { 1 } else { 3 };
for comp in 0..num_comps {
for i in 0..=usize::from(scc.num_palette_predictor_initializer_minus1) {
let num_bits = if comp == 0 {
sps.bit_depth_luma_minus8 + 8
} else {
sps.bit_depth_chroma_minus8 + 8
};
scc.palette_predictor_initializer[comp][i] =
r.read_bits(usize::from(num_bits))?;
}
}
}
}
scc.motion_vector_resolution_control_idc = r.read_bits(2)?;
scc.intra_boundary_filtering_disabled_flag = r.read_bit()?;
Ok(())
}
fn parse_sps_range_extension(sps: &mut Sps, r: &mut NaluReader) -> anyhow::Result<()> {
let ext = &mut sps.range_extension;
ext.transform_skip_rotation_enabled_flag = r.read_bit()?;
ext.transform_skip_context_enabled_flag = r.read_bit()?;
ext.implicit_rdpcm_enabled_flag = r.read_bit()?;
ext.explicit_rdpcm_enabled_flag = r.read_bit()?;
ext.extended_precision_processing_flag = r.read_bit()?;
ext.intra_smoothing_disabled_flag = r.read_bit()?;
ext.high_precision_offsets_enabled_flag = r.read_bit()?;
ext.persistent_rice_adaptation_enabled_flag = r.read_bit()?;
ext.cabac_bypass_alignment_enabled_flag = r.read_bit()?;
Ok(())
}
/// Parse a SPS NALU.
pub fn parse_sps(&mut self, nalu: &Nalu) -> anyhow::Result<&Sps> {
if !matches!(nalu.header.type_, NaluType::SpsNut) {
return Err(anyhow!(
"Invalid NALU type, expected {:?}, got {:?}",
NaluType::SpsNut,
nalu.header.type_
));
}
let data = nalu.as_ref();
let header = &nalu.header;
let hdr_len = header.len();
// Skip the header
let mut r = NaluReader::new(&data[hdr_len..]);
let mut sps = Sps {
video_parameter_set_id: r.read_bits(4)?,
max_sub_layers_minus1: r.read_bits(3)?,
temporal_id_nesting_flag: r.read_bit()?,
..Default::default()
};
Self::parse_profile_tier_level(
&mut sps.profile_tier_level,
&mut r,
true,
sps.max_sub_layers_minus1,
)?;
sps.seq_parameter_set_id = r.read_ue_max(MAX_SPS_COUNT as u32 - 1)?;
sps.chroma_format_idc = r.read_ue_max(3)?;
if sps.chroma_format_idc == 3 {
sps.separate_colour_plane_flag = r.read_bit()?;
}
sps.chroma_array_type = if sps.separate_colour_plane_flag {
0
} else {
sps.chroma_format_idc
};
sps.pic_width_in_luma_samples = r.read_ue_bounded(1, 16888)?;
sps.pic_height_in_luma_samples = r.read_ue_bounded(1, 16888)?;
sps.conformance_window_flag = r.read_bit()?;
if sps.conformance_window_flag {
sps.conf_win_left_offset = r.read_ue()?;
sps.conf_win_right_offset = r.read_ue()?;
sps.conf_win_top_offset = r.read_ue()?;
sps.conf_win_bottom_offset = r.read_ue()?;
}
sps.bit_depth_luma_minus8 = r.read_ue_max(6)?;
sps.bit_depth_chroma_minus8 = r.read_ue_max(6)?;
sps.log2_max_pic_order_cnt_lsb_minus4 = r.read_ue_max(12)?;
sps.sub_layer_ordering_info_present_flag = r.read_bit()?;
{
let i = if sps.sub_layer_ordering_info_present_flag {
0
} else {
sps.max_sub_layers_minus1
};
for j in i..=sps.max_sub_layers_minus1 {
sps.max_dec_pic_buffering_minus1[j as usize] = r.read_ue_max(16)?;
sps.max_num_reorder_pics[j as usize] =
r.read_ue_max(sps.max_dec_pic_buffering_minus1[j as usize] as _)?;
sps.max_latency_increase_plus1[j as usize] = r.read_ue_max(u32::MAX - 1)?;
}
}
sps.log2_min_luma_coding_block_size_minus3 = r.read_ue_max(3)?;
sps.log2_diff_max_min_luma_coding_block_size = r.read_ue_max(6)?;
sps.log2_min_luma_transform_block_size_minus2 = r.read_ue_max(3)?;
sps.log2_diff_max_min_luma_transform_block_size = r.read_ue_max(3)?;
// (7-10)
sps.min_cb_log2_size_y = u32::from(sps.log2_min_luma_coding_block_size_minus3 + 3);
// (7-11)
sps.ctb_log2_size_y =
sps.min_cb_log2_size_y + u32::from(sps.log2_diff_max_min_luma_coding_block_size);
// (7-12)
sps.ctb_size_y = 1 << sps.ctb_log2_size_y;
// (7-17)
sps.pic_height_in_ctbs_y =
(sps.pic_height_in_luma_samples as f64 / sps.ctb_size_y as f64).ceil() as u32;
// (7-15)
sps.pic_width_in_ctbs_y =
(sps.pic_width_in_luma_samples as f64 / sps.ctb_size_y as f64).ceil() as u32;
sps.max_tb_log2_size_y = u32::from(
sps.log2_min_luma_transform_block_size_minus2
+ 2
+ sps.log2_diff_max_min_luma_transform_block_size,
);
sps.pic_size_in_samples_y =
u32::from(sps.pic_width_in_luma_samples) * u32::from(sps.pic_height_in_luma_samples);
if sps.max_tb_log2_size_y > std::cmp::min(sps.ctb_log2_size_y, 5) {
return Err(anyhow!(
"Invalid value for MaxTbLog2SizeY: {}",
sps.max_tb_log2_size_y
));
}
sps.pic_size_in_ctbs_y = sps.pic_width_in_ctbs_y * sps.pic_height_in_ctbs_y;
sps.max_transform_hierarchy_depth_inter = r.read_ue_max(4)?;
sps.max_transform_hierarchy_depth_intra = r.read_ue_max(4)?;
sps.scaling_list_enabled_flag = r.read_bit()?;
if sps.scaling_list_enabled_flag {
sps.scaling_list_data_present_flag = r.read_bit()?;
if sps.scaling_list_data_present_flag {
Self::parse_scaling_list_data(&mut sps.scaling_list, &mut r)?;
}
}
sps.amp_enabled_flag = r.read_bit()?;
sps.sample_adaptive_offset_enabled_flag = r.read_bit()?;
sps.pcm_enabled_flag = r.read_bit()?;
if sps.pcm_enabled_flag {
sps.pcm_sample_bit_depth_luma_minus1 = r.read_bits(4)?;
sps.pcm_sample_bit_depth_chroma_minus1 = r.read_bits(4)?;
sps.log2_min_pcm_luma_coding_block_size_minus3 = r.read_ue_max(2)?;
sps.log2_diff_max_min_pcm_luma_coding_block_size = r.read_ue_max(2)?;
sps.pcm_loop_filter_disabled_flag = r.read_bit()?;
}
sps.num_short_term_ref_pic_sets = r.read_ue_max(64)?;
for i in 0..sps.num_short_term_ref_pic_sets {
let mut st = ShortTermRefPicSet::default();
Self::parse_short_term_ref_pic_set(&sps, &mut st, &mut r, i)?;
sps.short_term_ref_pic_set.push(st);
}
sps.long_term_ref_pics_present_flag = r.read_bit()?;
if sps.long_term_ref_pics_present_flag {
sps.num_long_term_ref_pics_sps = r.read_ue_max(32)?;
for i in 0..usize::from(sps.num_long_term_ref_pics_sps) {
sps.lt_ref_pic_poc_lsb_sps[i] =
r.read_bits(usize::from(sps.log2_max_pic_order_cnt_lsb_minus4) + 4)?;
sps.used_by_curr_pic_lt_sps_flag[i] = r.read_bit()?;
}
}
sps.temporal_mvp_enabled_flag = r.read_bit()?;
sps.strong_intra_smoothing_enabled_flag = r.read_bit()?;
sps.vui_parameters_present_flag = r.read_bit()?;
if sps.vui_parameters_present_flag {
Self::parse_vui_parameters(&mut sps, &mut r)?;
}
sps.extension_present_flag = r.read_bit()?;
if sps.extension_present_flag {
sps.range_extension_flag = r.read_bit()?;
if sps.range_extension_flag {
Self::parse_sps_range_extension(&mut sps, &mut r)?;
}
let multilayer_extension_flag = r.read_bit()?;
if multilayer_extension_flag {
return Err(anyhow!("Multilayer extension not supported."));
}
let three_d_extension_flag = r.read_bit()?;
if three_d_extension_flag {
return Err(anyhow!("3D extension not supported."));
}
sps.scc_extension_flag = r.read_bit()?;
if sps.scc_extension_flag {
Self::parse_sps_scc_extension(&mut sps, &mut r)?;
}
}
let shift = if sps.range_extension.high_precision_offsets_enabled_flag {
sps.bit_depth_luma_minus8 + 7
} else {
7
};
sps.wp_offset_half_range_y = 1 << shift;
let shift = if sps.range_extension.high_precision_offsets_enabled_flag {
sps.bit_depth_chroma_minus8 + 7
} else {
7
};
sps.wp_offset_half_range_c = 1 << shift;
log::debug!(
"Parsed SPS({}), resolution: ({}, {}): NAL size was {}",
sps.seq_parameter_set_id,
sps.width(),
sps.height(),
nalu.size
);
let key = sps.seq_parameter_set_id;
self.active_spses.insert(key, sps);
if self.active_spses.keys().len() > MAX_SPS_COUNT {
return Err(anyhow!(
"Broken data: Number of active SPSs > MAX_SPS_COUNT"
));
}
Ok(self.get_sps(key).unwrap())
}
fn parse_pps_scc_extension(pps: &mut Pps, sps: &Sps, r: &mut NaluReader) -> anyhow::Result<()> {
let scc = &mut pps.scc_extension;
scc.curr_pic_ref_enabled_flag = r.read_bit()?;
scc.residual_adaptive_colour_transform_enabled_flag = r.read_bit()?;
if scc.residual_adaptive_colour_transform_enabled_flag {
scc.slice_act_qp_offsets_present_flag = r.read_bit()?;
scc.act_y_qp_offset_plus5 = r.read_se_bounded(-7, 17)?;
scc.act_cb_qp_offset_plus5 = r.read_se_bounded(-7, 17)?;
scc.act_cr_qp_offset_plus3 = r.read_se_bounded(-9, 15)?;
}
scc.palette_predictor_initializers_present_flag = r.read_bit()?;
if scc.palette_predictor_initializers_present_flag {
let max = sps.scc_extension.palette_max_size
+ sps.scc_extension.delta_palette_max_predictor_size;
scc.num_palette_predictor_initializers = r.read_ue_max(max.into())?;
if scc.num_palette_predictor_initializers > 0 {
scc.monochrome_palette_flag = r.read_bit()?;
scc.luma_bit_depth_entry_minus8 = r.read_ue_bounded(
sps.bit_depth_luma_minus8.into(),
sps.bit_depth_luma_minus8.into(),
)?;
if !scc.monochrome_palette_flag {
scc.chroma_bit_depth_entry_minus8 = r.read_ue_bounded(
sps.bit_depth_chroma_minus8.into(),
sps.bit_depth_chroma_minus8.into(),
)?;
}
let num_comps = if scc.monochrome_palette_flag { 1 } else { 3 };
for comp in 0..num_comps {
let num_bits = if comp == 0 {
scc.luma_bit_depth_entry_minus8 + 8
} else {
scc.chroma_bit_depth_entry_minus8 + 8
};
for i in 0..usize::from(scc.num_palette_predictor_initializers) {
scc.palette_predictor_initializer[comp][i] =
r.read_bits(num_bits.into())?;
}
}
}
}
Ok(())
}
fn parse_pps_range_extension(
pps: &mut Pps,
sps: &Sps,
r: &mut NaluReader,
) -> anyhow::Result<()> {
let rext = &mut pps.range_extension;
if pps.transform_skip_enabled_flag {
rext.log2_max_transform_skip_block_size_minus2 =
r.read_ue_max(sps.max_tb_log2_size_y - 2)?;
}
rext.cross_component_prediction_enabled_flag = r.read_bit()?;
rext.chroma_qp_offset_list_enabled_flag = r.read_bit()?;
if rext.chroma_qp_offset_list_enabled_flag {
rext.diff_cu_chroma_qp_offset_depth = r.read_ue()?;
rext.chroma_qp_offset_list_len_minus1 = r.read_ue_max(5)?;
for i in 0..=rext.chroma_qp_offset_list_len_minus1 as usize {
rext.cb_qp_offset_list[i] = r.read_se_bounded(-12, 12)?;
rext.cr_qp_offset_list[i] = r.read_se_bounded(-12, 12)?;
}
}
let bit_depth_y = sps.bit_depth_luma_minus8 + 8;
let max = u32::from(std::cmp::max(0, bit_depth_y - 10));
rext.log2_sao_offset_scale_luma = r.read_ue_max(max)?;
rext.log2_sao_offset_scale_chroma = r.read_ue_max(max)?;
Ok(())
}
/// Parse a PPS NALU.
pub fn parse_pps(&mut self, nalu: &Nalu) -> anyhow::Result<&Pps> {
if !matches!(nalu.header.type_, NaluType::PpsNut) {
return Err(anyhow!(
"Invalid NALU type, expected {:?}, got {:?}",
NaluType::PpsNut,
nalu.header.type_
));
}
let data = nalu.as_ref();
let header = &nalu.header;
let hdr_len = header.len();
// Skip the header
let mut r = NaluReader::new(&data[hdr_len..]);
let mut pps = Pps {
loop_filter_across_tiles_enabled_flag: true,
..Default::default()
};
pps.pic_parameter_set_id = r.read_ue_max(MAX_PPS_COUNT as u32 - 1)?;
pps.seq_parameter_set_id = r.read_ue_max(MAX_SPS_COUNT as u32 - 1)?;
let sps = self.get_sps(pps.seq_parameter_set_id).context(format!(
"Broken stream: stream references SPS {} that has not been successfully parsed",
pps.seq_parameter_set_id
))?;
pps.dependent_slice_segments_enabled_flag = r.read_bit()?;
pps.output_flag_present_flag = r.read_bit()?;
pps.num_extra_slice_header_bits = r.read_bits(3)?;
pps.sign_data_hiding_enabled_flag = r.read_bit()?;
pps.cabac_init_present_flag = r.read_bit()?;
// 7.4.7.1
pps.num_ref_idx_l0_default_active_minus1 = r.read_ue_max(14)?;
pps.num_ref_idx_l1_default_active_minus1 = r.read_ue_max(14)?;
// (7-5)
let qp_bd_offset_y = 6 * i32::from(sps.bit_depth_luma_minus8);
pps.init_qp_minus26 = r.read_se_bounded(-(26 + qp_bd_offset_y), 25)?;
pps.qp_bd_offset_y = qp_bd_offset_y as u32;
pps.constrained_intra_pred_flag = r.read_bit()?;
pps.transform_skip_enabled_flag = r.read_bit()?;
pps.cu_qp_delta_enabled_flag = r.read_bit()?;
if pps.cu_qp_delta_enabled_flag {
pps.diff_cu_qp_delta_depth =
r.read_ue_max(u32::from(sps.log2_diff_max_min_luma_coding_block_size))?;
}
pps.cb_qp_offset = r.read_se_bounded(-12, 12)?;
pps.cr_qp_offset = r.read_se_bounded(-12, 12)?;
pps.slice_chroma_qp_offsets_present_flag = r.read_bit()?;
pps.weighted_pred_flag = r.read_bit()?;
pps.weighted_bipred_flag = r.read_bit()?;
pps.transquant_bypass_enabled_flag = r.read_bit()?;
pps.tiles_enabled_flag = r.read_bit()?;
pps.entropy_coding_sync_enabled_flag = r.read_bit()?;
// A mix of the rbsp data and the algorithm in 6.5.1
if pps.tiles_enabled_flag {
pps.num_tile_columns_minus1 = r.read_ue_max(sps.pic_width_in_ctbs_y - 1)?;
pps.num_tile_rows_minus1 = r.read_ue_max(sps.pic_height_in_ctbs_y - 1)?;
pps.uniform_spacing_flag = r.read_bit()?;
if !pps.uniform_spacing_flag {
pps.column_width_minus1[usize::from(pps.num_tile_columns_minus1)] =
sps.pic_width_in_ctbs_y - 1;
for i in 0..usize::from(pps.num_tile_columns_minus1) {
pps.column_width_minus1[i] = r.read_ue_max(
pps.column_width_minus1[usize::from(pps.num_tile_columns_minus1)] - 1,
)?;
pps.column_width_minus1[usize::from(pps.num_tile_columns_minus1)] -=
pps.column_width_minus1[i] + 1;
}
pps.row_height_minus1[usize::from(pps.num_tile_rows_minus1)] =
sps.pic_height_in_ctbs_y - 1;
for i in 0..usize::from(pps.num_tile_rows_minus1) {
pps.row_height_minus1[i] = r.read_ue_max(
pps.row_height_minus1[usize::from(pps.num_tile_rows_minus1)] - 1,
)?;
pps.row_height_minus1[usize::from(pps.num_tile_rows_minus1)] -=
pps.row_height_minus1[i] + 1;
}
} else {
let nrows = u32::from(pps.num_tile_rows_minus1) + 1;
let ncols = u32::from(pps.num_tile_columns_minus1) + 1;
for j in 0..ncols {
pps.column_width_minus1[j as usize] = ((j + 1) * sps.pic_width_in_ctbs_y)
/ ncols
- j * sps.pic_width_in_ctbs_y / ncols
- 1;
}
for j in 0..nrows {
pps.row_height_minus1[j as usize] = ((j + 1) * sps.pic_height_in_ctbs_y)
/ nrows
- j * sps.pic_height_in_ctbs_y / nrows
- 1;
}
}
pps.loop_filter_across_tiles_enabled_flag = r.read_bit()?;
}
pps.loop_filter_across_slices_enabled_flag = r.read_bit()?;
pps.deblocking_filter_control_present_flag = r.read_bit()?;
if pps.deblocking_filter_control_present_flag {
pps.deblocking_filter_override_enabled_flag = r.read_bit()?;
pps.deblocking_filter_disabled_flag = r.read_bit()?;
if !pps.deblocking_filter_disabled_flag {
pps.beta_offset_div2 = r.read_se_bounded(-6, 6)?;
pps.tc_offset_div2 = r.read_se_bounded(-6, 6)?;
}
}
pps.scaling_list_data_present_flag = r.read_bit()?;
if pps.scaling_list_data_present_flag {
Self::parse_scaling_list_data(&mut pps.scaling_list, &mut r)?;
} else {
for size_id in 0..4 {
let mut matrix_id = 0;
while matrix_id < 6 {
Self::fill_default_scaling_list(&mut pps.scaling_list, size_id, matrix_id);
let step = if size_id == 3 { 3 } else { 1 };
matrix_id += step;
}
}
}
pps.lists_modification_present_flag = r.read_bit()?;
pps.log2_parallel_merge_level_minus2 = r.read_ue_max(sps.ctb_log2_size_y - 2)?;
pps.slice_segment_header_extension_present_flag = r.read_bit()?;
pps.extension_present_flag = r.read_bit()?;
if pps.extension_present_flag {
pps.range_extension_flag = r.read_bit()?;
if pps.range_extension_flag {
Self::parse_pps_range_extension(&mut pps, sps, &mut r)?;
}
let multilayer_extension_flag = r.read_bit()?;
if multilayer_extension_flag {
return Err(anyhow!("Multilayer extension is not supported"));
}
let three_d_extension_flag = r.read_bit()?;
if three_d_extension_flag {
return Err(anyhow!("3D extension is not supported"));
}
pps.scc_extension_flag = r.read_bit()?;
if pps.scc_extension_flag {
Self::parse_pps_scc_extension(&mut pps, sps, &mut r)?;
}
r.skip_bits(4)?; // pps_extension_4bits
}
pps.temporal_id = nalu.header.nuh_temporal_id_plus1 - 1;
log::debug!(
"Parsed PPS({}), NAL size was {}",
pps.pic_parameter_set_id,
nalu.size
);
let key = pps.pic_parameter_set_id;
self.active_ppses.insert(key, pps);
if self.active_ppses.keys().len() > MAX_PPS_COUNT {
return Err(anyhow!(
"Broken Data: number of active PPSs > MAX_PPS_COUNT"
));
}
Ok(self.get_pps(key).unwrap())
}
fn parse_pred_weight_table(
hdr: &mut SliceHeader,
r: &mut NaluReader,
sps: &Sps,
) -> anyhow::Result<()> {
let pwt = &mut hdr.pred_weight_table;
pwt.luma_log2_weight_denom = r.read_ue_max(7)?;
if sps.chroma_array_type != 0 {
pwt.delta_chroma_log2_weight_denom = r.read_se()?;
pwt.chroma_log2_weight_denom = (pwt.luma_log2_weight_denom as i32
+ pwt.delta_chroma_log2_weight_denom as i32)
.try_into()?;
}
for i in 0..=usize::from(hdr.num_ref_idx_l0_active_minus1) {
pwt.luma_weight_l0_flag[i] = r.read_bit()?;
}
if sps.chroma_array_type != 0 {
for i in 0..=usize::from(hdr.num_ref_idx_l0_active_minus1) {
pwt.chroma_weight_l0_flag[i] = r.read_bit()?;
}
}
for i in 0..=usize::from(hdr.num_ref_idx_l0_active_minus1) {
if pwt.luma_weight_l0_flag[i] {
pwt.delta_luma_weight_l0[i] = r.read_se_bounded(-128, 127)?;
pwt.luma_offset_l0[i] = r.read_se_bounded(-128, 127)?;
}
if pwt.chroma_weight_l0_flag[i] {
for j in 0..2 {
pwt.delta_chroma_weight_l0[i][j] = r.read_se_bounded(-128, 127)?;
pwt.delta_chroma_offset_l0[i][j] = r.read_se_bounded(
-4 * sps.wp_offset_half_range_c as i32,
4 * sps.wp_offset_half_range_c as i32 - 1,
)?;
}
}
}
if hdr.type_.is_b() {
for i in 0..=usize::from(hdr.num_ref_idx_l1_active_minus1) {
pwt.luma_weight_l1_flag[i] = r.read_bit()?;
}
if sps.chroma_format_idc != 0 {
for i in 0..=usize::from(hdr.num_ref_idx_l1_active_minus1) {
pwt.chroma_weight_l1_flag[i] = r.read_bit()?;
}
}
for i in 0..=usize::from(hdr.num_ref_idx_l1_active_minus1) {
if pwt.luma_weight_l1_flag[i] {
pwt.delta_luma_weight_l1[i] = r.read_se_bounded(-128, 127)?;
pwt.luma_offset_l1[i] = r.read_se_bounded(-128, 127)?;
}
if pwt.chroma_weight_l1_flag[i] {
for j in 0..2 {
pwt.delta_chroma_weight_l1[i][j] = r.read_se_bounded(-128, 127)?;
pwt.delta_chroma_offset_l1[i][j] = r.read_se_bounded(
-4 * sps.wp_offset_half_range_c as i32,
4 * sps.wp_offset_half_range_c as i32 - 1,
)?;
}
}
}
}
Ok(())
}
fn parse_ref_pic_lists_modification(
hdr: &mut SliceHeader,
r: &mut NaluReader,
) -> anyhow::Result<()> {
let rplm = &mut hdr.ref_pic_list_modification;
rplm.ref_pic_list_modification_flag_l0 = r.read_bit()?;
if rplm.ref_pic_list_modification_flag_l0 {
for _ in 0..=hdr.num_ref_idx_l0_active_minus1 {
let num_bits = (hdr.num_pic_total_curr as f64).log2().ceil() as _;
let entry = r.read_bits(num_bits)?;
if entry > hdr.num_pic_total_curr - 1 {
return Err(anyhow!(
"Invalid list_entry_l0 {}, expected at max NumPicTotalCurr - 1: {}",
entry,
hdr.num_pic_total_curr - 1
));
}
rplm.list_entry_l0.push(entry);
}
}
if hdr.type_.is_b() {
rplm.ref_pic_list_modification_flag_l1 = r.read_bit()?;
if rplm.ref_pic_list_modification_flag_l1 {
for _ in 0..=hdr.num_ref_idx_l1_active_minus1 {
let num_bits = (hdr.num_pic_total_curr as f64).log2().ceil() as _;
let entry = r.read_bits(num_bits)?;
if entry > hdr.num_pic_total_curr - 1 {
return Err(anyhow!(
"Invalid list_entry_l1 {}, expected at max NumPicTotalCurr - 1: {}",
entry,
hdr.num_pic_total_curr - 1
));
}
rplm.list_entry_l1.push(entry);
}
}
}
Ok(())
}
/// Further sets default values given `sps` and `pps`.
pub fn slice_header_set_defaults(hdr: &mut SliceHeader, sps: &Sps, pps: &Pps) {
// Set some defaults that can't be defined in Default::default().
hdr.deblocking_filter_disabled_flag = pps.deblocking_filter_disabled_flag;
hdr.beta_offset_div2 = pps.beta_offset_div2;
hdr.tc_offset_div2 = pps.tc_offset_div2;
hdr.loop_filter_across_slices_enabled_flag = pps.loop_filter_across_slices_enabled_flag;
hdr.curr_rps_idx = sps.num_short_term_ref_pic_sets;
hdr.use_integer_mv_flag = sps.scc_extension.motion_vector_resolution_control_idc != 0;
}
/// Parses a slice header from a slice NALU.
pub fn parse_slice_header<'a>(&mut self, nalu: Nalu<'a>) -> anyhow::Result<Slice<'a>> {
if !matches!(
nalu.header.type_,
NaluType::TrailN
| NaluType::TrailR
| NaluType::TsaN
| NaluType::TsaR
| NaluType::StsaN
| NaluType::StsaR
| NaluType::RadlN
| NaluType::RadlR
| NaluType::RaslN
| NaluType::RaslR
| NaluType::BlaWLp
| NaluType::BlaWRadl
| NaluType::BlaNLp
| NaluType::IdrWRadl
| NaluType::IdrNLp
| NaluType::CraNut,
) {
return Err(anyhow!(
"Invalid NALU type: {:?} is not a slice NALU",
nalu.header.type_
));
}
let data = nalu.as_ref();
let nalu_header = &nalu.header;
let hdr_len = nalu_header.len();
// Skip the header
let mut r = NaluReader::new(&data[hdr_len..]);
let mut hdr = SliceHeader {
first_slice_segment_in_pic_flag: r.read_bit()?,
..Default::default()
};
if nalu.header.type_.is_irap() {
hdr.no_output_of_prior_pics_flag = r.read_bit()?;
}
hdr.pic_parameter_set_id = r.read_ue_max(63)?;
let pps = self.get_pps(hdr.pic_parameter_set_id).with_context(|| {
format!(
"Broken stream: slice references PPS {} that has not been successfully parsed.",
hdr.pic_parameter_set_id,
)
})?;
let sps = self.get_sps(pps.seq_parameter_set_id).with_context(|| {
format!(
"Broken stream: slice's PPS references SPS {} that has not been successfully parsed.", pps.seq_parameter_set_id
)
})?;
Self::slice_header_set_defaults(&mut hdr, sps, pps);
if !hdr.first_slice_segment_in_pic_flag {
if pps.dependent_slice_segments_enabled_flag {
hdr.dependent_slice_segment_flag = r.read_bit()?;
}
let num_bits = (sps.pic_size_in_ctbs_y as f64).log2().ceil() as _;
hdr.segment_address = r.read_bits(num_bits)?;
if hdr.segment_address > sps.pic_size_in_ctbs_y - 1 {
return Err(anyhow!(
"Invalid slice_segment_address {}",
hdr.segment_address
));
}
}
if !hdr.dependent_slice_segment_flag {
r.skip_bits(usize::from(pps.num_extra_slice_header_bits))?;
let slice_type: u32 = r.read_ue()?;
hdr.type_ = SliceType::n(slice_type).ok_or(anyhow!("Invalid slice type"))?;
if pps.output_flag_present_flag {
hdr.pic_output_flag = r.read_bit()?;
}
if sps.separate_colour_plane_flag {
hdr.colour_plane_id = r.read_bits(2)?;
}
if !matches!(nalu_header.type_, NaluType::IdrWRadl | NaluType::IdrNLp) {
let num_bits = usize::from(sps.log2_max_pic_order_cnt_lsb_minus4 + 4);
hdr.pic_order_cnt_lsb = r.read_bits(num_bits)?;
if u32::from(hdr.pic_order_cnt_lsb)
> 2u32.pow(u32::from(sps.log2_max_pic_order_cnt_lsb_minus4 + 4))
{
return Err(anyhow!(
"Invalid pic_order_cnt_lsb {}",
hdr.pic_order_cnt_lsb
));
}
hdr.short_term_ref_pic_set_sps_flag = r.read_bit()?;
if !hdr.short_term_ref_pic_set_sps_flag {
let epb_before = r.num_epb();
let bits_left_before = r.num_bits_left();
let st_rps_idx = sps.num_short_term_ref_pic_sets;
Self::parse_short_term_ref_pic_set(
sps,
&mut hdr.short_term_ref_pic_set,
&mut r,
st_rps_idx,
)?;
hdr.st_rps_bits = ((bits_left_before - r.num_bits_left())
- 8 * (r.num_epb() - epb_before))
as u32;
} else if sps.num_short_term_ref_pic_sets > 1 {
let num_bits = (sps.num_short_term_ref_pic_sets as f64).log2().ceil() as _;
hdr.short_term_ref_pic_set_idx = r.read_bits(num_bits)?;
if hdr.short_term_ref_pic_set_idx > sps.num_short_term_ref_pic_sets - 1 {
return Err(anyhow!(
"Invalid short_term_ref_pic_set_idx {}",
hdr.short_term_ref_pic_set_idx
));
}
}
if hdr.short_term_ref_pic_set_sps_flag {
hdr.curr_rps_idx = hdr.short_term_ref_pic_set_idx;
}
if sps.long_term_ref_pics_present_flag {
if sps.num_long_term_ref_pics_sps > 0 {
hdr.num_long_term_sps =
r.read_ue_max(u32::from(sps.num_long_term_ref_pics_sps))?;
}
hdr.num_long_term_pics = r.read_ue_max(
MAX_LONG_TERM_REF_PIC_SETS as u32 - u32::from(hdr.num_long_term_sps),
)?;
let num_lt = hdr.num_long_term_sps + hdr.num_long_term_pics;
for i in 0..usize::from(num_lt) {
// The variables `PocLsbLt[ i ]` and `UsedByCurrPicLt[ i ]` are derived as follows:
//
// – If i is less than num_long_term_sps, `PocLsbLt[ i ]` is set equal to
// lt_ref_pic_poc_lsb_sps[ `lt_idx_sps[ i ]` ] and `UsedByCurrPicLt[ i ]` is set equal
// to used_by_curr_pic_lt_sps_flag[ `lt_idx_sps[ i ]` ].
//
// – Otherwise, `PocLsbLt[ i ]`
// is set equal to `poc_lsb_lt[ i ]` and `UsedByCurrPicLt[ i ]` is set equal to
// `used_by_curr_pic_lt_flag[ i ]`.
if i < usize::from(hdr.num_long_term_sps) {
if sps.num_long_term_ref_pics_sps > 1 {
let num_bits =
(sps.num_long_term_ref_pics_sps as f64).log2().ceil() as _;
hdr.lt_idx_sps[i] = r.read_bits(num_bits)?;
if hdr.lt_idx_sps[i] > sps.num_long_term_ref_pics_sps - 1 {
return Err(anyhow!(
"Invalid lt_idx_sps[{}] {}",
i,
hdr.lt_idx_sps[i]
));
}
}
hdr.poc_lsb_lt[i] =
sps.lt_ref_pic_poc_lsb_sps[usize::from(hdr.lt_idx_sps[i])];
hdr.used_by_curr_pic_lt[i] =
sps.used_by_curr_pic_lt_sps_flag[usize::from(hdr.lt_idx_sps[i])];
} else {
let num_bits = usize::from(sps.log2_max_pic_order_cnt_lsb_minus4) + 4;
hdr.poc_lsb_lt[i] = r.read_bits(num_bits)?;
hdr.used_by_curr_pic_lt[i] = r.read_bit()?;
}
hdr.delta_poc_msb_present_flag[i] = r.read_bit()?;
if hdr.delta_poc_msb_present_flag[i] {
// The value of `delta_poc_msb_cycle_lt[ i ]` shall be
// in the range of 0 to 2(32 −
// log2_max_pic_order_cnt_lsb_minus4 − 4 ),
// inclusive. When `delta_poc_msb_cycle_lt[ i ]` is
// not present, it is inferred to be equal to 0.
let max =
2u32.pow(32 - u32::from(sps.log2_max_pic_order_cnt_lsb_minus4) - 4);
hdr.delta_poc_msb_cycle_lt[i] = r.read_ue_max(max)?;
}
// Equation 7-52 (simplified)
if i != 0 && i != usize::from(hdr.num_long_term_sps) {
hdr.delta_poc_msb_cycle_lt[i] += hdr.delta_poc_msb_cycle_lt[i - 1];
}
}
}
if sps.temporal_mvp_enabled_flag {
hdr.temporal_mvp_enabled_flag = r.read_bit()?;
}
}
if sps.sample_adaptive_offset_enabled_flag {
hdr.sao_luma_flag = r.read_bit()?;
if sps.chroma_array_type != 0 {
hdr.sao_chroma_flag = r.read_bit()?;
}
}
if hdr.type_.is_p() || hdr.type_.is_b() {
hdr.num_ref_idx_active_override_flag = r.read_bit()?;
if hdr.num_ref_idx_active_override_flag {
hdr.num_ref_idx_l0_active_minus1 = r.read_ue_max(MAX_REF_IDX_ACTIVE - 1)?;
if hdr.type_.is_b() {
hdr.num_ref_idx_l1_active_minus1 = r.read_ue_max(MAX_REF_IDX_ACTIVE - 1)?;
}
} else {
hdr.num_ref_idx_l0_active_minus1 = pps.num_ref_idx_l0_default_active_minus1;
hdr.num_ref_idx_l1_active_minus1 = pps.num_ref_idx_l1_default_active_minus1;
}
// 7-57
let mut num_pic_total_curr = 0;
let rps = if hdr.short_term_ref_pic_set_sps_flag {
sps.short_term_ref_pic_set
.get(usize::from(hdr.curr_rps_idx))
.ok_or(anyhow!("Invalid RPS"))?
} else {
&hdr.short_term_ref_pic_set
};
for i in 0..usize::from(rps.num_negative_pics) {
if rps.used_by_curr_pic_s0[i] {
num_pic_total_curr += 1;
}
}
for i in 0..usize::from(rps.num_positive_pics) {
if rps.used_by_curr_pic_s1[i] {
num_pic_total_curr += 1;
}
}
for i in 0..usize::from(hdr.num_long_term_sps + hdr.num_long_term_pics) {
if hdr.used_by_curr_pic_lt[i] {
num_pic_total_curr += 1;
}
}
if pps.scc_extension.curr_pic_ref_enabled_flag {
num_pic_total_curr += 1;
}
hdr.num_pic_total_curr = num_pic_total_curr;
if pps.lists_modification_present_flag && hdr.num_pic_total_curr > 1 {
Self::parse_ref_pic_lists_modification(&mut hdr, &mut r)?;
}
if hdr.type_.is_b() {
hdr.mvd_l1_zero_flag = r.read_bit()?;
}
if pps.cabac_init_present_flag {
hdr.cabac_init_flag = r.read_bit()?;
}
if hdr.temporal_mvp_enabled_flag {
if hdr.type_.is_b() {
hdr.collocated_from_l0_flag = r.read_bit()?;
}
if (hdr.collocated_from_l0_flag && hdr.num_ref_idx_l0_active_minus1 > 0)
|| (!hdr.collocated_from_l0_flag && hdr.num_ref_idx_l1_active_minus1 > 0)
{
let max = if (hdr.type_.is_p() || hdr.type_.is_b())
&& hdr.collocated_from_l0_flag
{
hdr.num_ref_idx_l0_active_minus1
} else if hdr.type_.is_b() && !hdr.collocated_from_l0_flag {
hdr.num_ref_idx_l1_active_minus1
} else {
return Err(anyhow!("Invalid value for collocated_ref_idx"));
};
{
hdr.collocated_ref_idx = r.read_ue_max(u32::from(max))?;
}
}
}
if (pps.weighted_pred_flag && hdr.type_.is_p())
|| (pps.weighted_bipred_flag && hdr.type_.is_b())
{
Self::parse_pred_weight_table(&mut hdr, &mut r, sps)?;
}
hdr.five_minus_max_num_merge_cand = r.read_ue()?;
if sps.scc_extension.motion_vector_resolution_control_idc == 2 {
hdr.use_integer_mv_flag = r.read_bit()?;
}
}
hdr.qp_delta = r.read_se_bounded(-87, 77)?;
let slice_qp_y = (26 + pps.init_qp_minus26 + hdr.qp_delta) as i32;
if slice_qp_y < -(pps.qp_bd_offset_y as i32) || slice_qp_y > 51 {
return Err(anyhow!("Invalid slice_qp_delta: {}", hdr.qp_delta));
}
if pps.slice_chroma_qp_offsets_present_flag {
hdr.cb_qp_offset = r.read_se_bounded(-12, 12)?;
let qp_offset = pps.cb_qp_offset + hdr.cb_qp_offset;
if !(-12..=12).contains(&qp_offset) {
return Err(anyhow!(
"Invalid value for slice_cb_qp_offset: {}",
hdr.cb_qp_offset
));
}
hdr.cr_qp_offset = r.read_se_bounded(-12, 12)?;
let qp_offset = pps.cr_qp_offset + hdr.cr_qp_offset;
if !(-12..=12).contains(&qp_offset) {
return Err(anyhow!(
"Invalid value for slice_cr_qp_offset: {}",
hdr.cr_qp_offset
));
}
}
if pps.scc_extension.slice_act_qp_offsets_present_flag {
hdr.slice_act_y_qp_offset = r.read_se_bounded(-12, 12)?;
hdr.slice_act_cb_qp_offset = r.read_se_bounded(-12, 12)?;
hdr.slice_act_cr_qp_offset = r.read_se_bounded(-12, 12)?;
}
if pps.range_extension.chroma_qp_offset_list_enabled_flag {
hdr.cu_chroma_qp_offset_enabled_flag = r.read_bit()?;
}
if pps.deblocking_filter_override_enabled_flag {
hdr.deblocking_filter_override_flag = r.read_bit()?;
}
if hdr.deblocking_filter_override_flag {
hdr.deblocking_filter_disabled_flag = r.read_bit()?;
if !hdr.deblocking_filter_disabled_flag {
hdr.beta_offset_div2 = r.read_se_bounded(-6, 6)?;
hdr.tc_offset_div2 = r.read_se_bounded(-6, 6)?;
}
}
if pps.loop_filter_across_slices_enabled_flag
&& (hdr.sao_luma_flag
|| hdr.sao_chroma_flag
|| !hdr.deblocking_filter_disabled_flag)
{
hdr.loop_filter_across_slices_enabled_flag = r.read_bit()?;
}
}
if pps.tiles_enabled_flag || pps.entropy_coding_sync_enabled_flag {
let max = if !pps.tiles_enabled_flag && pps.entropy_coding_sync_enabled_flag {
sps.pic_height_in_ctbs_y - 1
} else if pps.tiles_enabled_flag && !pps.entropy_coding_sync_enabled_flag {
u32::from((pps.num_tile_columns_minus1 + 1) * (pps.num_tile_rows_minus1 + 1) - 1)
} else {
(u32::from(pps.num_tile_columns_minus1) + 1) * sps.pic_height_in_ctbs_y - 1
};
hdr.num_entry_point_offsets = r.read_ue_max(max)?;
if hdr.num_entry_point_offsets > 0 {
hdr.offset_len_minus1 = r.read_ue_max(31)?;
for i in 0..hdr.num_entry_point_offsets as usize {
let num_bits = usize::from(hdr.offset_len_minus1 + 1);
hdr.entry_point_offset_minus1[i] = r.read_bits(num_bits)?;
}
}
}
if pps.slice_segment_header_extension_present_flag {
let segment_header_extension_length = r.read_ue_max(256)?;
for _ in 0..segment_header_extension_length {
r.skip_bits(8)?; // slice_segment_header_extension_data_byte[i]
}
}
// byte_alignment()
r.skip_bits(1)?; // Alignment bit
let num_bits = r.num_bits_left() % 8;
r.skip_bits(num_bits)?;
let epb = r.num_epb();
hdr.header_bit_size = ((nalu.size - epb) * 8 - r.num_bits_left()) as u32;
hdr.n_emulation_prevention_bytes = epb as u32;
log::debug!(
"Parsed slice {:?}, NAL size was {}",
nalu_header.type_,
nalu.size
);
Ok(Slice { header: hdr, nalu })
}
/// Returns a previously parsed vps given `vps_id`, if any.
pub fn get_vps(&self, vps_id: u8) -> Option<&Vps> {
self.active_vpses.get(&vps_id)
}
/// Returns a previously parsed sps given `sps_id`, if any.
pub fn get_sps(&self, sps_id: u8) -> Option<&Sps> {
self.active_spses.get(&sps_id)
}
/// Returns a previously parsed pps given `pps_id`, if any.
pub fn get_pps(&self, pps_id: u8) -> Option<&Pps> {
self.active_ppses.get(&pps_id)
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use crate::codec::h264::nalu::Nalu;
use crate::codec::h265::parser::Level;
use crate::codec::h265::parser::NaluHeader;
use crate::codec::h265::parser::NaluType;
use crate::codec::h265::parser::Parser;
use crate::codec::h265::parser::SliceType;
const STREAM_BEAR: &[u8] = include_bytes!("test_data/bear.h265");
const STREAM_BEAR_NUM_NALUS: usize = 35;
const STREAM_BBB: &[u8] = include_bytes!("test_data/bbb.h265");
const STREAM_BBB_NUM_NALUS: usize = 64;
const STREAM_TEST25FPS: &[u8] = include_bytes!("test_data/test-25fps.h265");
const STREAM_TEST25FPS_NUM_NALUS: usize = 254;
const STREAM_TEST_25_FPS_SLICE_0: &[u8] =
include_bytes!("test_data/test-25fps-h265-slice-data-0.bin");
const STREAM_TEST_25_FPS_SLICE_1: &[u8] =
include_bytes!("test_data/test-25fps-h265-slice-data-1.bin");
fn dispatch_parse_call(parser: &mut Parser, nalu: Nalu<NaluHeader>) -> anyhow::Result<()> {
match nalu.header.type_ {
NaluType::TrailN
| NaluType::TrailR
| NaluType::TsaN
| NaluType::TsaR
| NaluType::StsaN
| NaluType::StsaR
| NaluType::RadlN
| NaluType::RadlR
| NaluType::RaslN
| NaluType::RaslR
| NaluType::BlaWLp
| NaluType::BlaWRadl
| NaluType::BlaNLp
| NaluType::IdrWRadl
| NaluType::IdrNLp
| NaluType::CraNut => {
parser.parse_slice_header(nalu).unwrap();
}
NaluType::VpsNut => {
parser.parse_vps(&nalu).unwrap();
}
NaluType::SpsNut => {
parser.parse_sps(&nalu).unwrap();
}
NaluType::PpsNut => {
parser.parse_pps(&nalu).unwrap();
}
_ => { /* ignore */ }
}
Ok(())
}
fn find_nalu_by_type(
bitstream: &[u8],
nalu_type: NaluType,
mut nskip: i32,
) -> Option<Nalu<NaluHeader>> {
let mut cursor = Cursor::new(bitstream);
while let Ok(nalu) = Nalu::<NaluHeader>::next(&mut cursor) {
if nalu.header.type_ == nalu_type {
if nskip == 0 {
return Some(nalu);
} else {
nskip -= 1;
}
}
}
None
}
/// This test is adapted from chromium, available at media/video/h265_parser_unittest.cc
#[test]
fn parse_nalus_from_stream_file() {
let mut cursor = Cursor::new(STREAM_BEAR);
let mut num_nalus = 0;
while Nalu::<NaluHeader>::next(&mut cursor).is_ok() {
num_nalus += 1;
}
assert_eq!(num_nalus, STREAM_BEAR_NUM_NALUS);
let mut cursor = Cursor::new(STREAM_BBB);
let mut num_nalus = 0;
while Nalu::<NaluHeader>::next(&mut cursor).is_ok() {
num_nalus += 1;
}
assert_eq!(num_nalus, STREAM_BBB_NUM_NALUS);
let mut cursor = Cursor::new(STREAM_TEST25FPS);
let mut num_nalus = 0;
while Nalu::<NaluHeader>::next(&mut cursor).is_ok() {
num_nalus += 1;
}
assert_eq!(num_nalus, STREAM_TEST25FPS_NUM_NALUS);
}
/// Parse the syntax, making sure we can parse the files without crashing.
/// Does not check whether the parsed values are correct.
#[test]
fn parse_syntax_from_nals() {
let mut cursor = Cursor::new(STREAM_BBB);
let mut parser = Parser::default();
while let Ok(nalu) = Nalu::<NaluHeader>::next(&mut cursor) {
dispatch_parse_call(&mut parser, nalu).unwrap();
}
let mut cursor = Cursor::new(STREAM_BEAR);
let mut parser = Parser::default();
while let Ok(nalu) = Nalu::<NaluHeader>::next(&mut cursor) {
dispatch_parse_call(&mut parser, nalu).unwrap();
}
let mut cursor = Cursor::new(STREAM_TEST25FPS);
let mut parser = Parser::default();
while let Ok(nalu) = Nalu::<NaluHeader>::next(&mut cursor) {
dispatch_parse_call(&mut parser, nalu).unwrap();
}
}
/// Adapted from Chromium (media/video/h265_parser_unittest.cc::VpsParsing())
#[test]
fn chromium_vps_parsing() {
let mut cursor = Cursor::new(STREAM_BEAR);
let mut parser = Parser::default();
let vps_nalu = Nalu::<NaluHeader>::next(&mut cursor).unwrap();
let vps = parser.parse_vps(&vps_nalu).unwrap();
assert!(vps.base_layer_internal_flag);
assert!(vps.base_layer_available_flag);
assert_eq!(vps.max_layers_minus1, 0);
assert_eq!(vps.max_sub_layers_minus1, 0);
assert!(vps.temporal_id_nesting_flag);
assert_eq!(vps.profile_tier_level.general_profile_idc, 1);
assert_eq!(vps.profile_tier_level.general_level_idc, Level::L2);
assert_eq!(vps.max_dec_pic_buffering_minus1[0], 4);
assert_eq!(vps.max_num_reorder_pics[0], 2);
assert_eq!(vps.max_latency_increase_plus1[0], 0);
for i in 1..7 {
assert_eq!(vps.max_dec_pic_buffering_minus1[i], 0);
assert_eq!(vps.max_num_reorder_pics[i], 0);
assert_eq!(vps.max_latency_increase_plus1[i], 0);
}
assert_eq!(vps.max_layer_id, 0);
assert_eq!(vps.num_layer_sets_minus1, 0);
assert!(!vps.timing_info_present_flag);
}
/// Adapted from Chromium (media/video/h265_parser_unittest.cc::SpsParsing())
#[test]
fn chromium_sps_parsing() {
let mut parser = Parser::default();
let sps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::SpsNut, 0).unwrap();
let sps = parser.parse_sps(&sps_nalu).unwrap();
assert_eq!(sps.max_sub_layers_minus1, 0);
assert_eq!(sps.profile_tier_level.general_profile_idc, 1);
assert_eq!(sps.profile_tier_level.general_level_idc, Level::L2);
assert_eq!(sps.seq_parameter_set_id, 0);
assert_eq!(sps.chroma_format_idc, 1);
assert!(!sps.separate_colour_plane_flag);
assert_eq!(sps.pic_width_in_luma_samples, 320);
assert_eq!(sps.pic_height_in_luma_samples, 184);
assert_eq!(sps.conf_win_left_offset, 0);
assert_eq!(sps.conf_win_right_offset, 0);
assert_eq!(sps.conf_win_top_offset, 0);
assert_eq!(sps.conf_win_bottom_offset, 2);
assert_eq!(sps.bit_depth_luma_minus8, 0);
assert_eq!(sps.bit_depth_chroma_minus8, 0);
assert_eq!(sps.log2_max_pic_order_cnt_lsb_minus4, 4);
assert_eq!(sps.max_dec_pic_buffering_minus1[0], 4);
assert_eq!(sps.max_num_reorder_pics[0], 2);
assert_eq!(sps.max_latency_increase_plus1[0], 0);
for i in 1..7 {
assert_eq!(sps.max_dec_pic_buffering_minus1[i], 0);
assert_eq!(sps.max_num_reorder_pics[i], 0);
assert_eq!(sps.max_latency_increase_plus1[i], 0);
}
assert_eq!(sps.log2_min_luma_coding_block_size_minus3, 0);
assert_eq!(sps.log2_diff_max_min_luma_coding_block_size, 3);
assert_eq!(sps.log2_min_luma_transform_block_size_minus2, 0);
assert_eq!(sps.log2_diff_max_min_luma_transform_block_size, 3);
assert_eq!(sps.max_transform_hierarchy_depth_inter, 0);
assert_eq!(sps.max_transform_hierarchy_depth_intra, 0);
assert!(!sps.scaling_list_enabled_flag);
assert!(!sps.scaling_list_data_present_flag);
assert!(!sps.amp_enabled_flag);
assert!(sps.sample_adaptive_offset_enabled_flag);
assert!(!sps.pcm_enabled_flag);
assert_eq!(sps.pcm_sample_bit_depth_luma_minus1, 0);
assert_eq!(sps.pcm_sample_bit_depth_chroma_minus1, 0);
assert_eq!(sps.log2_min_pcm_luma_coding_block_size_minus3, 0);
assert_eq!(sps.log2_diff_max_min_pcm_luma_coding_block_size, 0);
assert!(!sps.pcm_loop_filter_disabled_flag);
assert_eq!(sps.num_short_term_ref_pic_sets, 0);
assert_eq!(sps.num_long_term_ref_pics_sps, 0);
assert!(sps.temporal_mvp_enabled_flag);
assert!(sps.strong_intra_smoothing_enabled_flag);
assert_eq!(sps.vui_parameters.sar_width, 0);
assert_eq!(sps.vui_parameters.sar_height, 0);
assert!(!sps.vui_parameters.video_full_range_flag);
assert!(!sps.vui_parameters.colour_description_present_flag);
// Note: the original test has 0 for the three variables below, but they
// have valid defaults in the spec (i.e.: 2).
assert_eq!(sps.vui_parameters.colour_primaries, 2);
assert_eq!(sps.vui_parameters.transfer_characteristics, 2);
assert_eq!(sps.vui_parameters.matrix_coeffs, 2);
assert_eq!(sps.vui_parameters.def_disp_win_left_offset, 0);
assert_eq!(sps.vui_parameters.def_disp_win_right_offset, 0);
assert_eq!(sps.vui_parameters.def_disp_win_top_offset, 0);
assert_eq!(sps.vui_parameters.def_disp_win_bottom_offset, 0);
}
/// Adapted from Chromium (media/video/h265_parser_unittest.cc::PpsParsing())
#[test]
fn chromium_pps_parsing() {
let mut parser = Parser::default();
// Have to parse the SPS to set up the parser's internal state.
let sps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::SpsNut, 0).unwrap();
parser.parse_sps(&sps_nalu).unwrap();
let pps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::PpsNut, 0).unwrap();
let pps = parser.parse_pps(&pps_nalu).unwrap();
assert_eq!(pps.pic_parameter_set_id, 0);
assert_eq!(pps.seq_parameter_set_id, 0);
assert!(!pps.dependent_slice_segments_enabled_flag);
assert!(!pps.output_flag_present_flag);
assert_eq!(pps.num_extra_slice_header_bits, 0);
assert!(pps.sign_data_hiding_enabled_flag);
assert!(!pps.cabac_init_present_flag);
assert_eq!(pps.num_ref_idx_l0_default_active_minus1, 0);
assert_eq!(pps.num_ref_idx_l1_default_active_minus1, 0);
assert_eq!(pps.init_qp_minus26, 0);
assert!(!pps.constrained_intra_pred_flag);
assert!(!pps.transform_skip_enabled_flag);
assert!(pps.cu_qp_delta_enabled_flag);
assert_eq!(pps.diff_cu_qp_delta_depth, 0);
assert_eq!(pps.cb_qp_offset, 0);
assert_eq!(pps.cr_qp_offset, 0);
assert!(!pps.slice_chroma_qp_offsets_present_flag);
assert!(pps.weighted_pred_flag);
assert!(!pps.weighted_bipred_flag);
assert!(!pps.transquant_bypass_enabled_flag);
assert!(!pps.tiles_enabled_flag);
assert!(pps.entropy_coding_sync_enabled_flag);
assert!(pps.loop_filter_across_tiles_enabled_flag);
assert!(!pps.scaling_list_data_present_flag);
assert!(!pps.lists_modification_present_flag);
assert_eq!(pps.log2_parallel_merge_level_minus2, 0);
assert!(!pps.slice_segment_header_extension_present_flag);
}
/// Adapted from Chromium (media/video/h265_parser_unittest.cc::SliceHeaderParsing())
#[test]
fn chromium_slice_header_parsing() {
let mut parser = Parser::default();
// Have to parse the SPS/VPS/PPS to set up the parser's internal state.
let vps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::VpsNut, 0).unwrap();
parser.parse_vps(&vps_nalu).unwrap();
let sps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::SpsNut, 0).unwrap();
parser.parse_sps(&sps_nalu).unwrap();
let pps_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::PpsNut, 0).unwrap();
parser.parse_pps(&pps_nalu).unwrap();
// Just like the Chromium test, do an IDR slice, then a non IDR slice.
let slice_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::IdrWRadl, 0).unwrap();
let slice = parser.parse_slice_header(slice_nalu).unwrap();
let hdr = &slice.header;
assert!(hdr.first_slice_segment_in_pic_flag);
assert!(!hdr.no_output_of_prior_pics_flag);
assert_eq!(hdr.pic_parameter_set_id, 0);
assert!(!hdr.dependent_slice_segment_flag);
assert_eq!(hdr.type_, SliceType::I);
assert!(hdr.sao_luma_flag);
assert!(hdr.sao_chroma_flag);
assert_eq!(hdr.qp_delta, 8);
assert!(hdr.loop_filter_across_slices_enabled_flag);
let slice_nalu = find_nalu_by_type(STREAM_BEAR, NaluType::TrailR, 0).unwrap();
let slice = parser.parse_slice_header(slice_nalu).unwrap();
let hdr = &slice.header;
assert!(hdr.first_slice_segment_in_pic_flag);
assert_eq!(hdr.pic_parameter_set_id, 0);
assert!(!hdr.dependent_slice_segment_flag);
assert_eq!(hdr.type_, SliceType::P);
assert_eq!(hdr.pic_order_cnt_lsb, 4);
assert!(!hdr.short_term_ref_pic_set_sps_flag);
assert_eq!(hdr.short_term_ref_pic_set.num_negative_pics, 1);
assert_eq!(hdr.short_term_ref_pic_set.num_positive_pics, 0);
assert_eq!(hdr.short_term_ref_pic_set.delta_poc_s0[0], -4);
assert!(hdr.short_term_ref_pic_set.used_by_curr_pic_s0[0]);
assert!(hdr.temporal_mvp_enabled_flag);
assert!(hdr.sao_luma_flag);
assert!(hdr.sao_chroma_flag);
assert!(!hdr.num_ref_idx_active_override_flag);
assert_eq!(hdr.pred_weight_table.luma_log2_weight_denom, 0);
assert_eq!(hdr.pred_weight_table.delta_chroma_log2_weight_denom, 7);
assert_eq!(hdr.pred_weight_table.delta_luma_weight_l0[0], 0);
assert_eq!(hdr.pred_weight_table.luma_offset_l0[0], -2);
assert_eq!(hdr.pred_weight_table.delta_chroma_weight_l0[0][0], -9);
assert_eq!(hdr.pred_weight_table.delta_chroma_weight_l0[0][1], -9);
assert_eq!(hdr.pred_weight_table.delta_chroma_offset_l0[0][0], 0);
assert_eq!(hdr.pred_weight_table.delta_chroma_offset_l0[0][1], 0);
assert_eq!(hdr.five_minus_max_num_merge_cand, 3);
assert_eq!(hdr.qp_delta, 8);
assert!(hdr.loop_filter_across_slices_enabled_flag);
}
/// A custom test for VPS parsing with data manually extracted from
/// GStreamer using GDB.
#[test]
fn test25fps_vps_header_parsing() {
let mut cursor = Cursor::new(STREAM_TEST25FPS);
let mut parser = Parser::default();
let vps_nalu = Nalu::<NaluHeader>::next(&mut cursor).unwrap();
let vps = parser.parse_vps(&vps_nalu).unwrap();
assert!(vps.base_layer_internal_flag);
assert!(vps.base_layer_available_flag);
assert_eq!(vps.max_layers_minus1, 0);
assert_eq!(vps.max_sub_layers_minus1, 0);
assert!(vps.temporal_id_nesting_flag);
assert_eq!(vps.profile_tier_level.general_profile_space, 0);
assert!(!vps.profile_tier_level.general_tier_flag);
assert_eq!(vps.profile_tier_level.general_profile_idc, 1);
for i in 0..32 {
let val = i == 1 || i == 2;
assert_eq!(
vps.profile_tier_level.general_profile_compatibility_flag[i],
val
);
}
assert!(vps.profile_tier_level.general_progressive_source_flag);
assert!(!vps.profile_tier_level.general_interlaced_source_flag);
assert!(!vps.profile_tier_level.general_non_packed_constraint_flag,);
assert!(vps.profile_tier_level.general_frame_only_constraint_flag,);
assert!(!vps.profile_tier_level.general_max_12bit_constraint_flag,);
assert!(!vps.profile_tier_level.general_max_10bit_constraint_flag,);
assert!(!vps.profile_tier_level.general_max_8bit_constraint_flag,);
assert!(!vps.profile_tier_level.general_max_422chroma_constraint_flag,);
assert!(!vps.profile_tier_level.general_max_420chroma_constraint_flag,);
assert!(
!vps.profile_tier_level
.general_max_monochrome_constraint_flag,
);
assert!(!vps.profile_tier_level.general_intra_constraint_flag);
assert!(
!vps.profile_tier_level
.general_one_picture_only_constraint_flag,
);
assert!(
!vps.profile_tier_level
.general_lower_bit_rate_constraint_flag,
);
assert!(!vps.profile_tier_level.general_max_14bit_constraint_flag,);
assert_eq!(vps.profile_tier_level.general_level_idc, Level::L2);
assert!(vps.sub_layer_ordering_info_present_flag);
assert_eq!(vps.max_dec_pic_buffering_minus1[0], 4);
assert_eq!(vps.max_num_reorder_pics[0], 2);
assert_eq!(vps.max_latency_increase_plus1[0], 5);
for i in 1..7 {
assert_eq!(vps.max_dec_pic_buffering_minus1[i], 0);
assert_eq!(vps.max_num_reorder_pics[i], 0);
assert_eq!(vps.max_latency_increase_plus1[i], 0);
}
assert_eq!(vps.max_layer_id, 0);
assert_eq!(vps.num_layer_sets_minus1, 0);
assert!(!vps.timing_info_present_flag);
assert_eq!(vps.num_units_in_tick, 0);
assert_eq!(vps.time_scale, 0);
assert!(!vps.poc_proportional_to_timing_flag);
assert_eq!(vps.num_ticks_poc_diff_one_minus1, 0);
assert_eq!(vps.num_hrd_parameters, 0);
}
/// A custom test for SPS parsing with data manually extracted from
/// GStreamer using GDB.
#[test]
fn test25fps_sps_header_parsing() {
let mut parser = Parser::default();
let sps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::SpsNut, 0).unwrap();
let sps = parser.parse_sps(&sps_nalu).unwrap();
assert_eq!(sps.max_sub_layers_minus1, 0);
assert_eq!(sps.profile_tier_level.general_profile_space, 0);
assert!(!sps.profile_tier_level.general_tier_flag);
assert_eq!(sps.profile_tier_level.general_profile_idc, 1);
for i in 0..32 {
let val = i == 1 || i == 2;
assert_eq!(
sps.profile_tier_level.general_profile_compatibility_flag[i],
val
);
}
assert!(sps.profile_tier_level.general_progressive_source_flag);
assert!(!sps.profile_tier_level.general_interlaced_source_flag);
assert!(!sps.profile_tier_level.general_non_packed_constraint_flag,);
assert!(sps.profile_tier_level.general_frame_only_constraint_flag,);
assert!(!sps.profile_tier_level.general_max_12bit_constraint_flag,);
assert!(!sps.profile_tier_level.general_max_10bit_constraint_flag,);
assert!(!sps.profile_tier_level.general_max_8bit_constraint_flag,);
assert!(!sps.profile_tier_level.general_max_422chroma_constraint_flag,);
assert!(!sps.profile_tier_level.general_max_420chroma_constraint_flag,);
assert!(
!sps.profile_tier_level
.general_max_monochrome_constraint_flag,
);
assert!(!sps.profile_tier_level.general_intra_constraint_flag);
assert!(
!sps.profile_tier_level
.general_one_picture_only_constraint_flag,
);
assert!(
!sps.profile_tier_level
.general_lower_bit_rate_constraint_flag,
);
assert!(!sps.profile_tier_level.general_max_14bit_constraint_flag,);
assert_eq!(sps.profile_tier_level.general_level_idc, Level::L2);
assert_eq!(sps.seq_parameter_set_id, 0);
assert_eq!(sps.chroma_format_idc, 1);
assert!(!sps.separate_colour_plane_flag);
assert_eq!(sps.pic_width_in_luma_samples, 320);
assert_eq!(sps.pic_height_in_luma_samples, 240);
assert_eq!(sps.conf_win_left_offset, 0);
assert_eq!(sps.conf_win_right_offset, 0);
assert_eq!(sps.conf_win_top_offset, 0);
assert_eq!(sps.conf_win_bottom_offset, 0);
assert_eq!(sps.bit_depth_luma_minus8, 0);
assert_eq!(sps.bit_depth_chroma_minus8, 0);
assert_eq!(sps.log2_max_pic_order_cnt_lsb_minus4, 4);
assert!(sps.sub_layer_ordering_info_present_flag);
assert_eq!(sps.max_dec_pic_buffering_minus1[0], 4);
assert_eq!(sps.max_num_reorder_pics[0], 2);
assert_eq!(sps.max_latency_increase_plus1[0], 5);
for i in 1..7 {
assert_eq!(sps.max_dec_pic_buffering_minus1[i], 0);
assert_eq!(sps.max_num_reorder_pics[i], 0);
assert_eq!(sps.max_latency_increase_plus1[i], 0);
}
assert_eq!(sps.log2_min_luma_coding_block_size_minus3, 0);
assert_eq!(sps.log2_diff_max_min_luma_coding_block_size, 3);
assert_eq!(sps.log2_min_luma_transform_block_size_minus2, 0);
assert_eq!(sps.log2_diff_max_min_luma_transform_block_size, 3);
assert_eq!(sps.max_transform_hierarchy_depth_inter, 0);
assert_eq!(sps.max_transform_hierarchy_depth_intra, 0);
assert!(!sps.scaling_list_enabled_flag);
assert!(!sps.scaling_list_data_present_flag);
assert!(!sps.amp_enabled_flag);
assert!(sps.sample_adaptive_offset_enabled_flag);
assert!(!sps.pcm_enabled_flag);
assert_eq!(sps.pcm_sample_bit_depth_luma_minus1, 0);
assert_eq!(sps.pcm_sample_bit_depth_chroma_minus1, 0);
assert_eq!(sps.log2_min_pcm_luma_coding_block_size_minus3, 0);
assert_eq!(sps.log2_diff_max_min_pcm_luma_coding_block_size, 0);
assert!(!sps.pcm_loop_filter_disabled_flag);
assert_eq!(sps.num_short_term_ref_pic_sets, 0);
assert_eq!(sps.num_long_term_ref_pics_sps, 0);
assert!(sps.temporal_mvp_enabled_flag);
assert!(sps.strong_intra_smoothing_enabled_flag);
assert_eq!(sps.vui_parameters.sar_width, 0);
assert_eq!(sps.vui_parameters.sar_height, 0);
assert!(!sps.vui_parameters.video_full_range_flag);
assert!(!sps.vui_parameters.colour_description_present_flag);
assert!(sps.vui_parameters.video_signal_type_present_flag);
assert!(sps.vui_parameters.timing_info_present_flag);
assert_eq!(sps.vui_parameters.num_units_in_tick, 1);
assert_eq!(sps.vui_parameters.time_scale, 25);
assert!(!sps.vui_parameters.poc_proportional_to_timing_flag);
assert_eq!(sps.vui_parameters.num_ticks_poc_diff_one_minus1, 0);
assert!(!sps.vui_parameters.hrd_parameters_present_flag);
assert_eq!(sps.vui_parameters.colour_primaries, 2);
assert_eq!(sps.vui_parameters.transfer_characteristics, 2);
assert_eq!(sps.vui_parameters.matrix_coeffs, 2);
assert_eq!(sps.vui_parameters.def_disp_win_left_offset, 0);
assert_eq!(sps.vui_parameters.def_disp_win_right_offset, 0);
assert_eq!(sps.vui_parameters.def_disp_win_top_offset, 0);
assert_eq!(sps.vui_parameters.def_disp_win_bottom_offset, 0);
}
/// A custom test for PPS parsing with data manually extracted from
/// GStreamer using GDB.
#[test]
fn test25fps_pps_header_parsing() {
let mut parser = Parser::default();
let sps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::SpsNut, 0).unwrap();
parser.parse_sps(&sps_nalu).unwrap();
let pps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::PpsNut, 0).unwrap();
let pps = parser.parse_pps(&pps_nalu).unwrap();
assert!(!pps.dependent_slice_segments_enabled_flag);
assert!(!pps.output_flag_present_flag);
assert_eq!(pps.num_extra_slice_header_bits, 0);
assert!(pps.sign_data_hiding_enabled_flag);
assert!(!pps.cabac_init_present_flag);
assert_eq!(pps.num_ref_idx_l0_default_active_minus1, 0);
assert_eq!(pps.num_ref_idx_l1_default_active_minus1, 0);
assert_eq!(pps.init_qp_minus26, 0);
assert!(!pps.constrained_intra_pred_flag);
assert!(!pps.transform_skip_enabled_flag);
assert!(pps.cu_qp_delta_enabled_flag);
assert_eq!(pps.diff_cu_qp_delta_depth, 1);
assert_eq!(pps.cb_qp_offset, 0);
assert_eq!(pps.cr_qp_offset, 0);
assert!(!pps.slice_chroma_qp_offsets_present_flag);
assert!(pps.weighted_pred_flag);
assert!(!pps.weighted_bipred_flag);
assert!(!pps.transquant_bypass_enabled_flag);
assert!(!pps.tiles_enabled_flag);
assert!(pps.entropy_coding_sync_enabled_flag);
assert_eq!(pps.num_tile_rows_minus1, 0);
assert_eq!(pps.num_tile_columns_minus1, 0);
assert!(pps.uniform_spacing_flag);
assert_eq!(pps.column_width_minus1, [0; 19]);
assert_eq!(pps.row_height_minus1, [0; 21]);
assert!(pps.loop_filter_across_slices_enabled_flag);
assert!(pps.loop_filter_across_tiles_enabled_flag);
assert!(!pps.deblocking_filter_control_present_flag);
assert!(!pps.deblocking_filter_override_enabled_flag);
assert!(!pps.deblocking_filter_disabled_flag);
assert_eq!(pps.beta_offset_div2, 0);
assert_eq!(pps.tc_offset_div2, 0);
assert!(!pps.lists_modification_present_flag);
assert_eq!(pps.log2_parallel_merge_level_minus2, 0);
assert!(!pps.slice_segment_header_extension_present_flag);
assert!(!pps.extension_present_flag);
}
/// A custom test for slice header parsing with data manually extracted from
/// GStreamer using GDB.
#[test]
fn test25fps_slice_header_parsing() {
let mut parser = Parser::default();
// Have to parse the SPS/VPS/PPS to set up the parser's internal state.
let vps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::VpsNut, 0).unwrap();
parser.parse_vps(&vps_nalu).unwrap();
let sps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::SpsNut, 0).unwrap();
parser.parse_sps(&sps_nalu).unwrap();
let pps_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::PpsNut, 0).unwrap();
parser.parse_pps(&pps_nalu).unwrap();
let slice_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::IdrNLp, 0).unwrap();
let slice = parser.parse_slice_header(slice_nalu).unwrap();
let hdr = &slice.header;
assert!(hdr.first_slice_segment_in_pic_flag);
assert!(!hdr.no_output_of_prior_pics_flag);
assert!(!hdr.dependent_slice_segment_flag);
assert_eq!(hdr.type_, SliceType::I);
assert!(hdr.pic_output_flag);
assert_eq!(hdr.colour_plane_id, 0);
assert_eq!(hdr.pic_order_cnt_lsb, 0);
assert!(!hdr.short_term_ref_pic_set_sps_flag);
assert_eq!(hdr.lt_idx_sps, [0; 16]);
assert_eq!(hdr.poc_lsb_lt, [0; 16]);
assert_eq!(hdr.used_by_curr_pic_lt, [false; 16]);
assert_eq!(hdr.delta_poc_msb_cycle_lt, [0; 16]);
assert_eq!(hdr.delta_poc_msb_present_flag, [false; 16]);
assert!(!hdr.temporal_mvp_enabled_flag);
assert!(hdr.sao_luma_flag);
assert!(hdr.sao_chroma_flag);
assert!(!hdr.num_ref_idx_active_override_flag);
assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0);
assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0);
assert!(!hdr.cabac_init_flag);
assert!(hdr.collocated_from_l0_flag);
assert_eq!(hdr.five_minus_max_num_merge_cand, 0);
assert!(!hdr.use_integer_mv_flag);
assert_eq!(hdr.qp_delta, 7);
assert_eq!(hdr.cb_qp_offset, 0);
assert_eq!(hdr.cr_qp_offset, 0);
assert!(!hdr.cu_chroma_qp_offset_enabled_flag);
assert!(!hdr.deblocking_filter_override_flag);
assert!(!hdr.deblocking_filter_override_flag);
assert_eq!(hdr.beta_offset_div2, 0);
assert_eq!(hdr.tc_offset_div2, 0);
assert!(hdr.loop_filter_across_slices_enabled_flag);
assert_eq!(hdr.num_entry_point_offsets, 3);
assert_eq!(hdr.offset_len_minus1, 11);
assert_eq!(hdr.num_pic_total_curr, 0);
// Remove the 2 bytes from the NALU header.
assert_eq!(hdr.header_bit_size - 16, 72);
assert_eq!(hdr.n_emulation_prevention_bytes, 0);
assert_eq!(slice.nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_0);
// Next slice
let slice_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::TrailR, 0).unwrap();
let slice = parser.parse_slice_header(slice_nalu).unwrap();
let hdr = &slice.header;
assert!(hdr.first_slice_segment_in_pic_flag);
assert!(!hdr.no_output_of_prior_pics_flag);
assert!(!hdr.dependent_slice_segment_flag);
assert_eq!(hdr.type_, SliceType::P);
assert!(hdr.pic_output_flag);
assert_eq!(hdr.colour_plane_id, 0);
assert_eq!(hdr.pic_order_cnt_lsb, 3);
assert!(!hdr.short_term_ref_pic_set_sps_flag);
assert_eq!(hdr.short_term_ref_pic_set.num_delta_pocs, 1);
assert_eq!(hdr.short_term_ref_pic_set.num_negative_pics, 1);
assert_eq!(hdr.short_term_ref_pic_set.num_positive_pics, 0);
assert!(hdr.short_term_ref_pic_set.used_by_curr_pic_s0[0]);
assert_eq!(hdr.short_term_ref_pic_set.delta_poc_s0[0], -3);
assert_eq!(hdr.lt_idx_sps, [0; 16]);
assert_eq!(hdr.poc_lsb_lt, [0; 16]);
assert_eq!(hdr.used_by_curr_pic_lt, [false; 16]);
assert_eq!(hdr.delta_poc_msb_cycle_lt, [0; 16]);
assert_eq!(hdr.delta_poc_msb_present_flag, [false; 16]);
assert!(hdr.temporal_mvp_enabled_flag);
assert!(hdr.sao_luma_flag);
assert!(hdr.sao_chroma_flag);
assert!(!hdr.num_ref_idx_active_override_flag);
assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0);
assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0);
assert!(!hdr.cabac_init_flag);
assert!(hdr.collocated_from_l0_flag);
assert_eq!(hdr.pred_weight_table.luma_log2_weight_denom, 7);
assert_eq!(hdr.five_minus_max_num_merge_cand, 2);
assert!(!hdr.use_integer_mv_flag);
assert_eq!(hdr.num_entry_point_offsets, 3);
assert_eq!(hdr.qp_delta, 7);
assert_eq!(hdr.cb_qp_offset, 0);
assert_eq!(hdr.cr_qp_offset, 0);
assert!(!hdr.cu_chroma_qp_offset_enabled_flag);
assert!(!hdr.deblocking_filter_override_flag);
assert!(!hdr.deblocking_filter_override_flag);
assert_eq!(hdr.beta_offset_div2, 0);
assert_eq!(hdr.tc_offset_div2, 0);
assert!(!hdr.loop_filter_across_slices_enabled_flag);
assert_eq!(hdr.num_entry_point_offsets, 3);
assert_eq!(hdr.offset_len_minus1, 10);
assert_eq!(hdr.num_pic_total_curr, 1);
assert_eq!(slice.nalu.size, 2983);
// Subtract 2 bytes to account for the header size.
assert_eq!(hdr.header_bit_size - 16, 96);
assert_eq!(slice.nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_1);
// Next slice
let slice_nalu = find_nalu_by_type(STREAM_TEST25FPS, NaluType::TrailR, 1).unwrap();
let slice = parser.parse_slice_header(slice_nalu).unwrap();
let hdr = &slice.header;
assert_eq!(slice.nalu.size, 290);
// Subtract 2 bytes to account for the header size.
assert_eq!(hdr.header_bit_size - 16, 80);
}
}