| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Can't reasonably expect client code to consume everything that has been parsed. |
| #![allow(dead_code)] |
| |
| use std::collections::BTreeMap; |
| use std::io::Cursor; |
| use std::io::Read; |
| use std::io::Seek; |
| use std::io::SeekFrom; |
| use std::rc::Rc; |
| |
| use crate::bitstream_utils::BitReader; |
| use crate::codec::h264::nalu; |
| use crate::codec::h264::nalu::Header; |
| use crate::codec::h264::picture::Field; |
| |
| pub type Nalu<'a> = nalu::Nalu<'a, NaluHeader>; |
| |
| pub(super) const DEFAULT_4X4_INTRA: [u8; 16] = [ |
| 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42, |
| ]; |
| |
| pub(super) const DEFAULT_4X4_INTER: [u8; 16] = [ |
| 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34, |
| ]; |
| |
| pub(super) const DEFAULT_8X8_INTRA: [u8; 64] = [ |
| 6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23, 23, 23, 23, 23, 23, 25, 25, 25, |
| 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, |
| 31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42, |
| ]; |
| |
| pub(super) const DEFAULT_8X8_INTER: [u8; 64] = [ |
| 9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21, 21, 21, 21, 21, 21, 22, 22, 22, |
| 22, 22, 22, 22, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, |
| 27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35, |
| ]; |
| |
| const MAX_PPS_COUNT: u16 = 256; |
| const MAX_SPS_COUNT: u8 = 32; |
| |
| /// The maximum number of pictures in the DPB, as per A.3.1, clause h) |
| const DPB_MAX_SIZE: usize = 16; |
| |
| #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] |
| pub struct Point<T> { |
| pub x: T, |
| pub y: T, |
| } |
| |
| #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] |
| pub struct Rect<T> { |
| pub min: Point<T>, |
| pub max: Point<T>, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| pub enum NaluType { |
| Unknown = 0, |
| Slice = 1, |
| SliceDpa = 2, |
| SliceDpb = 3, |
| SliceDpc = 4, |
| SliceIdr = 5, |
| Sei = 6, |
| Sps = 7, |
| Pps = 8, |
| AuDelimiter = 9, |
| SeqEnd = 10, |
| StreamEnd = 11, |
| FillerData = 12, |
| SpsExt = 13, |
| PrefixUnit = 14, |
| SubsetSps = 15, |
| DepthSps = 16, |
| SliceAux = 19, |
| SliceExt = 20, |
| SliceDepth = 21, |
| } |
| |
| impl TryFrom<u8> for NaluType { |
| type Error = String; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 0 => Ok(NaluType::Unknown), |
| 1 => Ok(NaluType::Slice), |
| 2 => Ok(NaluType::SliceDpa), |
| 3 => Ok(NaluType::SliceDpb), |
| 4 => Ok(NaluType::SliceDpc), |
| 5 => Ok(NaluType::SliceIdr), |
| 6 => Ok(NaluType::Sei), |
| 7 => Ok(NaluType::Sps), |
| 8 => Ok(NaluType::Pps), |
| 9 => Ok(NaluType::AuDelimiter), |
| 10 => Ok(NaluType::SeqEnd), |
| 11 => Ok(NaluType::StreamEnd), |
| 12 => Ok(NaluType::FillerData), |
| 13 => Ok(NaluType::SpsExt), |
| 14 => Ok(NaluType::PrefixUnit), |
| 15 => Ok(NaluType::SubsetSps), |
| 16 => Ok(NaluType::DepthSps), |
| 19 => Ok(NaluType::SliceAux), |
| 20 => Ok(NaluType::SliceExt), |
| 21 => Ok(NaluType::SliceDepth), |
| _ => Err(format!("Invalid NaluType {}", value)), |
| } |
| } |
| } |
| |
| #[derive(Clone, Debug, Default, PartialEq, Eq)] |
| pub struct RefPicListModification { |
| pub modification_of_pic_nums_idc: u8, |
| /* if modification_of_pic_nums_idc == 0 || 1 */ |
| pub abs_diff_pic_num_minus1: u32, |
| /* if modification_of_pic_nums_idc == 2 */ |
| pub long_term_pic_num: u32, |
| /* if modification_of_pic_nums_idc == 4 || 5 */ |
| pub abs_diff_view_idx_minus1: u32, |
| } |
| |
| #[derive(Clone, Debug, Default, PartialEq, Eq)] |
| pub struct PredWeightTable { |
| pub luma_log2_weight_denom: u8, |
| pub chroma_log2_weight_denom: u8, |
| |
| pub luma_weight_l0: [i16; 32], |
| pub luma_offset_l0: [i8; 32], |
| |
| /* if seq->ChromaArrayType != 0 */ |
| pub chroma_weight_l0: [[i16; 2]; 32], |
| pub chroma_offset_l0: [[i8; 2]; 32], |
| |
| /* if slice->slice_type % 5 == 1 */ |
| pub luma_weight_l1: [i16; 32], |
| pub luma_offset_l1: [i16; 32], |
| |
| /* and if seq->ChromaArrayType != 0 */ |
| pub chroma_weight_l1: [[i16; 2]; 32], |
| pub chroma_offset_l1: [[i8; 2]; 32], |
| } |
| |
| /// Representation of `MaxLongTermFrameIdx`. |
| /// |
| /// `MaxLongTermFrameIdx` is derived from `max_long_term_frame_idx_plus1`, an unsigned integer with |
| /// a special value indicating "no long-term frame indices". This type allows easy conversion |
| /// between the actual and "plus1" representation, while ensuring that the special value is always |
| /// handled by the code. |
| #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] |
| pub enum MaxLongTermFrameIdx { |
| #[default] |
| NoLongTermFrameIndices, |
| Idx(u32), |
| } |
| |
| impl MaxLongTermFrameIdx { |
| /// Create a value from `max_long_term_frame_idx_plus1`. |
| pub fn from_value_plus1(max_long_term_frame_idx_plus1: u32) -> Self { |
| match max_long_term_frame_idx_plus1 { |
| 0 => Self::NoLongTermFrameIndices, |
| i @ 1.. => Self::Idx(i - 1), |
| } |
| } |
| |
| /// Convert this value to the representation used by `max_long_term_frame_idx_plus1`. |
| pub fn to_value_plus1(self) -> u32 { |
| match self { |
| Self::NoLongTermFrameIndices => 0, |
| Self::Idx(i) => i + 1, |
| } |
| } |
| } |
| |
| impl PartialEq<u32> for MaxLongTermFrameIdx { |
| fn eq(&self, other: &u32) -> bool { |
| match self { |
| MaxLongTermFrameIdx::NoLongTermFrameIndices => false, |
| MaxLongTermFrameIdx::Idx(idx) => idx.eq(other), |
| } |
| } |
| } |
| |
| impl PartialOrd<u32> for MaxLongTermFrameIdx { |
| fn partial_cmp(&self, other: &u32) -> Option<std::cmp::Ordering> { |
| match self { |
| MaxLongTermFrameIdx::NoLongTermFrameIndices => Some(std::cmp::Ordering::Less), |
| MaxLongTermFrameIdx::Idx(idx) => Some(idx.cmp(other)), |
| } |
| } |
| } |
| |
| #[derive(Clone, Debug, Default, PartialEq, Eq)] |
| pub struct RefPicMarkingInner { |
| /// Specifies a control operation to be applied to affect the reference |
| /// picture marking. The `memory_management_control_operation` syntax element |
| /// is followed by data necessary for the operation specified by the value |
| /// of `memory_management_control_operation`. The values and control |
| /// operations associated with `memory_management_control_operation` are |
| /// specified in Table 7-9 |
| pub memory_management_control_operation: u8, |
| |
| /// Used (with memory_management_control_operation equal to 3 or 1) to |
| /// assign a long-term frame index to a short-term reference picture or to |
| /// mark a short-term reference picture as "unused for reference". |
| pub difference_of_pic_nums_minus1: u32, |
| |
| /// Used (with memory_management_control_operation equal to 2) to mark a |
| /// long-term reference picture as "unused for reference". |
| pub long_term_pic_num: u32, |
| |
| /// Used (with memory_management_control_operation equal to 3 or 6) to |
| /// assign a long-term frame index to a picture. |
| pub long_term_frame_idx: u32, |
| |
| /// Specifies the maximum value of long-term frame index allowed for |
| /// long-term reference pictures (until receipt of another value of |
| /// `max_long_term_frame_idx_plus1`). |
| pub max_long_term_frame_idx: MaxLongTermFrameIdx, |
| } |
| |
| #[derive(Clone, Debug, Default, PartialEq, Eq)] |
| pub struct RefPicMarking { |
| /// Specifies how the previously-decoded pictures in the decoded picture |
| /// buffer are treated after decoding of an IDR picture. See Annex C. |
| pub no_output_of_prior_pics_flag: bool, |
| |
| /// If unset, specifies that the MaxLongTermFrameIdx variable is set equal |
| /// to "no long-term frame indices" and that the IDR picture is marked as |
| /// "used for short-term reference". If set, specifies that the |
| /// MaxLongTermFrameIdx variable is set equal to 0 and that the current IDR |
| /// picture is marked "used for long-term reference" and is assigned |
| /// LongTermFrameIdx equal to 0. |
| pub long_term_reference_flag: bool, |
| |
| /// Selects the reference picture marking mode of the currently decoded |
| /// picture as specified in Table 7-8. |
| pub adaptive_ref_pic_marking_mode_flag: bool, |
| |
| /// An Vec with additional data used in the marking process. |
| pub inner: Vec<RefPicMarkingInner>, |
| } |
| |
| #[derive(Clone, Debug, Default, PartialEq, Eq)] |
| pub struct SliceHeader { |
| /// Specifies the address of the first macroblock in the slice. |
| pub first_mb_in_slice: u32, |
| |
| /// Specifies the coding type of the slice according to Table 7-6. |
| pub slice_type: SliceType, |
| |
| // Specifies the picture parameter set in use |
| pub pic_parameter_set_id: u8, |
| |
| /// Specifies the colour plane associated with the current slice RBSP when |
| /// `separate_colour_plane_flag` is set. |
| pub colour_plane_id: u8, |
| |
| /// Used as an identifier for pictures and shall be represented by |
| /// `log2_max_frame_num_minus4 + 4` bits in the bitstream. |
| pub frame_num: u16, |
| |
| /// If set, specifies that the slice is a slice of a coded field. If not |
| /// set, specifies that the slice is a slice of a coded frame. |
| pub field_pic_flag: bool, |
| |
| /// If set, specifies that the slice is part of a coded bottom field. If not |
| /// set, specifies that the picture is a coded top field. |
| pub bottom_field_flag: bool, |
| |
| /// Identifies an IDR picture. The values of `idr_pic_id` in all the slices |
| /// of an IDR picture shall remain unchanged. When two consecutive access |
| /// units in decoding order are both IDR access units, the value of |
| /// `idr_pic_id` in the slices of the first such IDR access unit shall |
| /// differ from the `idr_pic_id` in the second such IDR access unit |
| pub idr_pic_id: u16, |
| |
| /// Specifies the picture order count modulo `MaxPicOrderCntLsb` for the top |
| /// field of a coded frame or for a coded field. The length of the |
| /// `pic_order_cnt_lsb` syntax element is |
| /// `log2_max_pic_order_cnt_lsb_minus4` + 4 bits. |
| pub pic_order_cnt_lsb: u16, |
| |
| /// Specifies the picture order count difference between the bottom field |
| /// and the top field of a coded frame as follows |
| pub delta_pic_order_cnt_bottom: i32, |
| |
| /// The first entry specifies the picture order count difference from the |
| /// expected picture order count for the top field of a coded frame or for a |
| /// coded field as specified in clause 8.2.1 The second entry specifies the |
| /// picture order count difference from the expected picture order count for |
| /// the bottom field of a coded frame specified in clause 8.2.1. |
| pub delta_pic_order_cnt: [i32; 2], |
| |
| /// This value is required by V4L2 stateless decode params so it is calculated |
| /// by parser while processing slice header. |
| pub pic_order_cnt_bit_size: usize, |
| |
| /// Shall be equal to 0 for slices and slice data partitions belonging to |
| /// the primary coded picture. The value of `redundant_pic_cnt shall` be |
| /// greater than 0 for coded slices or coded slice data partitions of a |
| /// redundant coded picture |
| pub redundant_pic_cnt: u8, |
| |
| /// Specifies the method used in the decoding process to derive motion |
| /// vectors and reference indices for inter prediction > |
| pub direct_spatial_mv_pred_flag: bool, |
| |
| /// If set, specifies that the syntax element `num_ref_idx_l0_active_minus1` |
| /// is present for P, SP, and B slices and that the syntax element |
| /// `num_ref_idx_l1_active_minus1` is present for B slices. If 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 |
| /// shall 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 |
| /// shall be used to decode the slice. |
| pub num_ref_idx_l1_active_minus1: u8, |
| |
| /// If set, specifies that the syntax element `modification_of_pic_nums_idc` |
| /// is present for specifying reference picture list 0. If not set, |
| /// specifies that this syntax element is not present. |
| pub ref_pic_list_modification_flag_l0: bool, |
| |
| /// Reference picture list 0 modification as parsed with the |
| /// `ref_pic_list_modification()` process. |
| pub ref_pic_list_modification_l0: Vec<RefPicListModification>, |
| |
| /// If set, specifies that the syntax element `modification_of_pic_nums_idc` |
| /// is present for specifying reference picture list 1. If not set, |
| /// specifies that this syntax element is not present. |
| pub ref_pic_list_modification_flag_l1: bool, |
| |
| /// Reference picture list 1 modification as parsed with the |
| /// `ref_pic_list_modification()` process. |
| pub ref_pic_list_modification_l1: Vec<RefPicListModification>, |
| |
| /// Prediction weight table as parsed using 7.3.3.2 |
| pub pred_weight_table: PredWeightTable, |
| |
| /// Decoded reference picture marking parsed using 7.3.3.3 |
| pub dec_ref_pic_marking: RefPicMarking, |
| |
| /// This value is required by V4L2 stateless decode params so it is calculated |
| /// by parser while processing slice header. |
| pub dec_ref_pic_marking_bit_size: usize, |
| |
| /// Specifies the index for determining the initialization table used in the |
| /// initialization process for context variables. |
| pub cabac_init_idc: u8, |
| |
| /// Specifies the initial value of QP Y to be used for all the macroblocks |
| /// in the slice until modified by the value of `mb_qp_delta` in the |
| /// macroblock layer. The initial QPY quantization parameter for the slice |
| /// is computed using 7-30. |
| pub slice_qp_delta: i8, |
| |
| /// Specifies the decoding process to be used to decode P macroblocks in an |
| /// SP slice. |
| pub sp_for_switch_flag: bool, |
| |
| /// Specifies the value of QSY for all the macroblocks in SP and SI slices. |
| /// The QSY quantization parameter for the slice is computed using 7-31. |
| pub slice_qs_delta: i8, |
| |
| /// Specifies whether the operation of the deblocking filter shall be |
| /// disabled across some block edges of the slice and specifies for which |
| /// edges the filtering is disabled. |
| pub disable_deblocking_filter_idc: u8, |
| |
| /// Specifies the offset used in accessing the α and tC0 deblocking filter |
| /// tables for filtering operations controlled by the macroblocks within the |
| /// slice. From this value, the offset that shall be applied when addressing |
| /// these tables shall be computed using 7-32. |
| pub slice_alpha_c0_offset_div2: i8, |
| |
| /// Specifies the offset used in accessing the β deblocking filter table for |
| /// filtering operations controlled by the macroblocks within the slice. |
| /// From this value, the offset that is applied when addressing the β table |
| /// of the deblocking filter shall be computed using 7-33. |
| pub slice_beta_offset_div2: i8, |
| |
| /// Same as `MaxPicNum` in the specification. |
| pub max_pic_num: u32, |
| |
| /// Size of the slice_header() in bits |
| pub header_bit_size: usize, |
| |
| /// Number of emulation prevention bytes (EPB) in this slice_header() |
| pub n_emulation_prevention_bytes: usize, |
| } |
| |
| impl SliceHeader { |
| /// Returns the field that is coded by this header. |
| pub fn field(&self) -> Field { |
| if self.field_pic_flag { |
| if self.bottom_field_flag { |
| Field::Bottom |
| } else { |
| Field::Top |
| } |
| } else { |
| Field::Frame |
| } |
| } |
| } |
| |
| pub struct SliceHeaderBuilder(SliceHeader); |
| |
| impl SliceHeaderBuilder { |
| pub fn new(pps: &Pps) -> Self { |
| SliceHeaderBuilder(SliceHeader { |
| pic_parameter_set_id: pps.pic_parameter_set_id, |
| ..Default::default() |
| }) |
| } |
| |
| pub fn slice_type(mut self, type_: SliceType) -> Self { |
| self.0.slice_type = type_; |
| self |
| } |
| |
| pub fn first_mb_in_slice(mut self, value: u32) -> Self { |
| self.0.first_mb_in_slice = value; |
| self |
| } |
| |
| pub fn pic_order_cnt_lsb(mut self, value: u16) -> Self { |
| self.0.pic_order_cnt_lsb = value; |
| self |
| } |
| |
| pub fn idr_pic_id(mut self, value: u16) -> Self { |
| self.0.idr_pic_id = value; |
| self |
| } |
| |
| pub fn num_ref_idx_active_override_flag(mut self, value: bool) -> Self { |
| self.0.num_ref_idx_active_override_flag = value; |
| self |
| } |
| |
| pub fn num_ref_idx_l0_active_minus1(mut self, value: u8) -> Self { |
| self = self.num_ref_idx_active_override_flag(true); |
| self.0.num_ref_idx_l0_active_minus1 = value; |
| self |
| } |
| |
| pub fn num_ref_idx_l0_active(self, value: u8) -> Self { |
| self.num_ref_idx_l0_active_minus1(value - 1) |
| } |
| |
| pub fn num_ref_idx_l1_active_minus1(mut self, value: u8) -> Self { |
| self = self.num_ref_idx_active_override_flag(true); |
| self.0.num_ref_idx_l1_active_minus1 = value; |
| self |
| } |
| |
| pub fn num_ref_idx_l1_active(self, value: u8) -> Self { |
| self.num_ref_idx_l1_active_minus1(value - 1) |
| } |
| |
| pub fn build(self) -> SliceHeader { |
| self.0 |
| } |
| } |
| |
| /// A H264 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>, |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| /// See table 7-6 in the specification. |
| pub enum SliceType { |
| P = 0, |
| B = 1, |
| I = 2, |
| Sp = 3, |
| Si = 4, |
| } |
| |
| impl TryFrom<u8> for SliceType { |
| type Error = String; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 0 => Ok(SliceType::P), |
| 1 => Ok(SliceType::B), |
| 2 => Ok(SliceType::I), |
| 3 => Ok(SliceType::Sp), |
| 4 => Ok(SliceType::Si), |
| _ => Err(format!("Invalid SliceType {}", value)), |
| } |
| } |
| } |
| |
| impl SliceType { |
| /// Whether this is a P slice. See table 7-6 in the specification. |
| pub fn is_p(&self) -> bool { |
| matches!(self, SliceType::P) |
| } |
| |
| /// Whether this is a B slice. See table 7-6 in the specification. |
| pub fn is_b(&self) -> bool { |
| matches!(self, SliceType::B) |
| } |
| |
| /// Whether this is an I slice. See table 7-6 in the specification. |
| pub fn is_i(&self) -> bool { |
| matches!(self, SliceType::I) |
| } |
| |
| /// Whether this is a SP slice. See table 7-6 in the specification. |
| pub fn is_sp(&self) -> bool { |
| matches!(self, SliceType::Sp) |
| } |
| |
| /// Whether this is a SI slice. See table 7-6 in the specification. |
| pub fn is_si(&self) -> bool { |
| matches!(self, SliceType::Si) |
| } |
| } |
| |
| impl Default for SliceType { |
| fn default() -> Self { |
| Self::P |
| } |
| } |
| |
| #[derive(Clone, Copy)] |
| #[repr(u8)] |
| pub enum Profile { |
| Baseline = 66, |
| Main = 77, |
| Extended = 88, |
| High = 100, |
| High10 = 110, |
| High422P = 122, |
| } |
| |
| impl TryFrom<u8> for Profile { |
| type Error = String; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 66 => Ok(Profile::Baseline), |
| 77 => Ok(Profile::Main), |
| 88 => Ok(Profile::Extended), |
| 100 => Ok(Profile::High), |
| 110 => Ok(Profile::High10), |
| 122 => Ok(Profile::High422P), |
| _ => Err(format!("Invalid Profile {}", value)), |
| } |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] |
| pub enum Level { |
| #[default] |
| L1 = 10, |
| L1B = 9, |
| L1_1 = 11, |
| L1_2 = 12, |
| L1_3 = 13, |
| L2_0 = 20, |
| L2_1 = 21, |
| L2_2 = 22, |
| L3 = 30, |
| L3_1 = 31, |
| L3_2 = 32, |
| L4 = 40, |
| L4_1 = 41, |
| L4_2 = 42, |
| L5 = 50, |
| L5_1 = 51, |
| L5_2 = 52, |
| L6 = 60, |
| L6_1 = 61, |
| L6_2 = 62, |
| } |
| |
| impl TryFrom<u8> for Level { |
| type Error = String; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 10 => Ok(Level::L1), |
| 9 => Ok(Level::L1B), |
| 11 => Ok(Level::L1_1), |
| 12 => Ok(Level::L1_2), |
| 13 => Ok(Level::L1_3), |
| 20 => Ok(Level::L2_0), |
| 21 => Ok(Level::L2_1), |
| 22 => Ok(Level::L2_2), |
| 30 => Ok(Level::L3), |
| 31 => Ok(Level::L3_1), |
| 32 => Ok(Level::L3_2), |
| 40 => Ok(Level::L4), |
| 41 => Ok(Level::L4_1), |
| 42 => Ok(Level::L4_2), |
| 50 => Ok(Level::L5), |
| 51 => Ok(Level::L5_1), |
| 52 => Ok(Level::L5_2), |
| 60 => Ok(Level::L6), |
| 61 => Ok(Level::L6_1), |
| 62 => Ok(Level::L6_2), |
| _ => Err(format!("Invalid Level {}", value)), |
| } |
| } |
| } |
| |
| /// A H264 Sequence Parameter Set. A syntax structure containing syntax elements |
| /// that apply to zero or more entire coded video sequences as determined by the |
| /// content of a seq_parameter_set_id syntax element found in the picture |
| /// parameter set referred to by the pic_parameter_set_id syntax element found |
| /// in each slice header. |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct Sps { |
| /// Identifies the sequence parameter set that is referred to by the picture |
| /// parameter set |
| pub seq_parameter_set_id: u8, |
| |
| /// Profile to which the coded video sequence conforms |
| pub profile_idc: u8, |
| |
| /// Retains the same meaning as in the specification. See 7.4.2.1.1 |
| pub constraint_set0_flag: bool, |
| /// Retains the same meaning as in the specification. See 7.4.2.1.1 |
| pub constraint_set1_flag: bool, |
| /// Retains the same meaning as in the specification. See 7.4.2.1.1 |
| pub constraint_set2_flag: bool, |
| /// Retains the same meaning as in the specification. See 7.4.2.1.1 |
| pub constraint_set3_flag: bool, |
| /// Retains the same meaning as in the specification. See 7.4.2.1.1 |
| pub constraint_set4_flag: bool, |
| /// Retains the same meaning as in the specification. See 7.4.2.1.1 |
| pub constraint_set5_flag: bool, |
| |
| /// Level to which the coded video sequence conforms |
| pub level_idc: Level, |
| |
| /// Specifies the chroma sampling relative to the luma sampling as specified |
| /// in clause 6.2. |
| pub chroma_format_idc: u8, |
| |
| /// Specifies whether the three colour components of the 4:4:4 chroma format |
| /// are coded separately. |
| pub separate_colour_plane_flag: bool, |
| |
| /// Specifies the bit depth of the samples of the luma array and the value |
| /// of the luma quantization parameter range offset QpBdOffsetY. See 7-3 and |
| /// 7-4. |
| pub bit_depth_luma_minus8: u8, |
| |
| /// Specifies the bit depth of the samples of the chroma arrays and the |
| /// value of the chroma quantization parameter range offset QpBdOffsetC. See |
| /// 7-5 and 7-6. |
| pub bit_depth_chroma_minus8: u8, |
| |
| /// qpprime_y_zero_transform_bypass_flag equal to 1 specifies that, when |
| /// QP′Y is equal to 0, a transform bypass operation for the transform |
| /// coefficient decoding process and picture construction process prior to |
| /// deblocking filter process as specified in clause 8.5 shall be applied. |
| /// qpprime_y_zero_transform_bypass_flag equal to 0 specifies that the |
| /// transform coefficient decoding process and picture construction process |
| /// prior to deblocking filter process shall not use the transform bypass |
| /// operation |
| /// QP′Y is defined in 7-38 as QP′Y = QPY + QpBdOffsetY |
| pub qpprime_y_zero_transform_bypass_flag: bool, |
| |
| /// Whether `seq_scaling_list_present_flag[i]` for i = 0..7 or i = 0..11 is |
| /// present or whether the sequence level scaling list shall be specified by |
| /// Flat_4x4_16 for i = 0..5 and flat_8x8_16 for i = 6..11 |
| pub seq_scaling_matrix_present_flag: bool, |
| |
| /// 4x4 Scaling list as read with 7.3.2.1.1.1 |
| pub scaling_lists_4x4: [[u8; 16]; 6], |
| /// 8x8 Scaling list as read with 7.3.2.1.1.1 |
| pub scaling_lists_8x8: [[u8; 64]; 6], |
| |
| /// Specifies the value of the variable MaxFrameNum that is used in |
| /// frame_num related derivations as follows: MaxFrameNum = 2 ^ |
| /// (log2_max_frame_num_minus4 + 4 ) |
| pub log2_max_frame_num_minus4: u8, |
| |
| /// Specifies the method to decode picture order count (as specified in |
| /// clause 8.2.1) |
| pub pic_order_cnt_type: u8, |
| |
| /// Specifies the value of the variable MaxPicOrderCntLsb that is used in |
| /// the decoding process for picture order count as specified in clause |
| /// 8.2.1 as follows: MaxPicOrderCntLsb = 2 ^ ( |
| /// log2_max_pic_order_cnt_lsb_minus4 + 4 ). |
| pub log2_max_pic_order_cnt_lsb_minus4: u8, |
| |
| /// If true, specifies that `delta_pic_order_cnt[0]` and |
| /// `delta_pic_order_cnt[1]` are not present in the slice headers of the |
| /// sequence and shall be inferred to be equal to 0. |
| /// If false, specifies that `delta_pic_order_cnt[0]` is present in the |
| /// slice headers of the sequence and `delta_pic_order_cnt[1]` may be |
| /// present in the slice headers of the sequence. |
| pub delta_pic_order_always_zero_flag: bool, |
| |
| /// Used to calculate the picture order count of a non-reference picture as |
| /// specified in clause 8.2.1. |
| pub offset_for_non_ref_pic: i32, |
| |
| /// Used to calculate the picture order count of a bottom field as specified |
| /// in clause 8.2.1. |
| pub offset_for_top_to_bottom_field: i32, |
| |
| /// Used in the decoding process for picture order count as specified in |
| /// clause 8.2.1 |
| pub num_ref_frames_in_pic_order_cnt_cycle: u8, |
| |
| /// An element of a list of num_ref_frames_in_pic_order_cnt_cycle values |
| /// used in the decoding process for picture order count as specified in |
| /// clause 8.2. |
| pub offset_for_ref_frame: [i32; 255], |
| |
| /// Specifies the maximum number of short-term and long-term reference |
| /// frames, complementary reference field pairs, and non-paired reference |
| /// fields that may be used by the decoding process for inter prediction of |
| /// any picture in the coded video sequence. Also |
| /// determines the size of the sliding window operation as specified in |
| /// clause 8.2.5.3. |
| pub max_num_ref_frames: u8, |
| |
| /// Specifies the allowed values of frame_num as specified in clause 7.4.3 |
| /// and the decoding process in case of an inferred gap between values of |
| /// frame_num as specified in clause 8.2.5.2 |
| pub gaps_in_frame_num_value_allowed_flag: bool, |
| |
| /// Plus 1 specifies the width of each decoded picture in units of |
| /// macroblocks. |
| pub pic_width_in_mbs_minus1: u16, |
| /// Plus 1 specifies the height in slice group map units of a decoded frame |
| /// or field. |
| pub pic_height_in_map_units_minus1: u16, |
| |
| /// If true, specifies that every coded picture of the coded video sequence |
| /// is a coded frame containing only frame macroblocks, else specifies that |
| /// coded pictures of the coded video sequence may either be coded fields or |
| /// coded frames. |
| pub frame_mbs_only_flag: bool, |
| |
| /// If true, specifies the possible use of switching between frame and field |
| /// macroblocks within frames, else, specifies no switching between frame |
| /// and field macroblocks within a picture. |
| pub mb_adaptive_frame_field_flag: bool, |
| |
| /// Specifies the method used in the derivation process for luma motion |
| /// vectors for B_Skip, B_Direct_16x16 and B_Direct_8x8 as specified in |
| /// clause 8.4.1.2. |
| pub direct_8x8_inference_flag: bool, |
| |
| /// If true, specifies that the frame cropping offset parameters follow next |
| /// in the sequence parameter, else specifies that the frame cropping offset |
| /// parameters are not present |
| pub frame_cropping_flag: bool, |
| |
| /// Specify the samples of the pictures in the coded video sequence that are |
| /// output from the decoding process, in terms of a rectangular region |
| /// specified in frame coordinates for output. |
| pub frame_crop_left_offset: u32, |
| /// Specify the samples of the pictures in the coded video sequence that are |
| /// output from the decoding process, in terms of a rectangular region |
| /// specified in frame coordinates for output. |
| pub frame_crop_right_offset: u32, |
| /// Specify the samples of the pictures in the coded video sequence that are |
| /// output from the decoding process, in terms of a rectangular region |
| /// specified in frame coordinates for output. |
| pub frame_crop_top_offset: u32, |
| /// Specify the samples of the pictures in the coded video sequence that are |
| /// output from the decoding process, in terms of a rectangular region |
| /// specified in frame coordinates for output. |
| pub frame_crop_bottom_offset: u32, |
| |
| // Calculated |
| /// Same as ExpectedDeltaPerPicOrderCntCycle, see 7-12 in the specification. |
| pub expected_delta_per_pic_order_cnt_cycle: i32, |
| |
| pub vui_parameters_present_flag: bool, |
| pub vui_parameters: VuiParams, |
| } |
| |
| impl Sps { |
| /// Returns the coded width of the stream. |
| /// |
| /// See 7-13 through 7-17 in the specification. |
| pub const fn width(&self) -> u32 { |
| (self.pic_width_in_mbs_minus1 as u32 + 1) * 16 |
| } |
| |
| /// Returns the coded height of the stream. |
| /// |
| /// See 7-13 through 7-17 in the specification. |
| pub const fn height(&self) -> u32 { |
| (self.pic_height_in_map_units_minus1 as u32 + 1) |
| * 16 |
| * (2 - self.frame_mbs_only_flag as u32) |
| } |
| |
| /// Returns `ChromaArrayType`, as computed in the specification. |
| pub const fn chroma_array_type(&self) -> u8 { |
| match self.separate_colour_plane_flag { |
| false => self.chroma_format_idc, |
| true => 0, |
| } |
| } |
| |
| /// Returns `SubWidthC` and `SubHeightC`. |
| /// |
| /// See table 6-1 in the specification. |
| fn sub_width_height_c(&self) -> (u32, u32) { |
| match (self.chroma_format_idc, self.separate_colour_plane_flag) { |
| (1, false) => (2, 2), |
| (2, false) => (2, 1), |
| (3, false) => (1, 1), |
| // undefined. |
| _ => (1, 1), |
| } |
| } |
| |
| /// Returns `CropUnitX` and `CropUnitY`. |
| /// |
| /// See 7-19 through 7-22 in the specification. |
| fn crop_unit_x_y(&self) -> (u32, u32) { |
| match self.chroma_array_type() { |
| 0 => (1, 2 - u32::from(self.frame_mbs_only_flag)), |
| _ => { |
| let (sub_width_c, sub_height_c) = self.sub_width_height_c(); |
| ( |
| sub_width_c, |
| sub_height_c * (2 - u32::from(self.frame_mbs_only_flag)), |
| ) |
| } |
| } |
| } |
| |
| /// Same as MaxFrameNum. See 7-10 in the specification. |
| pub fn max_frame_num(&self) -> u32 { |
| 1 << (self.log2_max_frame_num_minus4 + 4) |
| } |
| |
| pub fn visible_rectangle(&self) -> Rect<u32> { |
| if !self.frame_cropping_flag { |
| return Rect { |
| min: Point { x: 0, y: 0 }, |
| max: Point { |
| x: self.width(), |
| y: self.height(), |
| }, |
| }; |
| } |
| |
| let (crop_unit_x, crop_unit_y) = self.crop_unit_x_y(); |
| |
| let crop_left = crop_unit_x * self.frame_crop_left_offset; |
| let crop_right = crop_unit_x * self.frame_crop_right_offset; |
| let crop_top = crop_unit_y * self.frame_crop_top_offset; |
| let crop_bottom = crop_unit_y * self.frame_crop_bottom_offset; |
| |
| Rect { |
| min: Point { |
| x: crop_left, |
| y: crop_top, |
| }, |
| max: Point { |
| x: self.width() - crop_left - crop_right, |
| y: self.height() - crop_top - crop_bottom, |
| }, |
| } |
| } |
| |
| pub fn max_dpb_frames(&self) -> usize { |
| let profile = self.profile_idc; |
| let mut level = self.level_idc; |
| |
| // A.3.1 and A.3.2: Level 1b for Baseline, Constrained Baseline and Main |
| // profile if level_idc == 11 and constraint_set3_flag == 1 |
| if matches!(level, Level::L1_1) |
| && (profile == Profile::Baseline as u8 || profile == Profile::Main as u8) |
| && self.constraint_set3_flag |
| { |
| level = Level::L1B; |
| }; |
| |
| // Table A.1 |
| let max_dpb_mbs = match level { |
| Level::L1 => 396, |
| Level::L1B => 396, |
| Level::L1_1 => 900, |
| Level::L1_2 => 2376, |
| Level::L1_3 => 2376, |
| Level::L2_0 => 2376, |
| Level::L2_1 => 4752, |
| Level::L2_2 => 8100, |
| Level::L3 => 8100, |
| Level::L3_1 => 18000, |
| Level::L3_2 => 20480, |
| Level::L4 => 32768, |
| Level::L4_1 => 32768, |
| Level::L4_2 => 34816, |
| Level::L5 => 110400, |
| Level::L5_1 => 184320, |
| Level::L5_2 => 184320, |
| Level::L6 => 696320, |
| Level::L6_1 => 696320, |
| Level::L6_2 => 696320, |
| }; |
| |
| let width_mb = self.width() / 16; |
| let height_mb = self.height() / 16; |
| |
| let max_dpb_frames = |
| std::cmp::min(max_dpb_mbs / (width_mb * height_mb), DPB_MAX_SIZE as u32) as usize; |
| |
| let mut max_dpb_frames = std::cmp::max(max_dpb_frames, self.max_num_ref_frames as usize); |
| |
| if self.vui_parameters_present_flag && self.vui_parameters.bitstream_restriction_flag { |
| max_dpb_frames = std::cmp::max(1, self.vui_parameters.max_dec_frame_buffering as usize); |
| } |
| |
| max_dpb_frames |
| } |
| |
| pub fn max_num_order_frames(&self) -> u32 { |
| let vui = &self.vui_parameters; |
| let present = self.vui_parameters_present_flag && vui.bitstream_restriction_flag; |
| |
| if present { |
| vui.max_num_reorder_frames |
| } else { |
| let profile = self.profile_idc; |
| if (profile == 44 |
| || profile == 86 |
| || profile == 100 |
| || profile == 110 |
| || profile == 122 |
| || profile == 244) |
| && self.constraint_set3_flag |
| { |
| 0 |
| } else { |
| self.max_dpb_frames() as u32 |
| } |
| } |
| } |
| } |
| |
| // TODO: Replace with builder |
| impl Default for Sps { |
| fn default() -> Self { |
| Self { |
| scaling_lists_4x4: [[0; 16]; 6], |
| scaling_lists_8x8: [[0; 64]; 6], |
| offset_for_ref_frame: [0; 255], |
| seq_parameter_set_id: Default::default(), |
| profile_idc: Default::default(), |
| constraint_set0_flag: Default::default(), |
| constraint_set1_flag: Default::default(), |
| constraint_set2_flag: Default::default(), |
| constraint_set3_flag: Default::default(), |
| constraint_set4_flag: Default::default(), |
| constraint_set5_flag: Default::default(), |
| level_idc: Default::default(), |
| chroma_format_idc: Default::default(), |
| separate_colour_plane_flag: Default::default(), |
| bit_depth_luma_minus8: Default::default(), |
| bit_depth_chroma_minus8: Default::default(), |
| qpprime_y_zero_transform_bypass_flag: Default::default(), |
| seq_scaling_matrix_present_flag: Default::default(), |
| log2_max_frame_num_minus4: Default::default(), |
| pic_order_cnt_type: Default::default(), |
| log2_max_pic_order_cnt_lsb_minus4: Default::default(), |
| delta_pic_order_always_zero_flag: Default::default(), |
| offset_for_non_ref_pic: Default::default(), |
| offset_for_top_to_bottom_field: Default::default(), |
| num_ref_frames_in_pic_order_cnt_cycle: Default::default(), |
| max_num_ref_frames: Default::default(), |
| gaps_in_frame_num_value_allowed_flag: Default::default(), |
| pic_width_in_mbs_minus1: Default::default(), |
| pic_height_in_map_units_minus1: Default::default(), |
| frame_mbs_only_flag: Default::default(), |
| mb_adaptive_frame_field_flag: Default::default(), |
| direct_8x8_inference_flag: Default::default(), |
| frame_cropping_flag: Default::default(), |
| frame_crop_left_offset: Default::default(), |
| frame_crop_right_offset: Default::default(), |
| frame_crop_top_offset: Default::default(), |
| frame_crop_bottom_offset: Default::default(), |
| expected_delta_per_pic_order_cnt_cycle: Default::default(), |
| vui_parameters_present_flag: Default::default(), |
| vui_parameters: Default::default(), |
| } |
| } |
| } |
| |
| #[derive(Default)] |
| pub struct SpsBuilder(Sps); |
| |
| impl SpsBuilder { |
| pub fn new() -> Self { |
| Default::default() |
| } |
| |
| pub fn seq_parameter_set_id(mut self, value: u8) -> Self { |
| self.0.seq_parameter_set_id = value; |
| self |
| } |
| |
| pub fn profile_idc(mut self, value: Profile) -> Self { |
| self.0.profile_idc = value as u8; |
| self |
| } |
| |
| pub fn level_idc(mut self, value: Level) -> Self { |
| self.0.level_idc = value; |
| self |
| } |
| |
| pub fn frame_crop_offsets(mut self, top: u32, bottom: u32, left: u32, right: u32) -> Self { |
| self.0.frame_cropping_flag = true; |
| self.0.frame_crop_top_offset = top; |
| self.0.frame_crop_bottom_offset = bottom; |
| self.0.frame_crop_left_offset = left; |
| self.0.frame_crop_right_offset = right; |
| self |
| } |
| |
| pub fn frame_crop(self, top: u32, bottom: u32, left: u32, right: u32) -> Self { |
| let sub_width_c = if self.0.chroma_format_idc > 2 { 1 } else { 2 }; |
| let sub_height_c = if self.0.chroma_format_idc > 1 { 1 } else { 2 }; |
| |
| let crop_unit_x = sub_width_c; |
| let crop_unit_y = sub_height_c * (if self.0.frame_mbs_only_flag { 1 } else { 2 }); |
| |
| self.frame_crop_offsets( |
| top / crop_unit_y, |
| bottom / crop_unit_y, |
| left / crop_unit_x, |
| right / crop_unit_x, |
| ) |
| } |
| |
| pub fn resolution(mut self, width: u32, height: u32) -> Self { |
| const MB_SIZE: u32 = 16; |
| |
| let mb_width = (width + MB_SIZE - 1) / MB_SIZE; |
| let mb_height = (height + MB_SIZE - 1) / MB_SIZE; |
| |
| self.0.pic_width_in_mbs_minus1 = (mb_width - 1) as u16; |
| self.0.pic_height_in_map_units_minus1 = (mb_height - 1) as u16; |
| |
| let compressed_width = mb_width * MB_SIZE; |
| let compressed_height = mb_height * MB_SIZE; |
| |
| if compressed_width != width || compressed_height != height { |
| self = self.frame_crop(0, compressed_height - height, 0, compressed_width - width); |
| } |
| |
| self |
| } |
| |
| pub fn chroma_format_idc(mut self, value: u8) -> Self { |
| self.0.chroma_format_idc = value; |
| self |
| } |
| |
| pub fn max_num_ref_frames(mut self, value: u8) -> Self { |
| self.0.max_num_ref_frames = value; |
| self |
| } |
| |
| pub fn frame_mbs_only_flag(mut self, value: bool) -> Self { |
| self.0.frame_mbs_only_flag = value; |
| self |
| } |
| |
| pub fn mb_adaptive_frame_field_flag(mut self, value: bool) -> Self { |
| self.0.mb_adaptive_frame_field_flag = value; |
| self |
| } |
| |
| pub fn seq_scaling_matrix_present_flag(mut self, value: bool) -> Self { |
| self.0.seq_scaling_matrix_present_flag = value; |
| self |
| } |
| |
| pub fn direct_8x8_inference_flag(mut self, value: bool) -> Self { |
| self.0.direct_8x8_inference_flag = value; |
| self |
| } |
| |
| pub fn vui_parameters_present(mut self) -> Self { |
| if self.0.vui_parameters_present_flag { |
| return self; |
| } |
| |
| self.0.vui_parameters_present_flag = true; |
| // Disable all options at default |
| self.0.vui_parameters.aspect_ratio_info_present_flag = false; |
| self.0.vui_parameters.overscan_info_present_flag = false; |
| self.0.vui_parameters.video_signal_type_present_flag = false; |
| self.0.vui_parameters.colour_description_present_flag = false; |
| self.0.vui_parameters.chroma_loc_info_present_flag = false; |
| self.0.vui_parameters.timing_info_present_flag = false; |
| self.0.vui_parameters.nal_hrd_parameters_present_flag = false; |
| self.0.vui_parameters.vcl_hrd_parameters_present_flag = false; |
| self.0.vui_parameters.pic_struct_present_flag = false; |
| self.0.vui_parameters.bitstream_restriction_flag = false; |
| self |
| } |
| |
| pub fn aspect_ratio_idc(mut self, value: u8) -> Self { |
| self = self.vui_parameters_present(); |
| self.0.vui_parameters.aspect_ratio_info_present_flag = true; |
| self.0.vui_parameters.aspect_ratio_idc = value; |
| self |
| } |
| |
| pub fn sar_resolution(mut self, width: u16, height: u16) -> Self { |
| self = self.aspect_ratio_idc(255); |
| self.0.vui_parameters.sar_width = width; |
| self.0.vui_parameters.sar_height = height; |
| self |
| } |
| |
| pub fn aspect_ratio(self, width_ratio: u16, height_ratio: u16) -> Self { |
| // H.264 Table E-1 |
| match (width_ratio, height_ratio) { |
| (1, 1) => self.aspect_ratio_idc(1), |
| (12, 11) => self.aspect_ratio_idc(2), |
| (10, 11) => self.aspect_ratio_idc(3), |
| (16, 11) => self.aspect_ratio_idc(4), |
| (40, 33) => self.aspect_ratio_idc(5), |
| (24, 11) => self.aspect_ratio_idc(6), |
| (20, 11) => self.aspect_ratio_idc(7), |
| (32, 11) => self.aspect_ratio_idc(8), |
| (80, 33) => self.aspect_ratio_idc(9), |
| (18, 11) => self.aspect_ratio_idc(10), |
| (15, 11) => self.aspect_ratio_idc(11), |
| (64, 33) => self.aspect_ratio_idc(12), |
| (160, 99) => self.aspect_ratio_idc(13), |
| (4, 3) => self.aspect_ratio_idc(14), |
| (3, 2) => self.aspect_ratio_idc(15), |
| (2, 1) => self.aspect_ratio_idc(16), |
| |
| _ => self.sar_resolution(width_ratio, height_ratio), |
| } |
| } |
| |
| pub fn timing_info( |
| mut self, |
| num_units_in_tick: u32, |
| time_scale: u32, |
| fixed_frame_rate_flag: bool, |
| ) -> Self { |
| self = self.vui_parameters_present(); |
| self.0.vui_parameters.timing_info_present_flag = true; |
| self.0.vui_parameters.num_units_in_tick = num_units_in_tick; |
| self.0.vui_parameters.time_scale = time_scale; |
| self.0.vui_parameters.fixed_frame_rate_flag = fixed_frame_rate_flag; |
| self |
| } |
| |
| pub fn log2_max_frame_num_minus4(mut self, value: u8) -> Self { |
| self.0.log2_max_frame_num_minus4 = value; |
| self |
| } |
| |
| pub fn max_frame_num(self, value: u32) -> Self { |
| self.log2_max_frame_num_minus4(value.ilog2() as u8 - 4u8) |
| } |
| |
| pub fn pic_order_cnt_type(mut self, value: u8) -> Self { |
| self.0.pic_order_cnt_type = value; |
| self |
| } |
| |
| pub fn log2_max_pic_order_cnt_lsb_minus4(mut self, value: u8) -> Self { |
| self.0.log2_max_pic_order_cnt_lsb_minus4 = value; |
| self |
| } |
| |
| pub fn max_pic_order_cnt_lsb(self, value: u32) -> Self { |
| self.log2_max_pic_order_cnt_lsb_minus4(value.ilog2() as u8 - 4u8) |
| } |
| |
| pub fn delta_pic_order_always_zero_flag(mut self, value: bool) -> Self { |
| self.0.delta_pic_order_always_zero_flag = value; |
| self |
| } |
| |
| pub fn bit_depth_chroma_minus8(mut self, value: u8) -> Self { |
| self.0.bit_depth_chroma_minus8 = value; |
| self |
| } |
| |
| pub fn bit_depth_chroma(self, value: u8) -> Self { |
| self.bit_depth_luma_minus8(value - 8u8) |
| } |
| |
| pub fn bit_depth_luma_minus8(mut self, value: u8) -> Self { |
| self.0.bit_depth_luma_minus8 = value; |
| self |
| } |
| |
| pub fn bit_depth_luma(self, value: u8) -> Self { |
| self.bit_depth_luma_minus8(value - 8u8) |
| } |
| |
| pub fn build(self) -> Rc<Sps> { |
| Rc::new(self.0) |
| } |
| } |
| |
| #[derive(Clone, Debug, Default, PartialEq, Eq)] |
| pub struct HrdParams { |
| /// Plus 1 specifies the number of alternative CPB specifications in the |
| /// bitstream. The value of `cpb_cnt_minus1` shall be in the range of 0 to 31, |
| /// inclusive |
| pub cpb_cnt_minus1: u8, |
| /// Together with `bit_rate_value_minus1[ SchedSelIdx ]` specifies the |
| /// maximum input bit rate of the `SchedSelIdx`-th CPB. |
| pub bit_rate_scale: u8, |
| /// Together with `cpb_size_value_minus1[ SchedSelIdx ]` specifies the CPB |
| /// size of the SchedSelIdx-th CPB. |
| pub cpb_size_scale: u8, |
| |
| /// `[ SchedSelIdx ]` (together with bit_rate_scale) specifies the maximum |
| /// input bit rate for the SchedSelIdx-th CPB. |
| pub bit_rate_value_minus1: [u32; 32], |
| /// `[ SchedSelIdx ]` is used together with cpb_size_scale to specify the |
| /// SchedSelIdx-th CPB size. |
| pub cpb_size_value_minus1: [u32; 32], |
| /// `[ SchedSelIdx ]` equal to 0 specifies that to decode this bitstream by |
| /// the HRD using the `SchedSelIdx`-th CPB specification, the hypothetical |
| /// stream delivery scheduler (HSS) operates in an intermittent bit rate |
| /// mode. `cbr_flag[ SchedSelIdx ]` equal to 1 specifies that the HSS operates |
| /// in a constant bit rate (CBR) mode |
| pub cbr_flag: [bool; 32], |
| |
| /// Specifies the length in bits of the `initial_cpb_removal_delay[ |
| /// SchedSelIdx ]` and `initial_cpb_removal_delay_offset[ SchedSelIdx ]` syntax |
| /// elements of the buffering period SEI message. |
| pub initial_cpb_removal_delay_length_minus1: u8, |
| /// Specifies the length in bits of the `cpb_removal_delay` syntax element. |
| pub cpb_removal_delay_length_minus1: u8, |
| /// Specifies the length in bits of the `dpb_output_delay` syntax element. |
| pub dpb_output_delay_length_minus1: u8, |
| /// If greater than 0, specifies the length in bits of the `time_offset` |
| /// syntax element. `time_offset_length` equal to 0 specifies that the |
| /// `time_offset` syntax element is not present |
| pub time_offset_length: u8, |
| } |
| |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub struct VuiParams { |
| /// Specifies whether `aspect_ratio_idc` is present. |
| pub aspect_ratio_info_present_flag: bool, |
| /// Specifies the value of the sample aspect ratio of the luma samples. |
| /// Table E-1 shows the meaning of the code. When aspect_ratio_idc indicates |
| /// Extended_SAR, the sample aspect ratio is represented by sar_width : |
| /// sar_height. When the aspect_ratio_idc syntax element is not present, |
| /// aspect_ratio_idc value shall be inferred to be equal to 0 |
| pub aspect_ratio_idc: u8, |
| |
| /* if aspect_ratio_idc == 255 */ |
| /// Indicates the horizontal size of the sample aspect ratio (in arbitrary |
| /// units) |
| pub sar_width: u16, |
| /// Indicates the vertical size of the sample aspect ratio (in the same |
| /// arbitrary units as sar_width). |
| pub sar_height: u16, |
| |
| /// If true specifies that the overscan_appropriate_flag is present. Else, |
| /// the preferred display method for the video signal is unspecified |
| pub overscan_info_present_flag: bool, |
| /* if overscan_info_present_flag */ |
| /// If true, indicates that the cropped decoded pictures output are suitable |
| /// for display using overscan. Else, indicates that the cropped decoded |
| /// pictures output contain visually important information in the entire |
| /// region out to the edges of the cropping rectangle of the picture, such |
| /// that the cropped decoded pictures output should not be displayed using |
| /// overscan. |
| pub overscan_appropriate_flag: bool, |
| |
| /// Specifies that video_format, video_full_range_flag and |
| /// colour_description_present_flag are 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 Recommendation | |
| /// International Standard. When the video_format syntax element is not |
| /// present, video_format value shall be inferred to be equal to 5. |
| 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, |
| /// Specifies that colour_primaries, transfer_characteristics and |
| /// matrix_coefficients are 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 by ISO 11664-1. |
| pub colour_primaries: u8, |
| /// Retains same meaning as in the specification. |
| pub transfer_characteristics: u8, |
| /// 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_coefficients: u8, |
| |
| /// Specifies that chroma_sample_loc_type_top_field and |
| /// chroma_sample_loc_type_bottom_field are present |
| pub chroma_loc_info_present_flag: bool, |
| /// Specify the location of chroma samples. See the spec for more details. |
| pub chroma_sample_loc_type_top_field: u8, |
| /// Specify the location of chroma samples. See the spec for more details. |
| pub chroma_sample_loc_type_bottom_field: u8, |
| |
| /// Specifies that num_units_in_tick, time_scale and fixed_frame_rate_flag |
| /// are present in the bitstream |
| pub timing_info_present_flag: bool, |
| /* if timing_info_present_flag */ |
| /// The number of time units of a clock operating at the frequency |
| /// time_scale Hz that corresponds to one increment (called a clock tick) of |
| /// a clock tick counter |
| 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 |
| /// time_scale of 27 000 000. time_scale shall be greater than 0. |
| pub time_scale: u32, |
| /// Retains the same meaning as the specification. |
| pub fixed_frame_rate_flag: bool, |
| |
| /// Specifies that NAL HRD parameters (pertaining to Type II bitstream |
| /// conformance) are present. |
| pub nal_hrd_parameters_present_flag: bool, |
| /* if nal_hrd_parameters_present_flag */ |
| /// The NAL HDR parameters |
| pub nal_hrd_parameters: HrdParams, |
| /// Specifies that VCL HRD parameters (pertaining to all bitstream |
| /// conformance) are present. |
| pub vcl_hrd_parameters_present_flag: bool, |
| /* if vcl_hrd_parameters_present_flag */ |
| /// The VCL HRD parameters |
| pub vcl_hrd_parameters: HrdParams, |
| |
| /// Specifies the HRD operational mode as specified in Annex C. |
| pub low_delay_hrd_flag: bool, |
| |
| /// Specifies that picture timing SEI messages (clause D.2.3) are present |
| /// that include the pic_struct syntax element. |
| pub pic_struct_present_flag: bool, |
| |
| /// Specifies that the following coded video sequence bitstream restriction |
| /// parameters are present |
| pub bitstream_restriction_flag: bool, |
| /* if bitstream_restriction_flag */ |
| /// If false, 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. If true, indicates that one or more |
| /// samples outside picture boundaries may be used in inter prediction. When |
| /// the motion_vectors_over_pic_boundaries_flag syntax element is not |
| /// present, motion_vectors_over_pic_boundaries_flag value shall be inferred |
| /// to be true. |
| pub motion_vectors_over_pic_boundaries_flag: bool, |
| /// 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 coded video |
| /// sequence. |
| pub max_bytes_per_pic_denom: u32, |
| /// Indicates an upper bound for the number of coded bits of |
| /// macroblock_layer( ) data for any macroblock in any picture of the coded |
| /// video sequence |
| pub max_bits_per_mb_denom: u32, |
| /// Retains the same meaning as the specification. |
| pub log2_max_mv_length_horizontal: u32, |
| /// Retains the same meaning as the specification. |
| pub log2_max_mv_length_vertical: u32, |
| /// Indicates an upper bound for the number of frames buffers, in the |
| /// decoded picture buffer (DPB), that are required for storing frames, |
| /// complementary field pairs, and non-paired fields before output. It is a |
| /// requirement of bitstream conformance that the maximum number of frames, |
| /// complementary field pairs, or non-paired fields that precede any frame, |
| /// complementary field pair, or non-paired field in the coded video |
| /// sequence in decoding order and follow it in output order shall be less |
| /// than or equal to max_num_reorder_frames. The value of |
| /// max_num_reorder_frames shall be in the range of 0 to |
| /// max_dec_frame_buffering, inclusive. |
| /// |
| /// When the max_num_reorder_frames syntax element is not present, the value |
| /// of max_num_reorder_frames value shall be inferred as follows: |
| /// If profile_idc is equal to 44, 86, 100, 110, 122, or 244 and |
| /// constraint_set3_flag is equal to 1, the value of max_num_reorder_frames |
| /// shall be inferred to be equal to 0. |
| /// |
| /// Otherwise (profile_idc is not equal to 44, 86, 100, 110, 122, or 244 or |
| /// constraint_set3_flag is equal to 0), the value of max_num_reorder_frames |
| /// shall be inferred to be equal to MaxDpbFrames. |
| pub max_num_reorder_frames: u32, |
| /// Specifies the required size of the HRD decoded picture buffer (DPB) in |
| /// units of frame buffers. It is a requirement of bitstream conformance |
| /// that the coded video sequence shall not require a decoded picture buffer |
| /// with size of more than Max( 1, max_dec_frame_buffering ) frame buffers |
| /// to enable the output of decoded pictures at the output times specified |
| /// by dpb_output_delay of the picture timing SEI messages. The value of |
| /// max_dec_frame_buffering shall be greater than or equal to |
| /// max_num_ref_frames. An upper bound for the value of |
| /// max_dec_frame_buffering is specified by the level limits in clauses |
| /// A.3.1, A.3.2, G.10.2.1, and H.10.2. |
| /// |
| /// When the max_dec_frame_buffering syntax element is not present, the |
| /// value of max_dec_frame_buffering shall be inferred as follows: |
| /// |
| /// If profile_idc is equal to 44, 86, 100, 110, 122, or 244 and |
| /// constraint_set3_flag is equal to 1, the value of max_dec_frame_buffering |
| /// shall be inferred to be equal to 0. |
| /// |
| /// Otherwise (profile_idc is not equal to 44, 86, 100, 110, 122, or 244 or |
| /// constraint_set3_flag is equal to 0), the value of |
| /// max_dec_frame_buffering shall be inferred to be equal to MaxDpbFrames. |
| pub max_dec_frame_buffering: 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_coefficients: 2, |
| chroma_loc_info_present_flag: Default::default(), |
| chroma_sample_loc_type_top_field: Default::default(), |
| chroma_sample_loc_type_bottom_field: Default::default(), |
| timing_info_present_flag: Default::default(), |
| num_units_in_tick: Default::default(), |
| time_scale: Default::default(), |
| fixed_frame_rate_flag: Default::default(), |
| nal_hrd_parameters_present_flag: Default::default(), |
| nal_hrd_parameters: Default::default(), |
| vcl_hrd_parameters_present_flag: Default::default(), |
| vcl_hrd_parameters: Default::default(), |
| low_delay_hrd_flag: Default::default(), |
| pic_struct_present_flag: Default::default(), |
| bitstream_restriction_flag: Default::default(), |
| motion_vectors_over_pic_boundaries_flag: Default::default(), |
| max_bytes_per_pic_denom: Default::default(), |
| max_bits_per_mb_denom: Default::default(), |
| log2_max_mv_length_horizontal: Default::default(), |
| log2_max_mv_length_vertical: Default::default(), |
| max_num_reorder_frames: Default::default(), |
| max_dec_frame_buffering: Default::default(), |
| } |
| } |
| } |
| |
| /// A H264 Picture Parameter Set. A syntax structure containing syntax elements |
| /// that apply to zero or more entire coded pictures as determined by the |
| /// `pic_parameter_set_id` syntax element found in each slice header. |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct Pps { |
| /// Identifies the picture parameter set that is referred to in the slice header. |
| pub pic_parameter_set_id: u8, |
| |
| /// Refers to the active sequence parameter set. |
| pub seq_parameter_set_id: u8, |
| |
| /// Selects the entropy decoding method to be applied for the syntax |
| /// elements for which two descriptors appear in the syntax tables as |
| /// follows: If `entropy_coding_mode_flag` is false, the method specified by |
| /// the left descriptor in the syntax table is applied (Exp-Golomb coded, |
| /// see clause 9.1 or CAVLC, see clause 9.2). Otherwise |
| /// (`entropy_coding_mode_flag` is true), the method specified by the right |
| /// descriptor in the syntax table is applied (CABAC, see clause 9.3). |
| pub entropy_coding_mode_flag: bool, |
| |
| /// If true, specifies that the syntax elements delta_pic_order_cnt_bottom |
| /// (when `pic_order_cnt_type` is equal to 0) or `delta_pic_order_cnt[1]` |
| /// (when `pic_order_cnt_type` is equal to 1), which are related to picture |
| /// order counts for the bottom field of a coded frame, are present in the |
| /// slice headers for coded frames as specified in clause 7.3.3. Otherwise, |
| /// specifies that the syntax elements `delta_pic_order_cnt_bottom` and |
| /// `delta_pic_order_cnt[1]` are not present in the slice headers. |
| pub bottom_field_pic_order_in_frame_present_flag: bool, |
| |
| /// Plus 1 specifies the number of slice groups for a picture. When |
| /// `num_slice_groups_minus1` is equal to 0, all slices of the picture |
| /// belong to the same slice group. The allowed range of |
| /// `num_slice_groups_minus1` is specified in Annex A. |
| pub num_slice_groups_minus1: u32, |
| |
| /// Specifies how `num_ref_idx_l0_active_minus1` is inferred for P, SP, and |
| /// B slices with `num_ref_idx_active_override_flag` not set. |
| pub num_ref_idx_l0_default_active_minus1: u8, |
| |
| /// Specifies how `num_ref_idx_l1_active_minus1` is inferred for B slices |
| /// with `num_ref_idx_active_override_flag` not set. |
| pub num_ref_idx_l1_default_active_minus1: u8, |
| |
| /// If not set, specifies that the default weighted prediction shall be |
| /// applied to P and SP slices. If set, specifies that explicit weighted |
| /// prediction shall be applied to P and SP slices. |
| pub weighted_pred_flag: bool, |
| |
| /// `weighted_bipred_idc` equal to 0 specifies that the default weighted |
| /// prediction shall be applied to B slices. `weighted_bipred_idc` equal to |
| /// 1 specifies that explicit weighted prediction shall be applied to B |
| /// slices. `weighted_bipred_idc` equal to 2 specifies that implicit |
| /// weighted prediction shall be applied to B slices |
| pub weighted_bipred_idc: u8, |
| |
| /// Specifies the initial value minus 26 of SliceQPY for each slice. The |
| /// initial value is modified at the slice layer when a non-zero value of |
| /// `slice_qp_delta` is decoded, and is modified further when a non-zero |
| /// value of `mb_qp_delta` is decoded at the macroblock layer. |
| pub pic_init_qp_minus26: i8, |
| |
| /// Specifies the initial value minus 26 of SliceQSY for all macroblocks in |
| /// SP or SI slices. The initial value is modified at the slice layer when a |
| /// non-zero value of `slice_qs_delta` is decoded. |
| pub pic_init_qs_minus26: i8, |
| |
| /// Specifies the offset that shall be added to QP Y and QSY for addressing |
| /// the table of QPC values for the Cb chroma component. |
| pub chroma_qp_index_offset: i8, |
| |
| /// If set, specifies that a set of syntax elements controlling the |
| /// characteristics of the deblocking filter is present in the slice header. |
| /// If not set, specifies that the set of syntax elements controlling the |
| /// characteristics of the deblocking filter is not present in the slice |
| /// headers and their inferred values are in effect. |
| pub deblocking_filter_control_present_flag: bool, |
| |
| /// If not set, specifies that intra prediction allows usage of residual |
| /// data and decoded samples of neighbouring macroblocks coded using Inter |
| /// macroblock prediction modes for the prediction of macroblocks coded |
| /// using Intra macroblock prediction modes. If set, specifies constrained |
| /// intra prediction, in which case prediction of macroblocks coded using |
| /// Intra macroblock prediction modes only uses residual data and decoded |
| /// samples from I or SI macroblock types. |
| pub constrained_intra_pred_flag: bool, |
| |
| /// If not set, specifies that the `redundant_pic_cnt` syntax element is not |
| /// present in slice headers, coded slice data partition B NAL units, and |
| /// coded slice data partition C NAL units that refer (either directly or by |
| /// association with a corresponding coded slice data partition A NAL unit) |
| /// to the picture parameter set. If set, specifies that the |
| /// `redundant_pic_cnt` syntax element is present in all slice headers, |
| /// coded slice data partition B NAL units, and coded slice data partition C |
| /// NAL units that refer (either directly or by association with a |
| /// corresponding coded slice data partition A NAL unit) to the picture |
| /// parameter set. |
| pub redundant_pic_cnt_present_flag: bool, |
| |
| /// If set, specifies that the 8x8 transform decoding process may be in use |
| /// (see clause 8.5). If not set, specifies that the 8x8 transform decoding |
| /// process is not in use. |
| pub transform_8x8_mode_flag: bool, |
| |
| /// If set, specifies that parameters are present to modify the scaling |
| /// lists specified in the sequence parameter set. If not set, specifies |
| /// that the scaling lists used for the picture shall be inferred to be |
| /// equal to those specified by the sequence parameter set. |
| pub pic_scaling_matrix_present_flag: bool, |
| |
| /// 4x4 Scaling list as read with 7.3.2.1.1.1 |
| pub scaling_lists_4x4: [[u8; 16]; 6], |
| /// 8x8 Scaling list as read with 7.3.2.1.1.1 |
| pub scaling_lists_8x8: [[u8; 64]; 6], |
| |
| /// Specifies the offset that shall be added to QPY and QSY for addressing |
| /// the table of QPC values for the Cr chroma component. When |
| /// `second_chroma_qp_index_offset` is not present, it shall be inferred to be |
| /// equal to `chroma_qp_index_offset`. |
| pub second_chroma_qp_index_offset: i8, |
| |
| /// The SPS referenced by this PPS. |
| pub sps: Rc<Sps>, |
| } |
| |
| pub struct PpsBuilder(Pps); |
| |
| impl PpsBuilder { |
| pub fn new(sps: Rc<Sps>) -> Self { |
| PpsBuilder(Pps { |
| pic_parameter_set_id: 0, |
| seq_parameter_set_id: sps.seq_parameter_set_id, |
| entropy_coding_mode_flag: false, |
| bottom_field_pic_order_in_frame_present_flag: false, |
| num_slice_groups_minus1: 0, |
| num_ref_idx_l0_default_active_minus1: 0, |
| num_ref_idx_l1_default_active_minus1: 0, |
| weighted_pred_flag: false, |
| weighted_bipred_idc: 0, |
| pic_init_qp_minus26: 0, |
| pic_init_qs_minus26: 0, |
| chroma_qp_index_offset: 0, |
| deblocking_filter_control_present_flag: false, |
| constrained_intra_pred_flag: false, |
| redundant_pic_cnt_present_flag: false, |
| transform_8x8_mode_flag: false, |
| pic_scaling_matrix_present_flag: false, |
| scaling_lists_4x4: [[0; 16]; 6], |
| scaling_lists_8x8: [[0; 64]; 6], |
| second_chroma_qp_index_offset: 0, |
| sps, |
| }) |
| } |
| |
| pub fn pic_parameter_set_id(mut self, value: u8) -> Self { |
| self.0.pic_parameter_set_id = value; |
| self |
| } |
| |
| pub fn pic_init_qp_minus26(mut self, value: i8) -> Self { |
| self.0.pic_init_qp_minus26 = value; |
| self |
| } |
| |
| pub fn pic_init_qp(self, value: u8) -> Self { |
| self.pic_init_qp_minus26(value as i8 - 26) |
| } |
| |
| pub fn deblocking_filter_control_present_flag(mut self, value: bool) -> Self { |
| self.0.deblocking_filter_control_present_flag = value; |
| self |
| } |
| |
| pub fn num_ref_idx_l0_default_active_minus1(mut self, value: u8) -> Self { |
| self.0.num_ref_idx_l0_default_active_minus1 = value; |
| self |
| } |
| |
| pub fn num_ref_idx_l0_default_active(self, value: u8) -> Self { |
| self.num_ref_idx_l0_default_active_minus1(value - 1) |
| } |
| |
| pub fn num_ref_idx_l1_default_active_minus1(mut self, value: u8) -> Self { |
| self.0.num_ref_idx_l1_default_active_minus1 = value; |
| self |
| } |
| |
| pub fn num_ref_idx_l1_default_active(self, value: u8) -> Self { |
| self.num_ref_idx_l1_default_active_minus1(value - 1) |
| } |
| |
| pub fn build(self) -> Rc<Pps> { |
| Rc::new(self.0) |
| } |
| } |
| |
| #[derive(Debug, Default)] |
| pub struct Parser { |
| active_spses: BTreeMap<u8, Rc<Sps>>, |
| active_ppses: BTreeMap<u8, Rc<Pps>>, |
| } |
| |
| impl Parser { |
| fn fill_default_scaling_list_4x4(scaling_list4x4: &mut [u8; 16], i: usize) { |
| // See table 7.2 in the spec. |
| assert!(i < 6); |
| if i < 3 { |
| *scaling_list4x4 = DEFAULT_4X4_INTRA; |
| } else if i < 6 { |
| *scaling_list4x4 = DEFAULT_4X4_INTER; |
| } |
| } |
| |
| fn fill_default_scaling_list_8x8(scaling_list8x8: &mut [u8; 64], i: usize) { |
| assert!(i < 6); |
| if i % 2 == 0 { |
| *scaling_list8x8 = DEFAULT_8X8_INTRA; |
| } else { |
| *scaling_list8x8 = DEFAULT_8X8_INTER; |
| } |
| } |
| |
| fn fill_fallback_scaling_list_4x4( |
| scaling_list4x4: &mut [[u8; 16]; 6], |
| i: usize, |
| default_scaling_list_intra: &[u8; 16], |
| default_scaling_list_inter: &[u8; 16], |
| ) { |
| // See table 7.2 in the spec. |
| scaling_list4x4[i] = match i { |
| 0 => *default_scaling_list_intra, |
| 1 => scaling_list4x4[0], |
| 2 => scaling_list4x4[1], |
| 3 => *default_scaling_list_inter, |
| 4 => scaling_list4x4[3], |
| 5 => scaling_list4x4[4], |
| _ => panic!("Unexpected value {}", i), |
| } |
| } |
| |
| fn fill_fallback_scaling_list_8x8( |
| scaling_list8x8: &mut [[u8; 64]; 6], |
| i: usize, |
| default_scaling_list_intra: &[u8; 64], |
| default_scaling_list_inter: &[u8; 64], |
| ) { |
| // See table 7.2 in the spec. |
| scaling_list8x8[i] = match i { |
| 0 => *default_scaling_list_intra, |
| 1 => *default_scaling_list_inter, |
| 2 => scaling_list8x8[0], |
| 3 => scaling_list8x8[1], |
| 4 => scaling_list8x8[2], |
| 5 => scaling_list8x8[3], |
| _ => panic!("Unexpected value {}", i), |
| } |
| } |
| |
| fn fill_scaling_list_flat( |
| scaling_list4x4: &mut [[u8; 16]; 6], |
| scaling_list8x8: &mut [[u8; 64]; 6], |
| ) { |
| // (7-8) in the spec. |
| for outer in scaling_list4x4 { |
| for inner in outer { |
| *inner = 16; |
| } |
| } |
| |
| // (7-9) in the spec. |
| for outer in scaling_list8x8 { |
| for inner in outer { |
| *inner = 16; |
| } |
| } |
| } |
| |
| fn parse_scaling_list<U: AsMut<[u8]>>( |
| r: &mut BitReader, |
| scaling_list: &mut U, |
| use_default: &mut bool, |
| ) -> Result<(), String> { |
| // 7.3.2.1.1.1 |
| let mut last_scale = 8u8; |
| let mut next_scale = 8u8; |
| |
| for j in 0..scaling_list.as_mut().len() { |
| if next_scale != 0 { |
| let delta_scale = r.read_se::<i32>()?; |
| next_scale = ((last_scale as i32 + delta_scale + 256) % 256) as u8; |
| *use_default = j == 0 && next_scale == 0; |
| if *use_default { |
| return Ok(()); |
| } |
| } |
| |
| scaling_list.as_mut()[j] = if next_scale == 0 { |
| last_scale |
| } else { |
| next_scale |
| }; |
| |
| last_scale = scaling_list.as_mut()[j]; |
| } |
| |
| Ok(()) |
| } |
| |
| fn parse_sps_scaling_lists(r: &mut BitReader, sps: &mut Sps) -> Result<(), String> { |
| let scaling_lists4x4 = &mut sps.scaling_lists_4x4; |
| let scaling_lisst8x8 = &mut sps.scaling_lists_8x8; |
| |
| // Parse scaling_list4x4 |
| for i in 0..6 { |
| let seq_scaling_list_present_flag = r.read_bit()?; |
| if seq_scaling_list_present_flag { |
| let mut use_default = false; |
| |
| Parser::parse_scaling_list(r, &mut scaling_lists4x4[i], &mut use_default)?; |
| |
| if use_default { |
| Parser::fill_default_scaling_list_4x4(&mut scaling_lists4x4[i], i); |
| } |
| } else { |
| Parser::fill_fallback_scaling_list_4x4( |
| scaling_lists4x4, |
| i, |
| &DEFAULT_4X4_INTRA, |
| &DEFAULT_4X4_INTER, |
| ); |
| } |
| } |
| |
| // Parse scaling_list8x8 |
| let num_8x8 = if sps.chroma_format_idc != 3 { 2 } else { 6 }; |
| for i in 0..num_8x8 { |
| let seq_scaling_list_present_flag = r.read_bit()?; |
| if seq_scaling_list_present_flag { |
| let mut use_default = false; |
| Parser::parse_scaling_list(r, &mut scaling_lisst8x8[i], &mut use_default)?; |
| |
| if use_default { |
| Parser::fill_default_scaling_list_8x8(&mut scaling_lisst8x8[i], i); |
| } |
| } else { |
| Parser::fill_fallback_scaling_list_8x8( |
| scaling_lisst8x8, |
| i, |
| &DEFAULT_8X8_INTRA, |
| &DEFAULT_8X8_INTER, |
| ); |
| } |
| } |
| Ok(()) |
| } |
| |
| fn parse_pps_scaling_lists(r: &mut BitReader, pps: &mut Pps, sps: &Sps) -> Result<(), String> { |
| let scaling_lists4x4 = &mut pps.scaling_lists_4x4; |
| let scaling_lists8x8 = &mut pps.scaling_lists_8x8; |
| |
| for i in 0..6 { |
| let pic_scaling_list_present_flag = r.read_bit()?; |
| if pic_scaling_list_present_flag { |
| let mut use_default = false; |
| |
| Parser::parse_scaling_list(r, &mut scaling_lists4x4[i], &mut use_default)?; |
| |
| if use_default { |
| Parser::fill_default_scaling_list_4x4(&mut scaling_lists4x4[i], i); |
| } |
| } else if !sps.seq_scaling_matrix_present_flag { |
| // Table 7-2: Fallback rule A |
| Parser::fill_fallback_scaling_list_4x4( |
| scaling_lists4x4, |
| i, |
| &DEFAULT_4X4_INTRA, |
| &DEFAULT_4X4_INTER, |
| ); |
| } else { |
| // Table 7-2: Fallback rule B |
| Parser::fill_fallback_scaling_list_4x4( |
| scaling_lists4x4, |
| i, |
| &sps.scaling_lists_4x4[0], |
| &sps.scaling_lists_4x4[3], |
| ); |
| } |
| } |
| |
| if pps.transform_8x8_mode_flag { |
| let num8x8 = if sps.chroma_format_idc != 3 { 2 } else { 6 }; |
| |
| for i in 0..num8x8 { |
| let pic_scaling_list_present_flag = r.read_bit()?; |
| if pic_scaling_list_present_flag { |
| let mut use_default = false; |
| |
| Parser::parse_scaling_list(r, &mut scaling_lists8x8[i], &mut use_default)?; |
| |
| if use_default { |
| Parser::fill_default_scaling_list_8x8(&mut scaling_lists8x8[i], i); |
| } |
| } else if !sps.seq_scaling_matrix_present_flag { |
| // Table 7-2: Fallback rule A |
| Parser::fill_fallback_scaling_list_8x8( |
| scaling_lists8x8, |
| i, |
| &DEFAULT_8X8_INTRA, |
| &DEFAULT_8X8_INTER, |
| ); |
| } else { |
| // Table 7-2: Fallback rule B |
| Parser::fill_fallback_scaling_list_8x8( |
| scaling_lists8x8, |
| i, |
| &sps.scaling_lists_8x8[0], |
| &sps.scaling_lists_8x8[1], |
| ); |
| } |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| fn parse_hrd(r: &mut BitReader, hrd: &mut HrdParams) -> Result<(), String> { |
| hrd.cpb_cnt_minus1 = r.read_ue_max(31)?; |
| hrd.bit_rate_scale = r.read_bits(4)?; |
| hrd.cpb_size_scale = r.read_bits(4)?; |
| |
| for sched_sel_idx in 0..=usize::from(hrd.cpb_cnt_minus1) { |
| hrd.bit_rate_value_minus1[sched_sel_idx] = r.read_ue()?; |
| hrd.cpb_size_value_minus1[sched_sel_idx] = r.read_ue()?; |
| hrd.cbr_flag[sched_sel_idx] = r.read_bit()?; |
| } |
| |
| hrd.initial_cpb_removal_delay_length_minus1 = r.read_bits(5)?; |
| hrd.cpb_removal_delay_length_minus1 = r.read_bits(5)?; |
| hrd.dpb_output_delay_length_minus1 = r.read_bits(5)?; |
| hrd.time_offset_length = r.read_bits(5)?; |
| Ok(()) |
| } |
| |
| fn parse_vui(r: &mut BitReader, sps: &mut Sps) -> Result<(), String> { |
| 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)?; |
| if vui.aspect_ratio_idc == 255 { |
| 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_coefficients = 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.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_bit()? as u32; |
| if vui.num_units_in_tick == 0 { |
| return Err("num_units_in_tick == 0, which is not allowed by E.2.1".into()); |
| } |
| |
| vui.time_scale = r.read_bits::<u32>(31)? << 1; |
| vui.time_scale |= r.read_bit()? as u32; |
| if vui.time_scale == 0 { |
| return Err("time_scale == 0, which is not allowed by E.2.1".into()); |
| } |
| |
| vui.fixed_frame_rate_flag = r.read_bit()?; |
| } |
| |
| vui.nal_hrd_parameters_present_flag = r.read_bit()?; |
| if vui.nal_hrd_parameters_present_flag { |
| Parser::parse_hrd(r, &mut vui.nal_hrd_parameters)?; |
| } |
| |
| vui.vcl_hrd_parameters_present_flag = r.read_bit()?; |
| if vui.vcl_hrd_parameters_present_flag { |
| Parser::parse_hrd(r, &mut vui.vcl_hrd_parameters)?; |
| } |
| |
| if vui.nal_hrd_parameters_present_flag || vui.vcl_hrd_parameters_present_flag { |
| vui.low_delay_hrd_flag = r.read_bit()?; |
| } |
| |
| vui.pic_struct_present_flag = r.read_bit()?; |
| vui.bitstream_restriction_flag = r.read_bit()?; |
| |
| if vui.bitstream_restriction_flag { |
| vui.motion_vectors_over_pic_boundaries_flag = r.read_bit()?; |
| vui.max_bytes_per_pic_denom = r.read_ue()?; |
| vui.max_bits_per_mb_denom = r.read_ue_max(16)?; |
| vui.log2_max_mv_length_horizontal = r.read_ue_max(16)?; |
| vui.log2_max_mv_length_vertical = r.read_ue_max(16)?; |
| vui.max_num_reorder_frames = r.read_ue()?; |
| vui.max_dec_frame_buffering = r.read_ue()?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Parse a SPS and add it to the list of active SPSes. |
| /// |
| /// Returns a reference to the new SPS. |
| pub fn parse_sps(&mut self, nalu: &Nalu) -> Result<&Rc<Sps>, String> { |
| if !matches!(nalu.header.type_, NaluType::Sps) { |
| return Err(format!( |
| "Invalid NALU type, expected {:?}, got {:?}", |
| NaluType::Sps, |
| nalu.header.type_ |
| )); |
| } |
| |
| let data = nalu.as_ref(); |
| // Skip the header |
| let mut r = BitReader::new(&data[nalu.header.len()..], true); |
| let mut sps = Sps { |
| profile_idc: r.read_bits(8)?, |
| constraint_set0_flag: r.read_bit()?, |
| constraint_set1_flag: r.read_bit()?, |
| constraint_set2_flag: r.read_bit()?, |
| constraint_set3_flag: r.read_bit()?, |
| constraint_set4_flag: r.read_bit()?, |
| constraint_set5_flag: r.read_bit()?, |
| ..Default::default() |
| }; |
| |
| // skip reserved_zero_2bits |
| r.skip_bits(2)?; |
| |
| let level: u8 = r.read_bits(8)?; |
| sps.level_idc = Level::try_from(level)?; |
| sps.seq_parameter_set_id = r.read_ue_max(31)?; |
| |
| if sps.profile_idc == 100 |
| || sps.profile_idc == 110 |
| || sps.profile_idc == 122 |
| || sps.profile_idc == 244 |
| || sps.profile_idc == 44 |
| || sps.profile_idc == 83 |
| || sps.profile_idc == 86 |
| || sps.profile_idc == 118 |
| || sps.profile_idc == 128 |
| || sps.profile_idc == 138 |
| || sps.profile_idc == 139 |
| || sps.profile_idc == 134 |
| || sps.profile_idc == 135 |
| { |
| sps.chroma_format_idc = r.read_ue_max(3)?; |
| if sps.chroma_format_idc == 3 { |
| sps.separate_colour_plane_flag = r.read_bit()?; |
| } |
| |
| sps.bit_depth_luma_minus8 = r.read_ue_max(6)?; |
| sps.bit_depth_chroma_minus8 = r.read_ue_max(6)?; |
| sps.qpprime_y_zero_transform_bypass_flag = r.read_bit()?; |
| sps.seq_scaling_matrix_present_flag = r.read_bit()?; |
| |
| if sps.seq_scaling_matrix_present_flag { |
| Parser::parse_sps_scaling_lists(&mut r, &mut sps)?; |
| } else { |
| Parser::fill_scaling_list_flat( |
| &mut sps.scaling_lists_4x4, |
| &mut sps.scaling_lists_8x8, |
| ); |
| } |
| } else { |
| sps.chroma_format_idc = 1; |
| Parser::fill_scaling_list_flat(&mut sps.scaling_lists_4x4, &mut sps.scaling_lists_8x8); |
| } |
| |
| sps.log2_max_frame_num_minus4 = r.read_ue_max(12)?; |
| |
| sps.pic_order_cnt_type = r.read_ue_max(2)?; |
| |
| if sps.pic_order_cnt_type == 0 { |
| sps.log2_max_pic_order_cnt_lsb_minus4 = r.read_ue_max(12)?; |
| sps.expected_delta_per_pic_order_cnt_cycle = 0; |
| } else if sps.pic_order_cnt_type == 1 { |
| sps.delta_pic_order_always_zero_flag = r.read_bit()?; |
| sps.offset_for_non_ref_pic = r.read_se()?; |
| sps.offset_for_top_to_bottom_field = r.read_se()?; |
| sps.num_ref_frames_in_pic_order_cnt_cycle = r.read_ue_max(254)?; |
| |
| let mut offset_acc = 0; |
| for i in 0..usize::from(sps.num_ref_frames_in_pic_order_cnt_cycle) { |
| sps.offset_for_ref_frame[i] = r.read_se()?; |
| |
| // (7-12) in the spec. |
| offset_acc += sps.offset_for_ref_frame[i]; |
| } |
| |
| sps.expected_delta_per_pic_order_cnt_cycle = offset_acc; |
| } |
| |
| sps.max_num_ref_frames = r.read_ue_max(DPB_MAX_SIZE as u32)?; |
| sps.gaps_in_frame_num_value_allowed_flag = r.read_bit()?; |
| sps.pic_width_in_mbs_minus1 = r.read_ue()?; |
| sps.pic_height_in_map_units_minus1 = r.read_ue()?; |
| sps.frame_mbs_only_flag = r.read_bit()?; |
| |
| if !sps.frame_mbs_only_flag { |
| sps.mb_adaptive_frame_field_flag = r.read_bit()?; |
| } |
| |
| sps.direct_8x8_inference_flag = r.read_bit()?; |
| sps.frame_cropping_flag = r.read_bit()?; |
| |
| if sps.frame_cropping_flag { |
| sps.frame_crop_left_offset = r.read_ue()?; |
| sps.frame_crop_right_offset = r.read_ue()?; |
| sps.frame_crop_top_offset = r.read_ue()?; |
| sps.frame_crop_bottom_offset = r.read_ue()?; |
| |
| // Validate that cropping info is valid. |
| let (crop_unit_x, crop_unit_y) = sps.crop_unit_x_y(); |
| |
| let _ = sps |
| .frame_crop_left_offset |
| .checked_add(sps.frame_crop_right_offset) |
| .and_then(|r| r.checked_mul(crop_unit_x)) |
| .and_then(|r| sps.width().checked_sub(r)) |
| .ok_or::<String>("Invalid frame crop width".into())?; |
| |
| let _ = sps |
| .frame_crop_top_offset |
| .checked_add(sps.frame_crop_bottom_offset) |
| .and_then(|r| r.checked_mul(crop_unit_y)) |
| .and_then(|r| sps.height().checked_sub(r)) |
| .ok_or::<String>("invalid frame crop height".into())?; |
| } |
| |
| sps.vui_parameters_present_flag = r.read_bit()?; |
| if sps.vui_parameters_present_flag { |
| Parser::parse_vui(&mut r, &mut sps)?; |
| } |
| |
| let key = sps.seq_parameter_set_id; |
| |
| if self.active_spses.keys().len() >= MAX_SPS_COUNT as usize { |
| return Err("Broken data: Number of active SPSs > MAX_SPS_COUNT".into()); |
| } |
| |
| let sps = Rc::new(sps); |
| self.active_spses.remove(&key); |
| Ok(self.active_spses.entry(key).or_insert(sps)) |
| } |
| |
| pub fn parse_pps(&mut self, nalu: &Nalu) -> Result<&Pps, String> { |
| if !matches!(nalu.header.type_, NaluType::Pps) { |
| return Err(format!( |
| "Invalid NALU type, expected {:?}, got {:?}", |
| NaluType::Pps, |
| nalu.header.type_ |
| )); |
| } |
| |
| let data = nalu.as_ref(); |
| // Skip the header |
| let mut r = BitReader::new(&data[nalu.header.len()..], true); |
| let pic_parameter_set_id = r.read_ue_max(MAX_PPS_COUNT as u32 - 1)?; |
| let seq_parameter_set_id = r.read_ue_max(MAX_SPS_COUNT as u32 - 1)?; |
| let sps = self.get_sps(seq_parameter_set_id).ok_or::<String>(format!( |
| "Could not get SPS for seq_parameter_set_id {}", |
| seq_parameter_set_id |
| ))?; |
| let mut pps = Pps { |
| pic_parameter_set_id, |
| seq_parameter_set_id, |
| sps: Rc::clone(sps), |
| scaling_lists_4x4: [[0; 16]; 6], |
| scaling_lists_8x8: [[0; 64]; 6], |
| entropy_coding_mode_flag: Default::default(), |
| bottom_field_pic_order_in_frame_present_flag: Default::default(), |
| num_slice_groups_minus1: Default::default(), |
| num_ref_idx_l0_default_active_minus1: Default::default(), |
| num_ref_idx_l1_default_active_minus1: Default::default(), |
| weighted_pred_flag: Default::default(), |
| weighted_bipred_idc: Default::default(), |
| pic_init_qp_minus26: Default::default(), |
| pic_init_qs_minus26: Default::default(), |
| chroma_qp_index_offset: Default::default(), |
| deblocking_filter_control_present_flag: Default::default(), |
| constrained_intra_pred_flag: Default::default(), |
| redundant_pic_cnt_present_flag: Default::default(), |
| transform_8x8_mode_flag: Default::default(), |
| second_chroma_qp_index_offset: Default::default(), |
| pic_scaling_matrix_present_flag: Default::default(), |
| }; |
| |
| pps.entropy_coding_mode_flag = r.read_bit()?; |
| pps.bottom_field_pic_order_in_frame_present_flag = r.read_bit()?; |
| pps.num_slice_groups_minus1 = r.read_ue_max(7)?; |
| |
| if pps.num_slice_groups_minus1 > 0 { |
| return Err("Stream contain unsupported/unimplemented NALs".into()); |
| } |
| |
| pps.num_ref_idx_l0_default_active_minus1 = r.read_ue_max(31)?; |
| pps.num_ref_idx_l1_default_active_minus1 = r.read_ue_max(31)?; |
| |
| pps.weighted_pred_flag = r.read_bit()?; |
| pps.weighted_bipred_idc = r.read_bits(2)?; |
| |
| let qp_bd_offset_y = i32::from(6 * (sps.bit_depth_luma_minus8)); |
| pps.pic_init_qp_minus26 = r.read_se_bounded(-(26 + qp_bd_offset_y), 25)?; |
| pps.pic_init_qs_minus26 = r.read_se_bounded(-26, 25)?; |
| |
| pps.chroma_qp_index_offset = r.read_se_bounded(-12, 12)?; |
| |
| // When second_chroma_qp_index_offset is not present, it shall be |
| // inferred to be equal to chroma_qp_index_offset. |
| pps.second_chroma_qp_index_offset = pps.chroma_qp_index_offset; |
| |
| pps.deblocking_filter_control_present_flag = r.read_bit()?; |
| pps.constrained_intra_pred_flag = r.read_bit()?; |
| pps.redundant_pic_cnt_present_flag = r.read_bit()?; |
| |
| if r.has_more_rsbp_data() { |
| pps.transform_8x8_mode_flag = r.read_bit()?; |
| pps.pic_scaling_matrix_present_flag = r.read_bit()?; |
| |
| if pps.pic_scaling_matrix_present_flag { |
| Parser::parse_pps_scaling_lists(&mut r, &mut pps, sps)?; |
| } |
| |
| pps.second_chroma_qp_index_offset = r.read_se()?; |
| } |
| |
| if !pps.pic_scaling_matrix_present_flag { |
| // If not set, specifies that the scaling lists used for the picture |
| // shall be inferred to be equal to those specified by the sequence |
| // parameter set. When pic_scaling_matrix_present_flag is not |
| // present, it shall be inferred to be not set. |
| pps.scaling_lists_4x4 = sps.scaling_lists_4x4; |
| pps.scaling_lists_8x8 = sps.scaling_lists_8x8; |
| } |
| |
| let key = pps.pic_parameter_set_id; |
| |
| if self.active_ppses.keys().len() >= MAX_PPS_COUNT as usize { |
| return Err("Broken Data: number of active PPSs > MAX_PPS_COUNT".into()); |
| } |
| |
| let pps = Rc::new(pps); |
| self.active_ppses.remove(&key); |
| Ok(self.active_ppses.entry(key).or_insert(pps)) |
| } |
| |
| fn parse_ref_pic_list_modification( |
| r: &mut BitReader, |
| num_ref_idx_active_minus1: u8, |
| ref_list_mods: &mut Vec<RefPicListModification>, |
| ) -> Result<(), String> { |
| if num_ref_idx_active_minus1 >= 32 { |
| return Err("Broken Data: num_ref_idx_active_minus1 >= 32".into()); |
| } |
| |
| loop { |
| let mut pic_num_mod = RefPicListModification { |
| modification_of_pic_nums_idc: r.read_ue_max(3)?, |
| ..Default::default() |
| }; |
| |
| match pic_num_mod.modification_of_pic_nums_idc { |
| 0 | 1 => { |
| pic_num_mod.abs_diff_pic_num_minus1 = r.read_ue()?; |
| } |
| |
| 2 => { |
| pic_num_mod.long_term_pic_num = r.read_ue()?; |
| } |
| |
| 3 => { |
| ref_list_mods.push(pic_num_mod); |
| break; |
| } |
| |
| _ => return Err("Broken Data: modification_of_pic_nums_idc > 3".into()), |
| } |
| |
| ref_list_mods.push(pic_num_mod); |
| } |
| |
| Ok(()) |
| } |
| |
| fn parse_ref_pic_list_modifications( |
| r: &mut BitReader, |
| header: &mut SliceHeader, |
| ) -> Result<(), String> { |
| if !header.slice_type.is_i() && !header.slice_type.is_si() { |
| header.ref_pic_list_modification_flag_l0 = r.read_bit()?; |
| if header.ref_pic_list_modification_flag_l0 { |
| Parser::parse_ref_pic_list_modification( |
| r, |
| header.num_ref_idx_l0_active_minus1, |
| &mut header.ref_pic_list_modification_l0, |
| )?; |
| } |
| } |
| |
| if header.slice_type.is_b() { |
| header.ref_pic_list_modification_flag_l1 = r.read_bit()?; |
| if header.ref_pic_list_modification_flag_l1 { |
| Parser::parse_ref_pic_list_modification( |
| r, |
| header.num_ref_idx_l1_active_minus1, |
| &mut header.ref_pic_list_modification_l1, |
| )?; |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| fn parse_pred_weight_table( |
| r: &mut BitReader, |
| sps: &Sps, |
| header: &mut SliceHeader, |
| ) -> Result<(), String> { |
| let pt = &mut header.pred_weight_table; |
| pt.luma_log2_weight_denom = r.read_ue_max(7)?; |
| |
| // When luma_weight_l0_flag is equal to 0, luma_weight_l0[i] shall be |
| // inferred to be equal to 2 ^ luma_log2_weight_denom for |
| // RefPicList0[i]. |
| let default_luma_weight = 1 << pt.luma_log2_weight_denom; |
| for i in 0..=header.num_ref_idx_l0_active_minus1 { |
| pt.luma_weight_l0[usize::from(i)] = default_luma_weight; |
| } |
| |
| // When luma_weight_l1_flag is equal to 1, luma_weight_l1[i] shall be |
| // inferred to be equal to 2 ^ luma_log2_weight_denom for |
| // RefPicList1[i]. |
| if header.slice_type.is_b() { |
| for i in 0..=header.num_ref_idx_l1_active_minus1 { |
| pt.luma_weight_l1[usize::from(i)] = default_luma_weight; |
| } |
| } |
| |
| if sps.chroma_array_type() != 0 { |
| pt.chroma_log2_weight_denom = r.read_ue_max(7)?; |
| let default_chroma_weight = 1 << pt.chroma_log2_weight_denom; |
| |
| // When chroma_weight_l0_flag is equal to 0, chroma_weight_l0[i] |
| // shall be inferred to be equal to 2 ^ chroma_log2_weight_denom for |
| // RefPicList0[i]. |
| for i in 0..=header.num_ref_idx_l0_active_minus1 { |
| pt.chroma_weight_l0[usize::from(i)][0] = default_chroma_weight; |
| pt.chroma_weight_l0[usize::from(i)][1] = default_chroma_weight; |
| } |
| |
| // When chroma_weight_l1_flag is equal to 0, chroma_weight_l1[i] |
| // shall be inferred to be equal to 2 ^ chroma_log2_weight_denom for |
| // RefPicList1[i]. |
| for i in 0..=header.num_ref_idx_l1_active_minus1 { |
| pt.chroma_weight_l1[usize::from(i)][0] = default_chroma_weight; |
| pt.chroma_weight_l1[usize::from(i)][1] = default_chroma_weight; |
| } |
| } |
| |
| for i in 0..=header.num_ref_idx_l0_active_minus1 { |
| let luma_weight_l0_flag = r.read_bit()?; |
| |
| if luma_weight_l0_flag { |
| pt.luma_weight_l0[usize::from(i)] = r.read_se_bounded(-128, 127)?; |
| pt.luma_offset_l0[usize::from(i)] = r.read_se_bounded(-128, 127)?; |
| } |
| |
| if sps.chroma_array_type() != 0 { |
| let chroma_weight_l0_flag = r.read_bit()?; |
| if chroma_weight_l0_flag { |
| for j in 0..2 { |
| pt.chroma_weight_l0[usize::from(i)][j] = r.read_se_bounded(-128, 127)?; |
| pt.chroma_offset_l0[usize::from(i)][j] = r.read_se_bounded(-128, 127)?; |
| } |
| } |
| } |
| } |
| |
| if header.slice_type.is_b() { |
| for i in 0..=header.num_ref_idx_l1_active_minus1 { |
| let luma_weight_l1_flag = r.read_bit()?; |
| |
| if luma_weight_l1_flag { |
| pt.luma_weight_l1[usize::from(i)] = r.read_se_bounded(-128, 127)?; |
| pt.luma_offset_l1[usize::from(i)] = r.read_se_bounded(-128, 127)?; |
| } |
| |
| if sps.chroma_array_type() != 0 { |
| let chroma_weight_l1_flag = r.read_bit()?; |
| if chroma_weight_l1_flag { |
| for j in 0..2 { |
| pt.chroma_weight_l1[usize::from(i)][j] = |
| r.read_se_bounded(-128, 127)?; |
| pt.chroma_offset_l1[usize::from(i)][j] = |
| r.read_se_bounded(-128, 127)?; |
| } |
| } |
| } |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| fn parse_dec_ref_pic_marking( |
| r: &mut BitReader, |
| nalu: &Nalu, |
| header: &mut SliceHeader, |
| ) -> Result<(), String> { |
| let rpm = &mut header.dec_ref_pic_marking; |
| |
| let num_bits_left = r.num_bits_left(); |
| if nalu.header.idr_pic_flag { |
| rpm.no_output_of_prior_pics_flag = r.read_bit()?; |
| rpm.long_term_reference_flag = r.read_bit()?; |
| } else { |
| rpm.adaptive_ref_pic_marking_mode_flag = r.read_bit()?; |
| |
| if rpm.adaptive_ref_pic_marking_mode_flag { |
| loop { |
| let mut marking = RefPicMarkingInner::default(); |
| |
| let mem_mgmt_ctrl_op = r.read_ue_max::<u8>(6)?; |
| marking.memory_management_control_operation = mem_mgmt_ctrl_op; |
| |
| if mem_mgmt_ctrl_op == 0 { |
| break; |
| } |
| |
| if mem_mgmt_ctrl_op == 1 || mem_mgmt_ctrl_op == 3 { |
| marking.difference_of_pic_nums_minus1 = r.read_ue()?; |
| } |
| |
| if mem_mgmt_ctrl_op == 2 { |
| marking.long_term_pic_num = r.read_ue()?; |
| } |
| |
| if mem_mgmt_ctrl_op == 3 || mem_mgmt_ctrl_op == 6 { |
| marking.long_term_frame_idx = r.read_ue()?; |
| } |
| |
| if mem_mgmt_ctrl_op == 4 { |
| marking.max_long_term_frame_idx = |
| MaxLongTermFrameIdx::from_value_plus1(r.read_ue()?); |
| } |
| |
| rpm.inner.push(marking); |
| } |
| } |
| } |
| header.dec_ref_pic_marking_bit_size = num_bits_left - r.num_bits_left(); |
| |
| Ok(()) |
| } |
| |
| pub fn parse_slice_header<'a>(&self, nalu: Nalu<'a>) -> Result<Slice<'a>, String> { |
| if !matches!( |
| nalu.header.type_, |
| NaluType::Slice |
| | NaluType::SliceDpa |
| | NaluType::SliceDpb |
| | NaluType::SliceDpc |
| | NaluType::SliceIdr |
| | NaluType::SliceExt |
| ) { |
| return Err(format!( |
| "Invalid NALU type: {:?} is not a slice NALU", |
| nalu.header.type_ |
| )); |
| } |
| |
| let data = nalu.as_ref(); |
| // Skip the header |
| let mut r = BitReader::new(&data[nalu.header.len()..], true); |
| |
| let mut header = SliceHeader { |
| first_mb_in_slice: r.read_ue()?, |
| ..Default::default() |
| }; |
| |
| let slice_type = r.read_ue_max::<u8>(9)? % 5; |
| header.slice_type = SliceType::try_from(slice_type)?; |
| |
| header.pic_parameter_set_id = r.read_ue()?; |
| |
| let pps = self |
| .get_pps(header.pic_parameter_set_id) |
| .ok_or::<String>(format!( |
| "Could not get PPS for pic_parameter_set_id {}", |
| header.pic_parameter_set_id |
| ))?; |
| |
| let sps = &pps.sps; |
| |
| if sps.separate_colour_plane_flag { |
| header.colour_plane_id = r.read_bits(2)?; |
| } |
| |
| header.frame_num = r.read_bits(usize::from(sps.log2_max_frame_num_minus4) + 4)?; |
| |
| if !sps.frame_mbs_only_flag { |
| header.field_pic_flag = r.read_bit()?; |
| if header.field_pic_flag { |
| header.bottom_field_flag = r.read_bit()?; |
| } |
| } |
| |
| if header.field_pic_flag { |
| header.max_pic_num = 2 * sps.max_frame_num(); |
| } else { |
| header.max_pic_num = sps.max_frame_num(); |
| } |
| |
| if nalu.header.idr_pic_flag { |
| header.idr_pic_id = r.read_ue_max(0xffff)?; |
| } |
| |
| let num_bits_left = r.num_bits_left(); |
| if sps.pic_order_cnt_type == 0 { |
| header.pic_order_cnt_lsb = |
| r.read_bits(usize::from(sps.log2_max_pic_order_cnt_lsb_minus4) + 4)?; |
| |
| if pps.bottom_field_pic_order_in_frame_present_flag && !header.field_pic_flag { |
| header.delta_pic_order_cnt_bottom = r.read_se()?; |
| } |
| } |
| |
| if sps.pic_order_cnt_type == 1 && !sps.delta_pic_order_always_zero_flag { |
| header.delta_pic_order_cnt[0] = r.read_se()?; |
| if pps.bottom_field_pic_order_in_frame_present_flag && !header.field_pic_flag { |
| header.delta_pic_order_cnt[1] = r.read_se()?; |
| } |
| } |
| header.pic_order_cnt_bit_size = num_bits_left - r.num_bits_left(); |
| |
| if pps.redundant_pic_cnt_present_flag { |
| header.redundant_pic_cnt = r.read_ue_max(127)?; |
| } |
| |
| if header.slice_type.is_b() { |
| header.direct_spatial_mv_pred_flag = r.read_bit()?; |
| } |
| |
| if header.slice_type.is_p() || header.slice_type.is_sp() || header.slice_type.is_b() { |
| header.num_ref_idx_active_override_flag = r.read_bit()?; |
| if header.num_ref_idx_active_override_flag { |
| header.num_ref_idx_l0_active_minus1 = r.read_ue()?; |
| if header.slice_type.is_b() { |
| header.num_ref_idx_l1_active_minus1 = r.read_ue()?; |
| } |
| } else { |
| header.num_ref_idx_l0_active_minus1 = pps.num_ref_idx_l0_default_active_minus1; |
| if header.slice_type.is_b() { |
| header.num_ref_idx_l1_active_minus1 = pps.num_ref_idx_l1_default_active_minus1; |
| } |
| } |
| } |
| |
| if header.field_pic_flag { |
| if header.num_ref_idx_l0_active_minus1 > 31 || header.num_ref_idx_l1_active_minus1 > 31 |
| { |
| return Err("Broken Data".into()); |
| } |
| } else if header.num_ref_idx_l0_active_minus1 > 15 |
| || header.num_ref_idx_l1_active_minus1 > 15 |
| { |
| return Err("Broken Data".into()); |
| } |
| |
| if let NaluType::SliceExt = nalu.header.type_ { |
| return Err("Stream contain unsupported/unimplemented NALs".into()); |
| } |
| |
| Parser::parse_ref_pic_list_modifications(&mut r, &mut header)?; |
| |
| if (pps.weighted_pred_flag && (header.slice_type.is_p() || header.slice_type.is_sp())) |
| || (pps.weighted_bipred_idc == 1 && header.slice_type.is_b()) |
| { |
| Parser::parse_pred_weight_table(&mut r, sps, &mut header)?; |
| } |
| |
| if nalu.header.ref_idc != 0 { |
| Parser::parse_dec_ref_pic_marking(&mut r, &nalu, &mut header)?; |
| } |
| |
| if pps.entropy_coding_mode_flag && !header.slice_type.is_i() && !header.slice_type.is_si() { |
| header.cabac_init_idc = r.read_ue_max(2)?; |
| } |
| |
| header.slice_qp_delta = r.read_se_bounded(-87, 77)?; |
| |
| if header.slice_type.is_sp() || header.slice_type.is_si() { |
| if header.slice_type.is_sp() { |
| header.sp_for_switch_flag = r.read_bit()?; |
| } |
| |
| header.slice_qs_delta = r.read_se_bounded(-51, 51)?; |
| } |
| |
| if pps.deblocking_filter_control_present_flag { |
| header.disable_deblocking_filter_idc = r.read_ue_max(2)?; |
| |
| if header.disable_deblocking_filter_idc != 1 { |
| header.slice_alpha_c0_offset_div2 = r.read_se_bounded(-6, 6)?; |
| header.slice_beta_offset_div2 = r.read_se_bounded(-6, 6)?; |
| } |
| } |
| |
| if pps.num_slice_groups_minus1 > 0 { |
| return Err("Stream contain unsupported/unimplemented NALs".into()); |
| } |
| |
| let epb = r.num_epb(); |
| header.header_bit_size = (nalu.size - epb) * 8 - r.num_bits_left(); |
| |
| header.n_emulation_prevention_bytes = epb; |
| |
| Ok(Slice { header, nalu }) |
| } |
| |
| pub fn get_sps(&self, sps_id: u8) -> Option<&Rc<Sps>> { |
| self.active_spses.get(&sps_id) |
| } |
| |
| pub fn get_pps(&self, pps_id: u8) -> Option<&Rc<Pps>> { |
| self.active_ppses.get(&pps_id) |
| } |
| } |
| |
| #[derive(Debug)] |
| pub struct NaluHeader { |
| pub ref_idc: u8, |
| pub type_: NaluType, |
| pub idr_pic_flag: bool, |
| } |
| |
| impl Header for NaluHeader { |
| fn parse<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<Self, String> { |
| let mut byte_buf = [0u8; 1]; |
| cursor |
| .read_exact(&mut byte_buf) |
| .map_err(|_| String::from("Broken Data"))?; |
| let byte = byte_buf[0]; |
| let _ = cursor.seek(SeekFrom::Current(-1 * byte_buf.len() as i64)); |
| |
| let type_ = NaluType::try_from(byte & 0x1f)?; |
| |
| if let NaluType::SliceExt = type_ { |
| return Err("Stream contain unsupported/unimplemented NALs".into()); |
| } |
| |
| let ref_idc = (byte & 0x60) >> 5; |
| let idr_pic_flag = matches!(type_, NaluType::SliceIdr); |
| |
| Ok(NaluHeader { |
| ref_idc, |
| type_, |
| idr_pic_flag, |
| }) |
| } |
| |
| fn is_end(&self) -> bool { |
| matches!(self.type_, NaluType::SeqEnd | NaluType::StreamEnd) |
| } |
| |
| fn len(&self) -> usize { |
| 1 |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use std::io::Cursor; |
| |
| use crate::codec::h264::parser::Level; |
| use crate::codec::h264::parser::MaxLongTermFrameIdx; |
| use crate::codec::h264::parser::Nalu; |
| use crate::codec::h264::parser::NaluType; |
| use crate::codec::h264::parser::Parser; |
| |
| const STREAM_TEST_25_FPS: &[u8] = include_bytes!("test_data/test-25fps.h264"); |
| const STREAM_TEST_25_FPS_NUM_NALUS: usize = 759; |
| |
| const STREAM_TEST_25_FPS_SLICE_0: &[u8] = |
| include_bytes!("test_data/test-25fps-h264-slice-data-0.bin"); |
| const STREAM_TEST_25_FPS_SLICE_2: &[u8] = |
| include_bytes!("test_data/test-25fps-h264-slice-data-2.bin"); |
| const STREAM_TEST_25_FPS_SLICE_4: &[u8] = |
| include_bytes!("test_data/test-25fps-h264-slice-data-4.bin"); |
| |
| /// This test is adapted from chromium, available at media/video/h264_parser_unittest.cc |
| #[test] |
| fn parse_nalus_from_stream_file() { |
| let mut cursor = Cursor::new(STREAM_TEST_25_FPS); |
| let mut num_nalus = 0; |
| while Nalu::next(&mut cursor).is_ok() { |
| num_nalus += 1; |
| } |
| |
| assert_eq!(num_nalus, STREAM_TEST_25_FPS_NUM_NALUS) |
| } |
| |
| /// The results were manually extracted from the GStreamer parser using GDB |
| /// (gsth264parser.c) in order to compare both implementations using the |
| /// following pipeline: |
| /// gst-launch-1.0 filesrc location=test-25fps.h264 ! h264parse ! 'video/x-h264,stream-format=byte-stream' ! vah264dec ! fakevideosink |
| #[test] |
| fn parse_test25fps() { |
| let mut cursor = Cursor::new(STREAM_TEST_25_FPS); |
| let mut sps_ids = Vec::new(); |
| let mut pps_ids = Vec::new(); |
| let mut slices = Vec::new(); |
| |
| let mut parser = Parser::default(); |
| |
| while let Ok(nalu) = Nalu::next(&mut cursor) { |
| match nalu.header.type_ { |
| NaluType::Slice |
| | NaluType::SliceDpa |
| | NaluType::SliceDpb |
| | NaluType::SliceDpc |
| | NaluType::SliceIdr |
| | NaluType::SliceExt => { |
| let slice = parser.parse_slice_header(nalu).unwrap(); |
| slices.push(slice); |
| } |
| NaluType::Sps => { |
| let sps = parser.parse_sps(&nalu).unwrap(); |
| sps_ids.push(sps.seq_parameter_set_id); |
| } |
| NaluType::Pps => { |
| let pps = parser.parse_pps(&nalu).unwrap(); |
| pps_ids.push(pps.pic_parameter_set_id); |
| } |
| _ => { |
| continue; |
| } |
| } |
| } |
| |
| for sps_id in &sps_ids { |
| // four identical SPSes in this stream |
| let sps = parser.get_sps(*sps_id).unwrap(); |
| |
| assert_eq!(sps.seq_parameter_set_id, 0); |
| assert_eq!(sps.profile_idc, 77); |
| assert!(!sps.constraint_set0_flag); |
| assert!(sps.constraint_set1_flag); |
| assert!(!sps.constraint_set2_flag); |
| assert!(!sps.constraint_set3_flag); |
| assert!(!sps.constraint_set4_flag); |
| assert!(!sps.constraint_set5_flag); |
| assert_eq!(sps.level_idc, Level::L1_3); |
| assert_eq!(sps.chroma_format_idc, 1); |
| assert!(!sps.separate_colour_plane_flag); |
| assert_eq!(sps.bit_depth_luma_minus8, 0); |
| assert_eq!(sps.bit_depth_chroma_minus8, 0); |
| assert!(!sps.qpprime_y_zero_transform_bypass_flag); |
| assert!(!sps.seq_scaling_matrix_present_flag); |
| |
| for outer in &sps.scaling_lists_4x4 { |
| for inner in outer { |
| assert_eq!(*inner, 16); |
| } |
| } |
| |
| for outer in &sps.scaling_lists_8x8 { |
| for inner in outer { |
| assert_eq!(*inner, 16); |
| } |
| } |
| |
| assert_eq!(sps.log2_max_frame_num_minus4, 1); |
| assert_eq!(sps.pic_order_cnt_type, 0); |
| assert_eq!(sps.log2_max_pic_order_cnt_lsb_minus4, 3); |
| assert!(!sps.delta_pic_order_always_zero_flag); |
| assert_eq!(sps.offset_for_non_ref_pic, 0); |
| assert_eq!(sps.offset_for_top_to_bottom_field, 0); |
| assert_eq!(sps.num_ref_frames_in_pic_order_cnt_cycle, 0); |
| |
| for offset in sps.offset_for_ref_frame { |
| assert_eq!(offset, 0); |
| } |
| |
| assert_eq!(sps.max_num_ref_frames, 2); |
| assert!(!sps.gaps_in_frame_num_value_allowed_flag); |
| assert_eq!(sps.pic_width_in_mbs_minus1, 19); |
| assert_eq!(sps.pic_height_in_map_units_minus1, 14); |
| assert!(sps.frame_mbs_only_flag); |
| assert!(!sps.mb_adaptive_frame_field_flag); |
| assert!(!sps.direct_8x8_inference_flag); |
| assert!(!sps.frame_cropping_flag); |
| assert_eq!(sps.frame_crop_left_offset, 0); |
| assert_eq!(sps.frame_crop_right_offset, 0); |
| assert_eq!(sps.frame_crop_top_offset, 0); |
| assert_eq!(sps.frame_crop_bottom_offset, 0); |
| assert_eq!(sps.chroma_array_type(), 1); |
| assert_eq!(sps.max_frame_num(), 32); |
| assert_eq!(sps.width(), 320); |
| assert_eq!(sps.height(), 240); |
| } |
| |
| for pps_id in &pps_ids { |
| // four identical SPSes in this stream |
| let pps = parser.get_pps(*pps_id).unwrap(); |
| assert_eq!(pps.pic_parameter_set_id, 0); |
| assert_eq!(pps.seq_parameter_set_id, 0); |
| assert!(pps.bottom_field_pic_order_in_frame_present_flag); |
| assert_eq!(pps.num_slice_groups_minus1, 0); |
| assert_eq!(pps.num_ref_idx_l0_default_active_minus1, 0); |
| assert_eq!(pps.num_ref_idx_l1_default_active_minus1, 0); |
| assert!(!pps.weighted_pred_flag); |
| assert_eq!(pps.weighted_bipred_idc, 0); |
| assert_eq!(pps.pic_init_qp_minus26, 2); |
| assert_eq!(pps.pic_init_qs_minus26, 0); |
| assert_eq!(pps.chroma_qp_index_offset, 0); |
| assert!(!pps.deblocking_filter_control_present_flag); |
| assert!(!pps.constrained_intra_pred_flag); |
| assert!(!pps.redundant_pic_cnt_present_flag); |
| assert!(!pps.transform_8x8_mode_flag); |
| |
| for outer in &pps.scaling_lists_4x4 { |
| for inner in outer { |
| assert_eq!(*inner, 16); |
| } |
| } |
| |
| for outer in &pps.scaling_lists_8x8 { |
| for inner in outer { |
| assert_eq!(*inner, 16); |
| } |
| } |
| |
| assert_eq!(pps.second_chroma_qp_index_offset, 0); |
| assert!(!pps.pic_scaling_matrix_present_flag); |
| } |
| |
| // test an I slice |
| let hdr = &slices[0].header; |
| let nalu = &slices[0].nalu; |
| assert_eq!(nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_0); |
| |
| assert_eq!(hdr.first_mb_in_slice, 0); |
| assert!(hdr.slice_type.is_i()); |
| assert_eq!(hdr.colour_plane_id, 0); |
| assert_eq!(hdr.frame_num, 0); |
| assert!(!hdr.field_pic_flag); |
| assert!(!hdr.bottom_field_flag); |
| assert_eq!(hdr.idr_pic_id, 0); |
| assert_eq!(hdr.pic_order_cnt_lsb, 0); |
| assert_eq!(hdr.delta_pic_order_cnt_bottom, 0); |
| assert_eq!(hdr.delta_pic_order_cnt[0], 0); |
| assert_eq!(hdr.delta_pic_order_cnt[1], 0); |
| assert_eq!(hdr.redundant_pic_cnt, 0); |
| assert!(!hdr.direct_spatial_mv_pred_flag); |
| assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); |
| assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); |
| assert!(!hdr.ref_pic_list_modification_flag_l0); |
| |
| assert_eq!(hdr.ref_pic_list_modification_l0.len(), 0); |
| |
| for rplm in &hdr.ref_pic_list_modification_l0 { |
| assert_eq!(rplm.modification_of_pic_nums_idc, 0); |
| assert_eq!(rplm.abs_diff_pic_num_minus1, 0); |
| assert_eq!(rplm.long_term_pic_num, 0); |
| assert_eq!(rplm.abs_diff_view_idx_minus1, 0); |
| } |
| |
| assert!(!hdr.ref_pic_list_modification_flag_l1); |
| assert_eq!(hdr.ref_pic_list_modification_l1.len(), 0); |
| |
| for rplm in &hdr.ref_pic_list_modification_l1 { |
| assert_eq!(rplm.modification_of_pic_nums_idc, 0); |
| assert_eq!(rplm.abs_diff_pic_num_minus1, 0); |
| assert_eq!(rplm.long_term_pic_num, 0); |
| assert_eq!(rplm.abs_diff_view_idx_minus1, 0); |
| } |
| |
| // Safe because this type does not have any references |
| assert_eq!(hdr.pred_weight_table, unsafe { std::mem::zeroed() }); |
| |
| assert_eq!(hdr.dec_ref_pic_marking, Default::default()); |
| |
| assert_eq!(hdr.cabac_init_idc, 0); |
| assert_eq!(hdr.slice_qp_delta, 12); |
| assert_eq!(hdr.slice_qs_delta, 0); |
| assert_eq!(hdr.disable_deblocking_filter_idc, 0); |
| assert_eq!(hdr.slice_alpha_c0_offset_div2, 0); |
| assert_eq!(hdr.slice_beta_offset_div2, 0); |
| assert_eq!(hdr.max_pic_num, 32); |
| assert_eq!(hdr.header_bit_size, 38); |
| assert!(!hdr.num_ref_idx_active_override_flag); |
| |
| // test a P slice |
| let hdr = &slices[2].header; |
| let nalu = &slices[2].nalu; |
| assert_eq!(nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_2); |
| |
| assert_eq!(hdr.first_mb_in_slice, 0); |
| assert!(hdr.slice_type.is_p()); |
| assert_eq!(hdr.colour_plane_id, 0); |
| assert_eq!(hdr.frame_num, 1); |
| assert!(!hdr.field_pic_flag); |
| assert!(!hdr.bottom_field_flag); |
| assert_eq!(hdr.idr_pic_id, 0); |
| assert_eq!(hdr.pic_order_cnt_lsb, 4); |
| assert_eq!(hdr.delta_pic_order_cnt_bottom, 0); |
| assert_eq!(hdr.delta_pic_order_cnt[0], 0); |
| assert_eq!(hdr.delta_pic_order_cnt[1], 0); |
| assert_eq!(hdr.redundant_pic_cnt, 0); |
| assert!(!hdr.direct_spatial_mv_pred_flag); |
| assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); |
| assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); |
| assert!(!hdr.ref_pic_list_modification_flag_l0); |
| |
| assert_eq!(hdr.ref_pic_list_modification_l0.len(), 0); |
| |
| for rplm in &hdr.ref_pic_list_modification_l0 { |
| assert_eq!(rplm.modification_of_pic_nums_idc, 0); |
| assert_eq!(rplm.abs_diff_pic_num_minus1, 0); |
| assert_eq!(rplm.long_term_pic_num, 0); |
| assert_eq!(rplm.abs_diff_view_idx_minus1, 0); |
| } |
| |
| assert!(!hdr.ref_pic_list_modification_flag_l1); |
| assert_eq!(hdr.ref_pic_list_modification_l1.len(), 0); |
| |
| for rplm in &hdr.ref_pic_list_modification_l1 { |
| assert_eq!(rplm.modification_of_pic_nums_idc, 0); |
| assert_eq!(rplm.abs_diff_pic_num_minus1, 0); |
| assert_eq!(rplm.long_term_pic_num, 0); |
| assert_eq!(rplm.abs_diff_view_idx_minus1, 0); |
| } |
| |
| // Safe because this type does not have any references |
| assert_eq!(hdr.pred_weight_table, unsafe { std::mem::zeroed() }); |
| |
| assert_eq!(hdr.dec_ref_pic_marking, Default::default()); |
| |
| assert_eq!(hdr.cabac_init_idc, 0); |
| assert_eq!(hdr.slice_qp_delta, 0); |
| assert_eq!(hdr.slice_qs_delta, 0); |
| assert_eq!(hdr.disable_deblocking_filter_idc, 0); |
| assert_eq!(hdr.slice_alpha_c0_offset_div2, 0); |
| assert_eq!(hdr.slice_beta_offset_div2, 0); |
| assert_eq!(hdr.max_pic_num, 32); |
| assert_eq!(hdr.header_bit_size, 28); |
| assert!(!hdr.num_ref_idx_active_override_flag); |
| |
| // test a B slice |
| let hdr = &slices[4].header; |
| let nalu = &slices[4].nalu; |
| assert_eq!(nalu.as_ref(), STREAM_TEST_25_FPS_SLICE_4); |
| |
| assert_eq!(hdr.first_mb_in_slice, 0); |
| assert!(hdr.slice_type.is_b()); |
| assert_eq!(hdr.colour_plane_id, 0); |
| assert_eq!(hdr.frame_num, 2); |
| assert!(!hdr.field_pic_flag); |
| assert!(!hdr.bottom_field_flag); |
| assert_eq!(hdr.idr_pic_id, 0); |
| assert_eq!(hdr.pic_order_cnt_lsb, 2); |
| assert_eq!(hdr.delta_pic_order_cnt_bottom, 0); |
| assert_eq!(hdr.delta_pic_order_cnt[0], 0); |
| assert_eq!(hdr.delta_pic_order_cnt[1], 0); |
| assert_eq!(hdr.redundant_pic_cnt, 0); |
| assert!(hdr.direct_spatial_mv_pred_flag); |
| assert_eq!(hdr.num_ref_idx_l0_active_minus1, 0); |
| assert_eq!(hdr.num_ref_idx_l1_active_minus1, 0); |
| assert!(!hdr.ref_pic_list_modification_flag_l0); |
| |
| assert_eq!(hdr.ref_pic_list_modification_l0.len(), 0); |
| |
| for rplm in &hdr.ref_pic_list_modification_l0 { |
| assert_eq!(rplm.modification_of_pic_nums_idc, 0); |
| assert_eq!(rplm.abs_diff_pic_num_minus1, 0); |
| assert_eq!(rplm.long_term_pic_num, 0); |
| assert_eq!(rplm.abs_diff_view_idx_minus1, 0); |
| } |
| |
| assert!(!hdr.ref_pic_list_modification_flag_l1); |
| assert_eq!(hdr.ref_pic_list_modification_l1.len(), 0); |
| |
| for rplm in &hdr.ref_pic_list_modification_l1 { |
| assert_eq!(rplm.modification_of_pic_nums_idc, 0); |
| assert_eq!(rplm.abs_diff_pic_num_minus1, 0); |
| assert_eq!(rplm.long_term_pic_num, 0); |
| assert_eq!(rplm.abs_diff_view_idx_minus1, 0); |
| } |
| |
| // Safe because this type does not have any references |
| assert_eq!(hdr.pred_weight_table, unsafe { std::mem::zeroed() }); |
| |
| assert_eq!(hdr.dec_ref_pic_marking, Default::default()); |
| |
| assert_eq!(hdr.cabac_init_idc, 0); |
| assert_eq!(hdr.slice_qp_delta, 16); |
| assert_eq!(hdr.slice_qs_delta, 0); |
| assert_eq!(hdr.disable_deblocking_filter_idc, 0); |
| assert_eq!(hdr.slice_alpha_c0_offset_div2, 0); |
| assert_eq!(hdr.slice_beta_offset_div2, 0); |
| assert_eq!(hdr.max_pic_num, 32); |
| assert_eq!(hdr.header_bit_size, 41); |
| assert!(!hdr.num_ref_idx_active_override_flag); |
| } |
| |
| #[test] |
| fn invalid_sps_crop_width() { |
| // This SPS contains invalid frame_crop_*_offset settings. This led to |
| // unconditional panic in the parser in the past. This test make sure a |
| // panic is avoided. |
| let invalid_sps = vec![ |
| 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x0a, 0xfb, 0xb0, 0x32, 0xc0, 0xca, 0x80, |
| ]; |
| |
| let mut cursor = Cursor::new(invalid_sps.as_ref()); |
| let mut parser = Parser::default(); |
| |
| while let Ok(nalu) = Nalu::next(&mut cursor) { |
| assert_eq!(nalu.header.type_, NaluType::Sps); |
| parser.parse_sps(&nalu).unwrap_err(); |
| } |
| } |
| |
| #[test] |
| fn max_long_term_frame_idx() { |
| assert_eq!( |
| MaxLongTermFrameIdx::from_value_plus1(0), |
| MaxLongTermFrameIdx::NoLongTermFrameIndices |
| ); |
| assert_eq!( |
| MaxLongTermFrameIdx::NoLongTermFrameIndices.to_value_plus1(), |
| 0 |
| ); |
| |
| assert_eq!( |
| MaxLongTermFrameIdx::from_value_plus1(1), |
| MaxLongTermFrameIdx::Idx(0) |
| ); |
| assert_eq!(MaxLongTermFrameIdx::Idx(0).to_value_plus1(), 1); |
| |
| assert_eq!( |
| MaxLongTermFrameIdx::from_value_plus1(25), |
| MaxLongTermFrameIdx::Idx(24) |
| ); |
| assert_eq!(MaxLongTermFrameIdx::Idx(24).to_value_plus1(), 25); |
| |
| // Check PartialOrd<u32> implementation. |
| assert!(MaxLongTermFrameIdx::NoLongTermFrameIndices < 0); |
| assert_ne!(MaxLongTermFrameIdx::NoLongTermFrameIndices, 0); |
| assert_eq!(MaxLongTermFrameIdx::Idx(0), 0); |
| assert!(MaxLongTermFrameIdx::Idx(0) < 1); |
| assert_eq!(MaxLongTermFrameIdx::Idx(24), 24); |
| assert!(MaxLongTermFrameIdx::Idx(24) < 25); |
| } |
| } |