diff --git a/Cargo.toml b/Cargo.toml index 5c68651..5e1a331 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ hex-slice = "0.1.4" memchr = "2.1.1" rfc6381-codec = "0.1" log = "0.4" +lazy_static = "1.4" [dev-dependencies] hex-literal = "0.4.1" diff --git a/src/nal/pps.rs b/src/nal/pps.rs index 8b4f6d2..5a5a4a4 100644 --- a/src/nal/pps.rs +++ b/src/nal/pps.rs @@ -1,4 +1,5 @@ use super::sps; +use crate::nal::sps::SeqParameterSet; use crate::nal::sps::{SeqParamSetId, SeqParamSetIdError}; use crate::rbsp::BitRead; use crate::{rbsp, Context}; @@ -14,6 +15,14 @@ pub enum PpsError { BadPicParamSetId(PicParamSetIdError), BadSeqParamSetId(SeqParamSetIdError), ScalingMatrix(sps::ScalingMatrixError), + InvalidSecondChromaQpIndexOffset(i32), + InvalidPicInitQpMinus26(i32), + InvalidPicInitQsMinus26(i32), + InvalidChromaQpIndexOffset(i32), + InvalidRunLengthMinus1(u32), + InvalidTopLeft(u32), + InvalidBottomRight(u32), + InvalidSliceGroupChangeRateMinus1(u32), } impl From for PpsError { @@ -45,11 +54,21 @@ pub struct SliceRect { bottom_right: u32, } impl SliceRect { - fn read(r: &mut R) -> Result { - Ok(SliceRect { + fn read(r: &mut R, sps: &SeqParameterSet) -> Result { + let rect = SliceRect { top_left: r.read_ue("top_left")?, bottom_right: r.read_ue("bottom_right")?, - }) + }; + if rect.top_left > rect.bottom_right { + return Err(PpsError::InvalidTopLeft(rect.top_left)); + } + if rect.bottom_right > sps.pic_size_in_map_units() { + return Err(PpsError::InvalidBottomRight(rect.bottom_right)); + } + if rect.top_left % sps.pic_width_in_mbs() > rect.bottom_right % sps.pic_width_in_mbs() { + return Err(PpsError::InvalidTopLeft(rect.top_left)); + } + Ok(rect) } } @@ -76,25 +95,38 @@ pub enum SliceGroup { }, } impl SliceGroup { - fn read(r: &mut R, num_slice_groups_minus1: u32) -> Result { + fn read( + r: &mut R, + num_slice_groups_minus1: u32, + sps: &SeqParameterSet, + ) -> Result { let slice_group_map_type = r.read_ue("slice_group_map_type")?; match slice_group_map_type { 0 => Ok(SliceGroup::Interleaved { - run_length_minus1: Self::read_run_lengths(r, num_slice_groups_minus1)?, + run_length_minus1: Self::read_run_lengths(r, num_slice_groups_minus1, sps)?, }), 1 => Ok(SliceGroup::Dispersed { num_slice_groups_minus1, }), 2 => Ok(SliceGroup::ForegroundAndLeftover { - rectangles: Self::read_rectangles(r, num_slice_groups_minus1)?, - }), - 3 | 4 | 5 => Ok(SliceGroup::Changing { - change_type: SliceGroupChangeType::from_id(slice_group_map_type)?, - num_slice_groups_minus1, - slice_group_change_direction_flag: r - .read_bool("slice_group_change_direction_flag")?, - slice_group_change_rate_minus1: r.read_ue("slice_group_change_rate_minus1")?, + rectangles: Self::read_rectangles(r, num_slice_groups_minus1, sps)?, }), + 3 | 4 | 5 => { + let slice_group_change_direction_flag = + r.read_bool("slice_group_change_direction_flag")?; + let slice_group_change_rate_minus1 = r.read_ue("slice_group_change_rate_minus1")?; + if slice_group_change_rate_minus1 > sps.pic_size_in_map_units() - 1 { + return Err(PpsError::InvalidSliceGroupChangeRateMinus1( + slice_group_change_rate_minus1, + )); + } + Ok(SliceGroup::Changing { + change_type: SliceGroupChangeType::from_id(slice_group_map_type)?, + num_slice_groups_minus1, + slice_group_change_direction_flag, + slice_group_change_rate_minus1, + }) + } 6 => Ok(SliceGroup::ExplicitAssignment { num_slice_groups_minus1, slice_group_id: Self::read_group_ids(r, num_slice_groups_minus1)?, @@ -106,21 +138,27 @@ impl SliceGroup { fn read_run_lengths( r: &mut R, num_slice_groups_minus1: u32, + sps: &SeqParameterSet, ) -> Result, PpsError> { - let mut run_length_minus1 = Vec::with_capacity(num_slice_groups_minus1 as usize + 1); + let mut run_lengths = Vec::with_capacity(num_slice_groups_minus1 as usize + 1); for _ in 0..num_slice_groups_minus1 + 1 { - run_length_minus1.push(r.read_ue("run_length_minus1")?); + let run_length_minus1 = r.read_ue("run_length_minus1")?; + if run_length_minus1 > sps.pic_size_in_map_units() - 1 { + return Err(PpsError::InvalidRunLengthMinus1(run_length_minus1)); + } + run_lengths.push(run_length_minus1); } - Ok(run_length_minus1) + Ok(run_lengths) } fn read_rectangles( r: &mut R, num_slice_groups_minus1: u32, + seq_parameter_set: &SeqParameterSet, ) -> Result, PpsError> { let mut run_length_minus1 = Vec::with_capacity(num_slice_groups_minus1 as usize + 1); for _ in 0..num_slice_groups_minus1 + 1 { - run_length_minus1.push(SliceRect::read(r)?); + run_length_minus1.push(SliceRect::read(r, seq_parameter_set)?); } Ok(run_length_minus1) } @@ -196,11 +234,18 @@ impl PicParameterSetExtra { ) -> Result, PpsError> { Ok(if r.has_more_rbsp_data("transform_8x8_mode_flag")? { let transform_8x8_mode_flag = r.read_bool("transform_8x8_mode_flag")?; - Some(PicParameterSetExtra { + let extra = PicParameterSetExtra { transform_8x8_mode_flag, pic_scaling_matrix: PicScalingMatrix::read(r, sps, transform_8x8_mode_flag)?, second_chroma_qp_index_offset: r.read_se("second_chroma_qp_index_offset")?, - }) + }; + if extra.second_chroma_qp_index_offset < -12 || extra.second_chroma_qp_index_offset > 12 + { + return Err(PpsError::InvalidSecondChromaQpIndexOffset( + extra.second_chroma_qp_index_offset, + )); + } + Some(extra) } else { None }) @@ -261,7 +306,7 @@ impl PicParameterSet { entropy_coding_mode_flag: r.read_bool("entropy_coding_mode_flag")?, bottom_field_pic_order_in_frame_present_flag: r .read_bool("bottom_field_pic_order_in_frame_present_flag")?, - slice_groups: Self::read_slice_groups(&mut r)?, + slice_groups: Self::read_slice_groups(&mut r, seq_parameter_set)?, num_ref_idx_l0_default_active_minus1: read_num_ref_idx( &mut r, "num_ref_idx_l0_default_active_minus1", @@ -281,11 +326,28 @@ impl PicParameterSet { redundant_pic_cnt_present_flag: r.read_bool("redundant_pic_cnt_present_flag")?, extension: PicParameterSetExtra::read(&mut r, seq_parameter_set)?, }; + let qp_bd_offset_y = 6 * seq_parameter_set.chroma_info.bit_depth_luma_minus8; + if pps.pic_init_qp_minus26 < -(26 + i32::from(qp_bd_offset_y)) + || pps.pic_init_qp_minus26 > 25 + { + return Err(PpsError::InvalidPicInitQpMinus26(pps.pic_init_qp_minus26)); + } + if pps.pic_init_qs_minus26 < -26 || pps.pic_init_qs_minus26 > 25 { + return Err(PpsError::InvalidPicInitQsMinus26(pps.pic_init_qs_minus26)); + } + if pps.chroma_qp_index_offset < -12 || pps.chroma_qp_index_offset > 12 { + return Err(PpsError::InvalidChromaQpIndexOffset( + pps.chroma_qp_index_offset, + )); + } r.finish_rbsp()?; Ok(pps) } - fn read_slice_groups(r: &mut R) -> Result, PpsError> { + fn read_slice_groups( + r: &mut R, + sps: &SeqParameterSet, + ) -> Result, PpsError> { let num_slice_groups_minus1 = r.read_ue("num_slice_groups_minus1")?; if num_slice_groups_minus1 > 7 { // 7 is the maximum allowed in any profile; some profiles restrict it to 0. @@ -294,7 +356,7 @@ impl PicParameterSet { )); } Ok(if num_slice_groups_minus1 > 0 { - Some(SliceGroup::read(r, num_slice_groups_minus1)?) + Some(SliceGroup::read(r, num_slice_groups_minus1, sps)?) } else { None }) @@ -312,6 +374,7 @@ fn read_num_ref_idx(r: &mut R, name: &'static str) -> Result Level::L5, 51 => Level::L5_1, 52 => Level::L5_2, + 60 => Level::L6, + 61 => Level::L6_1, + 62 => Level::L6_2, _ => Level::Unknown(level_idc), } } @@ -230,6 +246,9 @@ impl Level { Level::L5 => 50, Level::L5_1 => 51, Level::L5_2 => 52, + Level::L6 => 60, + Level::L6_1 => 61, + Level::L6_2 => 62, Level::Unknown(level_idc) => level_idc, } } @@ -815,18 +834,77 @@ pub struct BitstreamRestrictions { pub max_dec_frame_buffering: u32, } impl BitstreamRestrictions { - fn read(r: &mut R) -> Result, BitReaderError> { + fn read( + r: &mut R, + sps: &SeqParameterSet, + ) -> Result, SpsError> { let bitstream_restriction_flag = r.read_bool("bitstream_restriction_flag")?; Ok(if bitstream_restriction_flag { + let motion_vectors_over_pic_boundaries_flag = + r.read_bool("motion_vectors_over_pic_boundaries_flag")?; + let max_bytes_per_pic_denom = r.read_ue("max_bytes_per_pic_denom")?; + if max_bytes_per_pic_denom > 16 { + return Err(SpsError::FieldValueTooLarge { + name: "max_bytes_per_pic_denom", + value: max_bytes_per_pic_denom, + }); + } + let max_bits_per_mb_denom = r.read_ue("max_bits_per_mb_denom")?; + if max_bits_per_mb_denom > 16 { + return Err(SpsError::FieldValueTooLarge { + name: "max_bits_per_mb_denom", + value: max_bits_per_mb_denom, + }); + } + // more recent versions of the spec say log2_max_mv_length_horizontal and + // log2_max_mv_length_vertical - "shall be in the range of 0 to 15, inclusive." + // However, older versions of the spec say 0 to 16, and real bitstreams present 16, so + // we apply the more-permissive check to avoid rejecting real files. + let log2_max_mv_length_horizontal = r.read_ue("log2_max_mv_length_horizontal")?; + if log2_max_mv_length_horizontal > 16 { + return Err(SpsError::FieldValueTooLarge { + name: "log2_max_mv_length_horizontal", + value: log2_max_mv_length_horizontal, + }); + } + let log2_max_mv_length_vertical = r.read_ue("log2_max_mv_length_vertical")?; + if log2_max_mv_length_vertical > 16 { + return Err(SpsError::FieldValueTooLarge { + name: "log2_max_mv_length_vertical", + value: log2_max_mv_length_vertical, + }); + } + let max_num_reorder_frames = r.read_ue("max_num_reorder_frames")?; + let max_dec_frame_buffering = r.read_ue("max_dec_frame_buffering")?; + if max_num_reorder_frames > max_dec_frame_buffering { + return Err(SpsError::FieldValueTooLarge { + name: "max_num_reorder_frames", + value: max_num_reorder_frames, + }); + } + // "The value of max_dec_frame_buffering shall be greater than or equal to + // max_num_ref_frames." + if max_dec_frame_buffering < sps.max_num_ref_frames { + return Err(SpsError::FieldValueTooLarge { + name: "max_dec_frame_buffering", + value: max_dec_frame_buffering, + }); + } + let max = max_val_for_max_dec_frame_buffering(sps); + if max_dec_frame_buffering > max { + return Err(SpsError::FieldValueTooLarge { + name: "max_dec_frame_buffering", + value: max_dec_frame_buffering, + }); + } Some(BitstreamRestrictions { - motion_vectors_over_pic_boundaries_flag: r - .read_bool("motion_vectors_over_pic_boundaries_flag")?, - max_bytes_per_pic_denom: r.read_ue("max_bytes_per_pic_denom")?, - max_bits_per_mb_denom: r.read_ue("max_bits_per_mb_denom")?, - log2_max_mv_length_horizontal: r.read_ue("log2_max_mv_length_horizontal")?, - log2_max_mv_length_vertical: r.read_ue("log2_max_mv_length_vertical")?, - max_num_reorder_frames: r.read_ue("max_num_reorder_frames")?, - max_dec_frame_buffering: r.read_ue("max_dec_frame_buffering")?, + motion_vectors_over_pic_boundaries_flag, + max_bytes_per_pic_denom, + max_bits_per_mb_denom, + log2_max_mv_length_horizontal, + log2_max_mv_length_vertical, + max_num_reorder_frames, + max_dec_frame_buffering, }) } else { None @@ -834,6 +912,55 @@ impl BitstreamRestrictions { } } +// calculates the maximum allowed value for the max_dec_frame_buffering field +fn max_val_for_max_dec_frame_buffering(sps: &SeqParameterSet) -> u32 { + let level = Level::from_constraint_flags_and_level_idc( + ConstraintFlags::from(sps.constraint_flags), + sps.level_idc, + ); + let profile = Profile::from_profile_idc(sps.profile_idc); + let pic_width_in_mbs = sps.pic_width_in_mbs_minus1 + 1; + let pic_height_in_map_units = sps.pic_height_in_map_units_minus1 + 1; + let frame_height_in_mbs = if let FrameMbsFlags::Frames = sps.frame_mbs_flags { + 1 + } else { + 2 + } * pic_height_in_map_units; + let max_dpb_mbs = LEVEL_LIMITS.get(&level).unwrap().max_dpb_mbs; + match profile { + // "A.3.1 - Level limits common to the Baseline, Constrained Baseline, Main, and Extended + // profiles" + Profile::Baseline | Profile::Main | Profile::Extended => { + std::cmp::min(max_dpb_mbs / (pic_width_in_mbs * frame_height_in_mbs), 16) + } + // "A.3.2 - Level limits common to the High, Progressive High, Constrained High, High 10, + // Progressive High 10, High 4:2:2, High 4:4:4 Predictive, High 10 Intra, High 4:2:2 Intra, + // High 4:4:4 Intra, and CAVLC 4:4:4 Intra profiles" + Profile::High | Profile::High422 | Profile::High10 | Profile::High444 => { + std::cmp::min(max_dpb_mbs / (pic_width_in_mbs * frame_height_in_mbs), 16) + } + + // "G.10.2.1 - Level limits common to Scalable Baseline, Scalable Constrained Baseline, + // Scalable High, Scalable Constrained High, and Scalable High Intra profiles" + Profile::ScalableBase | Profile::ScalableHigh => { + // Min( MaxDpbMbs / ( PicWidthInMbs * FrameHeightInMbs ), 16 ) + std::cmp::min(max_dpb_mbs / (pic_width_in_mbs * frame_height_in_mbs), 16) + } + + // "H.10.2.1 - Level limits common to Multiview High, Stereo High, and MFC High profiles" + //Profile::MultiviewHigh | Profile::StereoHigh | Profile::MFCDepthHigh => { + // // Min( mvcScaleFactor * MaxDpbMbs / ( PicWidthInMbs * FrameHeightInMbs ), Max( 1, Ceil( log2( NumViews ) ) ) * 16 ) + //} + + // "I.10.2.1 - Level limits common to Multiview Depth High profiles" + //Profile::MultiviewDepthHigh | Profile::EnhancedMultiviewDepthHigh => { + // let mvcd_scale_factor = 2.5; + // std::cmp::min( mvcd_scale_factor * max_dpb_mbs / ( TotalPicSizeInMbs / NumViews ) ), std::cmp::max(1, Ceil( log2( NumViews ) ) ) * 16 ) + //} + _ => unimplemented!("{:?}", profile), + } +} + #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct VuiParameters { pub aspect_ratio_info: Option, @@ -848,7 +975,10 @@ pub struct VuiParameters { pub bitstream_restrictions: Option, } impl VuiParameters { - fn read(r: &mut R) -> Result, SpsError> { + fn read( + r: &mut R, + sps: &SeqParameterSet, + ) -> Result, SpsError> { let vui_parameters_present_flag = r.read_bool("vui_parameters_present_flag")?; Ok(if vui_parameters_present_flag { let mut hrd_parameters_present = false; @@ -866,7 +996,7 @@ impl VuiParameters { None }, pic_struct_present_flag: r.read_bool("pic_struct_present_flag")?, - bitstream_restrictions: BitstreamRestrictions::read(r)?, + bitstream_restrictions: BitstreamRestrictions::read(r, sps)?, }) } else { None @@ -895,10 +1025,12 @@ pub struct SeqParameterSet { impl SeqParameterSet { pub fn from_bits(mut r: R) -> Result { let profile_idc = r.read_u8(8, "profile_idc")?.into(); - let sps = SeqParameterSet { + let constraint_flags = r.read_u8(8, "constraint_flags")?.into(); + let level_idc = r.read_u8(8, "level_idc")?; + let mut sps = SeqParameterSet { profile_idc, - constraint_flags: r.read_u8(8, "constraint_flags")?.into(), - level_idc: r.read_u8(8, "level_idc")?, + constraint_flags, + level_idc, seq_parameter_set_id: SeqParamSetId::from_u32(r.read_ue("seq_parameter_set_id")?) .map_err(SpsError::BadSeqParamSetId)?, chroma_info: ChromaInfo::read(&mut r, profile_idc)?, @@ -912,8 +1044,13 @@ impl SeqParameterSet { frame_mbs_flags: FrameMbsFlags::read(&mut r)?, direct_8x8_inference_flag: r.read_bool("direct_8x8_inference_flag")?, frame_cropping: FrameCropping::read(&mut r)?, - vui_parameters: VuiParameters::read(&mut r)?, + // read the basic SPS data without reading VUI parameters yet, since checks of the + // bitstream restriction fields within the VUI parameters will need access to the + // initial SPS data + vui_parameters: None, }; + let vui_parameters = VuiParameters::read(&mut r, &sps)?; + sps.vui_parameters = vui_parameters; r.finish_rbsp()?; Ok(sps) } @@ -1035,6 +1172,299 @@ impl SeqParameterSet { Some((timing_info.time_scale as f64) / (2.0 * (timing_info.num_units_in_tick as f64))) } + + pub fn pic_width_in_mbs(&self) -> u32 { + self.pic_width_in_mbs_minus1 + 1 + } + + /// From the spec: `PicHeightInMapUnits = pic_height_in_map_units_minus1 + 1` + pub fn pic_height_in_map_units(&self) -> u32 { + self.pic_height_in_map_units_minus1 + 1 + } + + /// From the spec: `PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits` + pub fn pic_size_in_map_units(&self) -> u32 { + self.pic_width_in_mbs() * self.pic_height_in_map_units() + } +} + +struct LevelLimit { + max_mbps: u32, + max_fs: u32, + max_dpb_mbs: u32, + max_br: u32, + max_cpb: u32, + max_vmv_r: u32, + min_cr: u8, + max_mvs_per2mb: Option, +} + +lazy_static! { + // "Table A-1 – Level limits" from the spec + static ref LEVEL_LIMITS: std::collections::HashMap = { + let mut m = std::collections::HashMap::new(); + m.insert( + Level::L1, + LevelLimit { + max_mbps: 1485, + max_fs: 99, + max_dpb_mbs: 396, + max_br: 64, + max_cpb: 175, + max_vmv_r: 64, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L1_b, + LevelLimit { + max_mbps: 1485, + max_fs: 99, + max_dpb_mbs: 396, + max_br: 128, + max_cpb: 350, + max_vmv_r: 64, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L1_1, + LevelLimit { + max_mbps: 3000, + max_fs: 396, + max_dpb_mbs: 900, + max_br: 192, + max_cpb: 500, + max_vmv_r: 128, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L1_2, + LevelLimit { + max_mbps: 6000, + max_fs: 396, + max_dpb_mbs: 2376, + max_br: 384, + max_cpb: 1000, + max_vmv_r: 128, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L1_3, + LevelLimit { + max_mbps: 11880, + max_fs: 396, + max_dpb_mbs: 2376, + max_br: 768, + max_cpb: 2000, + max_vmv_r: 128, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L2, + LevelLimit { + max_mbps: 11880, + max_fs: 396, + max_dpb_mbs: 2376, + max_br: 2000, + max_cpb: 2000, + max_vmv_r: 128, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L2_1, + LevelLimit { + max_mbps: 19800, + max_fs: 792, + max_dpb_mbs: 4752, + max_br: 4000, + max_cpb: 4000, + max_vmv_r: 256, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L2_2, + LevelLimit { + max_mbps: 20250, + max_fs: 1620, + max_dpb_mbs: 8100, + max_br: 4000, + max_cpb: 4000, + max_vmv_r: 256, + min_cr: 2, + max_mvs_per2mb: None, + }, + ); + m.insert( + Level::L3, + LevelLimit { + max_mbps: 40500, + max_fs: 1620, + max_dpb_mbs: 8100, + max_br: 10000, + max_cpb: 10000, + max_vmv_r: 256, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(32), + }, + ); + m.insert( + Level::L3_1, + LevelLimit { + max_mbps: 108000, + max_fs: 3600, + max_dpb_mbs: 18000, + max_br: 14000, + max_cpb: 14000, + max_vmv_r: 512, + min_cr: 4, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L3_2, + LevelLimit { + max_mbps: 216000, + max_fs: 5120, + max_dpb_mbs: 20480, + max_br: 20000, + max_cpb: 20000, + max_vmv_r: 512, + min_cr: 4, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L4, + LevelLimit { + max_mbps: 245760, + max_fs: 8192, + max_dpb_mbs: 32768, + max_br: 20000, + max_cpb: 25000, + max_vmv_r: 512, + min_cr: 4, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L4_1, + LevelLimit { + max_mbps: 245760, + max_fs: 8192, + max_dpb_mbs: 32768, + max_br: 50000, + max_cpb: 62500, + max_vmv_r: 512, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L4_2, + LevelLimit { + max_mbps: 522240, + max_fs: 8704, + max_dpb_mbs: 34816, + max_br: 50000, + max_cpb: 62500, + max_vmv_r: 512, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L5, + LevelLimit { + max_mbps: 589824, + max_fs: 22080, + max_dpb_mbs: 110400, + max_br: 135000, + max_cpb: 135000, + max_vmv_r: 512, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L5_1, + LevelLimit { + max_mbps: 983040, + max_fs: 36864, + max_dpb_mbs: 184320, + max_br: 240000, + max_cpb: 240000, + max_vmv_r: 512, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L5_2, + LevelLimit { + max_mbps: 2073600, + max_fs: 36864, + max_dpb_mbs: 184320, + max_br: 240000, + max_cpb: 240000, + max_vmv_r: 512, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L6, + LevelLimit { + max_mbps: 4177920, + max_fs: 139264, + max_dpb_mbs: 696320, + max_br: 240000, + max_cpb: 240000, + max_vmv_r: 8192, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L6_1, + LevelLimit { + max_mbps: 8355840, + max_fs: 139264, + max_dpb_mbs: 696320, + max_br: 480000, + max_cpb: 480000, + max_vmv_r: 8192, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m.insert( + Level::L6_2, + LevelLimit { + max_mbps: 16711680, + max_fs: 139264, + max_dpb_mbs: 696320, + max_br: 800000, + max_cpb: 800000, + max_vmv_r: 8192, + min_cr: 2, + max_mvs_per2mb: NonZeroU8::new(16), + }, + ); + m + }; } #[cfg(test)]