use std::io::Read;
use media_codec_bitstream::{BigEndian, BitReader};
use media_core::{invalid_data_error, not_found_error, Result};
use smallvec::{smallvec, SmallVec};
use crate::{
constants::{MAX_LONG_TERM_REF_PICS, MAX_REFS, MAX_SPS_COUNT, MAX_SUB_LAYERS, MAX_VPS_COUNT},
ps::ParameterSets,
scaling_list::ScalingListData,
vps::{HrdParameters, ProfileTierLevel, Vps},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Profile {
Main = 1,
Main10 = 2,
MainStillPicture = 3,
RangeExtensions = 4,
HighThroughput = 5,
MultiviewMain = 6,
ScalableMain = 7,
ThreeDMain = 8,
ScreenContentCoding = 9,
MultiviewRangeExtensions = 10,
ScalableRangeExtensions = 1 << 4 | 10,
HighThroughputScreenContentCodingExtensions = 11,
}
impl Profile {
pub fn from_profile_idc(profile_idc: u8) -> Option<Self> {
match profile_idc {
1 => Some(Profile::Main),
2 => Some(Profile::Main10),
3 => Some(Profile::MainStillPicture),
4 => Some(Profile::RangeExtensions),
5 => Some(Profile::HighThroughput),
6 => Some(Profile::MultiviewMain),
7 => Some(Profile::ScalableMain),
8 => Some(Profile::ThreeDMain),
9 => Some(Profile::ScreenContentCoding),
10 => Some(Profile::MultiviewRangeExtensions),
11 => Some(Profile::HighThroughputScreenContentCodingExtensions),
_ => None,
}
}
pub fn from_profile_tier_level(ptl: &ProfileTierLevel) -> Option<Self> {
let profile_idc = ptl.general_profile_idc;
if profile_idc == 10 {
if ptl.is_profile_compatible(6) {
return Some(Profile::MultiviewRangeExtensions);
}
if ptl.is_profile_compatible(7) {
return Some(Profile::ScalableRangeExtensions);
}
return Some(Profile::MultiviewRangeExtensions);
}
if let Some(profile) = Self::from_profile_idc(profile_idc) {
return Some(profile);
}
None
}
#[inline]
pub fn idc(&self) -> u8 {
*self as u8 | 0x0F
}
pub fn supports_high_bit_depth(&self) -> bool {
matches!(
self,
Profile::Main10 |
Profile::RangeExtensions |
Profile::HighThroughput |
Profile::MultiviewRangeExtensions |
Profile::ScalableRangeExtensions |
Profile::HighThroughputScreenContentCodingExtensions
)
}
pub fn supports_extended_chroma(&self) -> bool {
matches!(
self,
Profile::RangeExtensions |
Profile::HighThroughput |
Profile::MultiviewRangeExtensions |
Profile::ScalableRangeExtensions |
Profile::HighThroughputScreenContentCodingExtensions
)
}
pub fn is_scalable(&self) -> bool {
matches!(self, Profile::ScalableMain | Profile::ScalableRangeExtensions)
}
pub fn is_multiview(&self) -> bool {
matches!(self, Profile::MultiviewMain | Profile::MultiviewRangeExtensions | Profile::ThreeDMain)
}
pub fn is_screen_content(&self) -> bool {
matches!(self, Profile::ScreenContentCoding | Profile::HighThroughputScreenContentCodingExtensions)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum Tier {
#[default]
Main = 0,
High = 1,
}
impl From<bool> for Tier {
fn from(tier_flag: bool) -> Self {
if tier_flag {
Tier::High
} else {
Tier::Main
}
}
}
impl Tier {
pub fn name(&self) -> &'static str {
match self {
Tier::Main => "Main",
Tier::High => "High",
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum ChromaFormat {
Monochrome = 0,
#[default]
YUV420 = 1,
YUV422 = 2,
YUV444 = 3,
}
impl From<u8> for ChromaFormat {
fn from(value: u8) -> Self {
match value {
0 => ChromaFormat::Monochrome,
1 => ChromaFormat::YUV420,
2 => ChromaFormat::YUV422,
3 => ChromaFormat::YUV444,
_ => ChromaFormat::YUV420,
}
}
}
pub const EXTENDED_SAR: u8 = 255;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub enum AspectRatioInfo {
#[default]
Unspecified,
Ratio1x1,
Ratio12x11,
Ratio10x11,
Ratio16x11,
Ratio40x33,
Ratio24x11,
Ratio20x11,
Ratio32x11,
Ratio80x33,
Ratio18x11,
Ratio15x11,
Ratio64x33,
Ratio160x99,
Ratio4x3,
Ratio3x2,
Ratio2x1,
Reserved(u8),
Extended(u16, u16),
}
impl AspectRatioInfo {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<Self> {
let aspect_ratio_idc = reader.read::<8, u8>()?;
if aspect_ratio_idc == EXTENDED_SAR {
let sar_width = reader.read::<16, u16>()?;
let sar_height = reader.read::<16, u16>()?;
Ok(Self::Extended(sar_width, sar_height))
} else {
Ok(Self::from_idc(aspect_ratio_idc))
}
}
#[inline]
pub const fn idc(&self) -> u8 {
match self {
Self::Unspecified => 0,
Self::Ratio1x1 => 1,
Self::Ratio12x11 => 2,
Self::Ratio10x11 => 3,
Self::Ratio16x11 => 4,
Self::Ratio40x33 => 5,
Self::Ratio24x11 => 6,
Self::Ratio20x11 => 7,
Self::Ratio32x11 => 8,
Self::Ratio80x33 => 9,
Self::Ratio18x11 => 10,
Self::Ratio15x11 => 11,
Self::Ratio64x33 => 12,
Self::Ratio160x99 => 13,
Self::Ratio4x3 => 14,
Self::Ratio3x2 => 15,
Self::Ratio2x1 => 16,
Self::Reserved(idc) => *idc,
Self::Extended(_, _) => EXTENDED_SAR,
}
}
pub const fn sample_aspect_ratio(&self) -> Option<(u16, u16)> {
match self {
Self::Unspecified => None,
Self::Ratio1x1 => Some((1, 1)),
Self::Ratio12x11 => Some((12, 11)),
Self::Ratio10x11 => Some((10, 11)),
Self::Ratio16x11 => Some((16, 11)),
Self::Ratio40x33 => Some((40, 33)),
Self::Ratio24x11 => Some((24, 11)),
Self::Ratio20x11 => Some((20, 11)),
Self::Ratio32x11 => Some((32, 11)),
Self::Ratio80x33 => Some((80, 33)),
Self::Ratio18x11 => Some((18, 11)),
Self::Ratio15x11 => Some((15, 11)),
Self::Ratio64x33 => Some((64, 33)),
Self::Ratio160x99 => Some((160, 99)),
Self::Ratio4x3 => Some((4, 3)),
Self::Ratio3x2 => Some((3, 2)),
Self::Ratio2x1 => Some((2, 1)),
Self::Reserved(_) => None,
Self::Extended(w, h) => Some((*w, *h)),
}
}
pub const fn from_idc(idc: u8) -> Self {
match idc {
0 => Self::Unspecified,
1 => Self::Ratio1x1,
2 => Self::Ratio12x11,
3 => Self::Ratio10x11,
4 => Self::Ratio16x11,
5 => Self::Ratio40x33,
6 => Self::Ratio24x11,
7 => Self::Ratio20x11,
8 => Self::Ratio32x11,
9 => Self::Ratio80x33,
10 => Self::Ratio18x11,
11 => Self::Ratio15x11,
12 => Self::Ratio64x33,
13 => Self::Ratio160x99,
14 => Self::Ratio4x3,
15 => Self::Ratio3x2,
16 => Self::Ratio2x1,
_ => Self::Unspecified,
}
}
#[inline]
pub const fn is_extended(&self) -> bool {
matches!(self, Self::Extended(_, _))
}
#[inline]
pub const fn is_specified(&self) -> bool {
!matches!(self, Self::Unspecified | Self::Reserved(_))
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum VideoFormat {
Component = 0,
PAL = 1,
NTSC = 2,
SECAM = 3,
MAC = 4,
#[default]
Unspecified = 5,
}
impl From<u8> for VideoFormat {
fn from(value: u8) -> Self {
match value {
0 => VideoFormat::Component,
1 => VideoFormat::PAL,
2 => VideoFormat::NTSC,
3 => VideoFormat::SECAM,
4 => VideoFormat::MAC,
_ => VideoFormat::Unspecified,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ColourDescription {
pub colour_primaries: u8,
pub transfer_characteristics: u8,
pub matrix_coefficients: u8,
}
impl ColourDescription {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<Self> {
Ok(Self {
colour_primaries: reader.read::<8, u8>()?,
transfer_characteristics: reader.read::<8, u8>()?,
matrix_coefficients: reader.read::<8, u8>()?,
})
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct VideoSignalType {
pub video_format: VideoFormat,
pub video_full_range_flag: bool,
pub colour_description: Option<ColourDescription>,
}
impl VideoSignalType {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<Self> {
let video_format = VideoFormat::from(reader.read::<3, u8>()?);
let video_full_range_flag = reader.read_bit()?;
let colour_description_present_flag = reader.read_bit()?;
let colour_description = if colour_description_present_flag {
Some(ColourDescription::parse(reader)?)
} else {
None
};
Ok(Self {
video_format,
video_full_range_flag,
colour_description,
})
}
#[inline]
pub fn is_full_range(&self) -> bool {
self.video_full_range_flag
}
#[inline]
pub fn colour_description(&self) -> Option<&ColourDescription> {
self.colour_description.as_ref()
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ChromaLocInfo {
pub chroma_sample_loc_type_top_field: u32,
pub chroma_sample_loc_type_bottom_field: u32,
}
impl ChromaLocInfo {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<Self> {
Ok(Self {
chroma_sample_loc_type_top_field: reader.read_ue()?,
chroma_sample_loc_type_bottom_field: reader.read_ue()?,
})
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct TimingInfo {
pub vui_num_units_in_tick: u32,
pub vui_time_scale: u32,
pub vui_poc_proportional_to_timing_flag: bool,
pub vui_num_ticks_poc_diff_one: u32,
}
impl TimingInfo {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<Self> {
let vui_num_units_in_tick = reader.read::<32, u32>()?;
let vui_time_scale = reader.read::<32, u32>()?;
let vui_poc_proportional_to_timing_flag = reader.read_bit()?;
let vui_num_ticks_poc_diff_one = if vui_poc_proportional_to_timing_flag {
reader.read_ue()? + 1
} else {
0
};
Ok(Self {
vui_num_units_in_tick,
vui_time_scale,
vui_poc_proportional_to_timing_flag,
vui_num_ticks_poc_diff_one,
})
}
pub fn frame_rate(&self) -> Option<(u32, u32)> {
if self.vui_num_units_in_tick > 0 {
Some((self.vui_time_scale, self.vui_num_units_in_tick))
} else {
None
}
}
pub fn frame_rate_fps(&self) -> Option<f64> {
self.frame_rate().map(|(num, den)| num as f64 / den as f64)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct BitstreamRestriction {
pub tiles_fixed_structure_flag: bool,
pub motion_vectors_over_pic_boundaries_flag: bool,
pub restricted_ref_pic_lists_flag: bool,
pub min_spatial_segmentation_idc: u32,
pub max_bytes_per_pic_denom: u32,
pub max_bits_per_min_cu_denom: u32,
pub log2_max_mv_length_horizontal: u32,
pub log2_max_mv_length_vertical: u32,
}
impl BitstreamRestriction {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<Self> {
Ok(Self {
tiles_fixed_structure_flag: reader.read_bit()?,
motion_vectors_over_pic_boundaries_flag: reader.read_bit()?,
restricted_ref_pic_lists_flag: reader.read_bit()?,
min_spatial_segmentation_idc: reader.read_ue()?,
max_bytes_per_pic_denom: reader.read_ue()?,
max_bits_per_min_cu_denom: reader.read_ue()?,
log2_max_mv_length_horizontal: reader.read_ue()?,
log2_max_mv_length_vertical: reader.read_ue()?,
})
}
#[inline]
pub fn allows_mv_over_pic_boundaries(&self) -> bool {
self.motion_vectors_over_pic_boundaries_flag
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct VuiParameters {
pub aspect_ratio_info: Option<AspectRatioInfo>,
pub overscan_info_present_flag: bool,
pub overscan_appropriate_flag: bool,
pub video_signal_type: Option<VideoSignalType>,
pub chroma_loc_info: Option<ChromaLocInfo>,
pub neutral_chroma_indication_flag: bool,
pub field_seq_flag: bool,
pub frame_field_info_present_flag: bool,
pub default_display_window_flag: bool,
pub def_disp_win_left_offset: u32,
pub def_disp_win_right_offset: u32,
pub def_disp_win_top_offset: u32,
pub def_disp_win_bottom_offset: u32,
pub timing_info: Option<TimingInfo>,
pub vui_hrd_parameters_present_flag: bool,
pub hrd_parameters: Option<HrdParameters>,
pub bitstream_restriction: Option<BitstreamRestriction>,
}
impl VuiParameters {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>, max_sub_layers: u8) -> Result<Self> {
let aspect_ratio_info_present_flag = reader.read_bit()?;
let aspect_ratio_info = if aspect_ratio_info_present_flag {
Some(AspectRatioInfo::parse(reader)?)
} else {
None
};
let overscan_info_present_flag = reader.read_bit()?;
let overscan_appropriate_flag = if overscan_info_present_flag {
reader.read_bit()?
} else {
false
};
let video_signal_type_present_flag = reader.read_bit()?;
let video_signal_type = if video_signal_type_present_flag {
Some(VideoSignalType::parse(reader)?)
} else {
None
};
let chroma_loc_info_present_flag = reader.read_bit()?;
let chroma_loc_info = if chroma_loc_info_present_flag {
Some(ChromaLocInfo::parse(reader)?)
} else {
None
};
let neutral_chroma_indication_flag = reader.read_bit()?;
let field_seq_flag = reader.read_bit()?;
let frame_field_info_present_flag = reader.read_bit()?;
let default_display_window_flag = reader.read_bit()?;
let (def_disp_win_left_offset, def_disp_win_right_offset, def_disp_win_top_offset, def_disp_win_bottom_offset) =
if default_display_window_flag {
(reader.read_ue()?, reader.read_ue()?, reader.read_ue()?, reader.read_ue()?)
} else {
(0, 0, 0, 0)
};
let vui_timing_info_present_flag = reader.read_bit()?;
let (timing_info, vui_hrd_parameters_present_flag, hrd_parameters) = if vui_timing_info_present_flag {
let timing = TimingInfo::parse(reader)?;
let hrd_present = reader.read_bit()?;
let hrd = if hrd_present {
Some(HrdParameters::parse(reader, true, max_sub_layers)?)
} else {
None
};
(Some(timing), hrd_present, hrd)
} else {
(None, false, None)
};
let bitstream_restriction_flag = reader.read_bit()?;
let bitstream_restriction = if bitstream_restriction_flag {
Some(BitstreamRestriction::parse(reader)?)
} else {
None
};
Ok(Self {
aspect_ratio_info,
overscan_info_present_flag,
overscan_appropriate_flag,
video_signal_type,
chroma_loc_info,
neutral_chroma_indication_flag,
field_seq_flag,
frame_field_info_present_flag,
default_display_window_flag,
def_disp_win_left_offset,
def_disp_win_right_offset,
def_disp_win_top_offset,
def_disp_win_bottom_offset,
timing_info,
vui_hrd_parameters_present_flag,
hrd_parameters,
bitstream_restriction,
})
}
pub fn sample_aspect_ratio(&self) -> Option<(u16, u16)> {
self.aspect_ratio_info.as_ref().and_then(|ar| ar.sample_aspect_ratio())
}
pub fn frame_rate(&self) -> Option<(u32, u32)> {
self.timing_info.as_ref().and_then(|ti| ti.frame_rate())
}
pub fn frame_rate_fps(&self) -> Option<f64> {
self.timing_info.as_ref().and_then(|ti| ti.frame_rate_fps())
}
pub fn is_full_range(&self) -> bool {
self.video_signal_type.as_ref().is_some_and(|vs| vs.is_full_range())
}
pub fn colour_description(&self) -> Option<&ColourDescription> {
self.video_signal_type.as_ref().and_then(|vs| vs.colour_description())
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ShortTermRefPicSet {
pub inter_ref_pic_set_prediction_flag: bool,
pub num_negative_pics: u32,
pub num_positive_pics: u32,
pub delta_poc_s0: SmallVec<[u32; MAX_REFS]>,
pub used_by_curr_pic_s0_flag: SmallVec<[bool; MAX_REFS]>,
pub delta_poc_s1: SmallVec<[u32; MAX_REFS]>,
pub used_by_curr_pic_s1_flag: SmallVec<[bool; MAX_REFS]>,
}
impl ShortTermRefPicSet {
pub fn parse<R: Read>(
reader: &mut BitReader<R, BigEndian>,
st_rps_idx: usize,
num_short_term_ref_pic_sets: usize,
previous_sets: &[ShortTermRefPicSet],
) -> Result<Self> {
let mut rps = Self::default();
if st_rps_idx != 0 {
rps.inter_ref_pic_set_prediction_flag = reader.read_bit()?;
}
if rps.inter_ref_pic_set_prediction_flag {
let delta_idx = if st_rps_idx == num_short_term_ref_pic_sets {
reader.read_ue()? as usize + 1
} else {
1
};
let _delta_rps_sign = reader.read_bit()?;
let _abs_delta_rps = reader.read_ue()? + 1;
let ref_rps_idx = st_rps_idx - delta_idx;
if ref_rps_idx >= previous_sets.len() {
return Err(invalid_data_error!("ref_rps_idx", ref_rps_idx));
}
let ref_rps = &previous_sets[ref_rps_idx];
let num_delta_pocs = ref_rps.num_negative_pics + ref_rps.num_positive_pics;
for _ in 0..=num_delta_pocs {
let used_by_curr_pic_flag = reader.read_bit()?;
if !used_by_curr_pic_flag {
let _use_delta_flag = reader.read_bit()?;
}
}
rps.num_negative_pics = ref_rps.num_negative_pics;
rps.num_positive_pics = ref_rps.num_positive_pics;
} else {
rps.num_negative_pics = reader.read_ue()?;
rps.num_positive_pics = reader.read_ue()?;
rps.delta_poc_s0.reserve(rps.num_negative_pics as usize);
rps.used_by_curr_pic_s0_flag.reserve(rps.num_negative_pics as usize);
for _ in 0..rps.num_negative_pics {
rps.delta_poc_s0.push(reader.read_ue()? + 1);
rps.used_by_curr_pic_s0_flag.push(reader.read_bit()?);
}
rps.delta_poc_s1.reserve(rps.num_positive_pics as usize);
rps.used_by_curr_pic_s1_flag.reserve(rps.num_positive_pics as usize);
for _ in 0..rps.num_positive_pics {
rps.delta_poc_s1.push(reader.read_ue()? + 1);
rps.used_by_curr_pic_s1_flag.push(reader.read_bit()?);
}
}
Ok(rps)
}
#[inline]
pub fn num_pics(&self) -> u32 {
self.num_negative_pics + self.num_positive_pics
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Sps {
pub video_parameter_set_id: u8,
pub sps_max_sub_layers: u8,
pub sps_temporal_id_nesting_flag: bool,
pub profile_tier_level: ProfileTierLevel,
pub seq_parameter_set_id: u8,
pub chroma_format: ChromaFormat,
pub separate_colour_plane_flag: bool,
pub pic_width_in_luma_samples: u32,
pub pic_height_in_luma_samples: u32,
pub conformance_window_flag: bool,
pub conf_win_left_offset: u32,
pub conf_win_right_offset: u32,
pub conf_win_top_offset: u32,
pub conf_win_bottom_offset: u32,
pub bit_depth_luma: u8,
pub bit_depth_chroma: u8,
pub log2_max_pic_order_cnt_lsb: u8,
pub sps_sub_layer_ordering_info_present_flag: bool,
pub sps_max_dec_pic_buffering: SmallVec<[u32; MAX_SUB_LAYERS]>,
pub sps_max_num_reorder_pics: SmallVec<[u32; MAX_SUB_LAYERS]>,
pub sps_max_latency_increase_plus1: SmallVec<[u32; MAX_SUB_LAYERS]>,
pub log2_min_luma_coding_block_size: u32,
pub log2_diff_max_min_luma_coding_block_size: u32,
pub log2_min_luma_transform_block_size: u32,
pub log2_diff_max_min_luma_transform_block_size: u32,
pub max_transform_hierarchy_depth_inter: u32,
pub max_transform_hierarchy_depth_intra: u32,
pub scaling_list_enabled_flag: bool,
pub sps_scaling_list_data_present_flag: bool,
pub scaling_list_data: Option<ScalingListData>,
pub amp_enabled_flag: bool,
pub sample_adaptive_offset_enabled_flag: bool,
pub pcm_enabled_flag: bool,
pub pcm_sample_bit_depth_luma: u8,
pub pcm_sample_bit_depth_chroma: u8,
pub log2_min_pcm_luma_coding_block_size: u32,
pub log2_diff_max_min_pcm_luma_coding_block_size: u32,
pub pcm_loop_filter_disabled_flag: bool,
pub num_short_term_ref_pic_sets: u32,
pub short_term_ref_pic_sets: Vec<ShortTermRefPicSet>,
pub long_term_ref_pics_present_flag: bool,
pub num_long_term_ref_pics_sps: u32,
pub lt_ref_pic_poc_lsb_sps: SmallVec<[u32; MAX_LONG_TERM_REF_PICS]>,
pub used_by_curr_pic_lt_sps_flag: SmallVec<[bool; MAX_LONG_TERM_REF_PICS]>,
pub sps_temporal_mvp_enabled_flag: bool,
pub strong_intra_smoothing_enabled_flag: bool,
pub vui_parameters: Option<VuiParameters>,
pub sps_extension_present_flag: bool,
pub sps_range_extension_flag: bool,
pub sps_multilayer_extension_flag: bool,
pub sps_3d_extension_flag: bool,
pub sps_scc_extension_flag: bool,
pub sps_extension_4bits: u8,
}
impl Sps {
pub fn parse_vps_id(data: &[u8]) -> Result<u8> {
let mut reader = BitReader::new(data);
Self::parse_vps_id_from_bit_reader(&mut reader)
}
pub fn parse_with_vps(data: &[u8], vps: &Vps) -> Result<Self> {
let mut reader = BitReader::new(data);
Self::parse_from_bit_reader(&mut reader, Some(vps), None)
}
pub fn parse_with_param_sets(data: &[u8], param_sets: &ParameterSets) -> Result<Self> {
let mut reader = BitReader::new(data);
Self::parse_from_bit_reader(&mut reader, None, Some(param_sets))
}
pub fn parse_vps_id_from_bit_reader<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<u8> {
let video_parameter_set_id = reader.read::<4, u8>()?;
if video_parameter_set_id as usize >= MAX_VPS_COUNT {
return Err(invalid_data_error!("vps_id", video_parameter_set_id));
}
Ok(video_parameter_set_id)
}
pub fn parse_from_bit_reader<R: Read>(
reader: &mut BitReader<R, BigEndian>,
vps: Option<&Vps>,
param_sets: Option<&ParameterSets>,
) -> Result<Self> {
let video_parameter_set_id = Self::parse_vps_id_from_bit_reader(reader)?;
let vps = if let Some(vps) = vps {
if video_parameter_set_id != vps.video_parameter_set_id {
return Err(invalid_data_error!("vps_id", video_parameter_set_id));
}
Some(vps)
} else if let Some(param_sets) = param_sets {
Some(param_sets.get_vps(video_parameter_set_id as u32).ok_or_else(|| not_found_error!("vps_id", video_parameter_set_id))?)
} else {
None
};
let sps_max_sub_layers = reader.read::<3, u8>()? + 1;
if sps_max_sub_layers as usize > MAX_SUB_LAYERS {
return Err(invalid_data_error!("sps_max_sub_layers", sps_max_sub_layers));
}
if let Some(vps) = vps {
if sps_max_sub_layers > vps.vps_max_sub_layers {
return Err(invalid_data_error!("sps_max_sub_layers", sps_max_sub_layers));
}
}
let sps_temporal_id_nesting_flag = reader.read_bit()?;
let profile_tier_level = ProfileTierLevel::parse(reader, true, sps_max_sub_layers)?;
let seq_parameter_set_id = reader.read_ue()?;
if seq_parameter_set_id as usize >= MAX_SPS_COUNT {
return Err(invalid_data_error!("sps_id", seq_parameter_set_id));
}
let chroma_format_idc = reader.read_ue()?;
let chroma_format = ChromaFormat::from(chroma_format_idc as u8);
let separate_colour_plane_flag = if chroma_format_idc == 3 {
reader.read_bit()?
} else {
false
};
let pic_width_in_luma_samples = reader.read_ue()?;
let pic_height_in_luma_samples = reader.read_ue()?;
let conformance_window_flag = reader.read_bit()?;
let (conf_win_left_offset, conf_win_right_offset, conf_win_top_offset, conf_win_bottom_offset) = if conformance_window_flag {
(reader.read_ue()?, reader.read_ue()?, reader.read_ue()?, reader.read_ue()?)
} else {
(0, 0, 0, 0)
};
let bit_depth_luma = reader.read_ue()? as u8 + 8;
let bit_depth_chroma = reader.read_ue()? as u8 + 8;
let log2_max_pic_order_cnt_lsb = reader.read_ue()? as u8 + 4;
let sps_sub_layer_ordering_info_present_flag = reader.read_bit()?;
let num_sub_layers = sps_max_sub_layers as usize;
let start_idx = if sps_sub_layer_ordering_info_present_flag {
0
} else {
num_sub_layers - 1
};
let mut sps_max_dec_pic_buffering = smallvec![0u32; num_sub_layers];
let mut sps_max_num_reorder_pics = smallvec![0u32; num_sub_layers];
let mut sps_max_latency_increase_plus1 = smallvec![0u32; num_sub_layers];
for i in start_idx..num_sub_layers {
sps_max_dec_pic_buffering[i] = reader.read_ue()? + 1;
sps_max_num_reorder_pics[i] = reader.read_ue()?;
sps_max_latency_increase_plus1[i] = reader.read_ue()?;
}
if !sps_sub_layer_ordering_info_present_flag {
for i in 0..start_idx {
sps_max_dec_pic_buffering[i] = sps_max_dec_pic_buffering[start_idx];
sps_max_num_reorder_pics[i] = sps_max_num_reorder_pics[start_idx];
sps_max_latency_increase_plus1[i] = sps_max_latency_increase_plus1[start_idx];
}
}
let log2_min_luma_coding_block_size = reader.read_ue()? + 3;
let log2_diff_max_min_luma_coding_block_size = reader.read_ue()?;
let log2_min_luma_transform_block_size = reader.read_ue()? + 2;
let log2_diff_max_min_luma_transform_block_size = reader.read_ue()?;
let max_transform_hierarchy_depth_inter = reader.read_ue()?;
let max_transform_hierarchy_depth_intra = reader.read_ue()?;
let scaling_list_enabled_flag = reader.read_bit()?;
let (sps_scaling_list_data_present_flag, scaling_list_data) = if scaling_list_enabled_flag {
let present = reader.read_bit()?;
let data = if present {
let is_444 = chroma_format_idc == 3;
Some(ScalingListData::parse(reader, is_444)?)
} else {
None
};
(present, data)
} else {
(false, None)
};
let amp_enabled_flag = reader.read_bit()?;
let sample_adaptive_offset_enabled_flag = reader.read_bit()?;
let pcm_enabled_flag = reader.read_bit()?;
let (
pcm_sample_bit_depth_luma,
pcm_sample_bit_depth_chroma,
log2_min_pcm_luma_coding_block_size,
log2_diff_max_min_pcm_luma_coding_block_size,
pcm_loop_filter_disabled_flag,
) = if pcm_enabled_flag {
let luma_bits = reader.read::<4, u8>()? + 1;
let chroma_bits = reader.read::<4, u8>()? + 1;
let min_size = reader.read_ue()? + 3;
let diff_size = reader.read_ue()?;
let loop_filter = reader.read_bit()?;
(luma_bits, chroma_bits, min_size, diff_size, loop_filter)
} else {
(0, 0, 0, 0, false)
};
let num_short_term_ref_pic_sets = reader.read_ue()?;
let mut short_term_ref_pic_sets = Vec::with_capacity(num_short_term_ref_pic_sets as usize);
for i in 0..num_short_term_ref_pic_sets as usize {
let rps = ShortTermRefPicSet::parse(reader, i, num_short_term_ref_pic_sets as usize, &short_term_ref_pic_sets)?;
short_term_ref_pic_sets.push(rps);
}
let long_term_ref_pics_present_flag = reader.read_bit()?;
let (num_long_term_ref_pics_sps, lt_ref_pic_poc_lsb_sps, used_by_curr_pic_lt_sps_flag) = if long_term_ref_pics_present_flag {
let num_lt = reader.read_ue()?;
if num_lt as usize > MAX_LONG_TERM_REF_PICS {
return Err(invalid_data_error!("num_long_term_ref_pics_sps", num_lt));
}
let mut poc_lsb = SmallVec::with_capacity(num_lt as usize);
let mut used_flags = SmallVec::with_capacity(num_lt as usize);
let log2_max_poc_lsb = log2_max_pic_order_cnt_lsb as u32;
for _ in 0..num_lt {
poc_lsb.push(reader.read_var(log2_max_poc_lsb)?);
used_flags.push(reader.read_bit()?);
}
(num_lt, poc_lsb, used_flags)
} else {
(0, SmallVec::new(), SmallVec::new())
};
let sps_temporal_mvp_enabled_flag = reader.read_bit()?;
let strong_intra_smoothing_enabled_flag = reader.read_bit()?;
let vui_parameters_present_flag = reader.read_bit()?;
let vui_parameters = if vui_parameters_present_flag {
Some(VuiParameters::parse(reader, sps_max_sub_layers)?)
} else {
None
};
let sps_extension_present_flag = reader.read_bit()?;
let (sps_range_extension_flag, sps_multilayer_extension_flag, sps_3d_extension_flag, sps_scc_extension_flag, sps_extension_4bits) =
if sps_extension_present_flag {
(reader.read_bit()?, reader.read_bit()?, reader.read_bit()?, reader.read_bit()?, reader.read::<4, u8>()?)
} else {
(false, false, false, false, 0)
};
Ok(Self {
video_parameter_set_id,
sps_max_sub_layers,
sps_temporal_id_nesting_flag,
profile_tier_level,
seq_parameter_set_id: seq_parameter_set_id as u8,
chroma_format,
separate_colour_plane_flag,
pic_width_in_luma_samples,
pic_height_in_luma_samples,
conformance_window_flag,
conf_win_left_offset,
conf_win_right_offset,
conf_win_top_offset,
conf_win_bottom_offset,
bit_depth_luma,
bit_depth_chroma,
log2_max_pic_order_cnt_lsb,
sps_sub_layer_ordering_info_present_flag,
sps_max_dec_pic_buffering,
sps_max_num_reorder_pics,
sps_max_latency_increase_plus1,
log2_min_luma_coding_block_size,
log2_diff_max_min_luma_coding_block_size,
log2_min_luma_transform_block_size,
log2_diff_max_min_luma_transform_block_size,
max_transform_hierarchy_depth_inter,
max_transform_hierarchy_depth_intra,
scaling_list_enabled_flag,
sps_scaling_list_data_present_flag,
scaling_list_data,
amp_enabled_flag,
sample_adaptive_offset_enabled_flag,
pcm_enabled_flag,
pcm_sample_bit_depth_luma,
pcm_sample_bit_depth_chroma,
log2_min_pcm_luma_coding_block_size,
log2_diff_max_min_pcm_luma_coding_block_size,
pcm_loop_filter_disabled_flag,
num_short_term_ref_pic_sets,
short_term_ref_pic_sets,
long_term_ref_pics_present_flag,
num_long_term_ref_pics_sps,
lt_ref_pic_poc_lsb_sps,
used_by_curr_pic_lt_sps_flag,
sps_temporal_mvp_enabled_flag,
strong_intra_smoothing_enabled_flag,
vui_parameters,
sps_extension_present_flag,
sps_range_extension_flag,
sps_multilayer_extension_flag,
sps_3d_extension_flag,
sps_scc_extension_flag,
sps_extension_4bits,
})
}
#[inline]
pub fn bit_depth_luma(&self) -> u8 {
self.bit_depth_luma
}
#[inline]
pub fn bit_depth_chroma(&self) -> u8 {
self.bit_depth_chroma
}
#[inline]
pub fn log2_max_pic_order_cnt_lsb(&self) -> u8 {
self.log2_max_pic_order_cnt_lsb
}
#[inline]
pub fn max_pic_order_cnt_lsb(&self) -> u32 {
1 << self.log2_max_pic_order_cnt_lsb
}
#[inline]
pub fn max_sub_layers(&self) -> u8 {
self.sps_max_sub_layers
}
#[inline]
pub fn log2_min_cb_size(&self) -> u32 {
self.log2_min_luma_coding_block_size
}
#[inline]
pub fn log2_ctb_size(&self) -> u32 {
self.log2_min_luma_coding_block_size + self.log2_diff_max_min_luma_coding_block_size
}
#[inline]
pub fn min_cb_size(&self) -> u32 {
1 << self.log2_min_cb_size()
}
#[inline]
pub fn ctb_size(&self) -> u32 {
1 << self.log2_ctb_size()
}
#[inline]
pub fn pic_width_in_ctbs(&self) -> u32 {
self.pic_width_in_luma_samples.div_ceil(self.ctb_size())
}
#[inline]
pub fn pic_height_in_ctbs(&self) -> u32 {
self.pic_height_in_luma_samples.div_ceil(self.ctb_size())
}
#[inline]
pub fn pic_size_in_ctbs(&self) -> u32 {
self.pic_width_in_ctbs() * self.pic_height_in_ctbs()
}
pub fn width(&self) -> u32 {
let sub_width_c = self.sub_width_c();
self.pic_width_in_luma_samples - sub_width_c * (self.conf_win_left_offset + self.conf_win_right_offset)
}
pub fn height(&self) -> u32 {
let sub_height_c = self.sub_height_c();
self.pic_height_in_luma_samples - sub_height_c * (self.conf_win_top_offset + self.conf_win_bottom_offset)
}
fn sub_width_c(&self) -> u32 {
if self.separate_colour_plane_flag {
return 1;
}
match self.chroma_format {
ChromaFormat::Monochrome => 1,
ChromaFormat::YUV420 => 2,
ChromaFormat::YUV422 => 2,
ChromaFormat::YUV444 => 1,
}
}
fn sub_height_c(&self) -> u32 {
if self.separate_colour_plane_flag {
return 1;
}
match self.chroma_format {
ChromaFormat::Monochrome => 1,
ChromaFormat::YUV420 => 2,
ChromaFormat::YUV422 => 1,
ChromaFormat::YUV444 => 1,
}
}
pub fn sample_aspect_ratio(&self) -> Option<(u16, u16)> {
self.vui_parameters.as_ref().and_then(|vui_params| vui_params.sample_aspect_ratio())
}
pub fn frame_rate(&self) -> Option<(u32, u32)> {
self.vui_parameters.as_ref().and_then(|vui_params| vui_params.frame_rate())
}
pub fn frame_rate_fps(&self) -> Option<f64> {
self.vui_parameters.as_ref().and_then(|vui_params| vui_params.frame_rate_fps())
}
pub fn is_full_range(&self) -> bool {
self.vui_parameters.as_ref().is_some_and(|vui_params| vui_params.is_full_range())
}
pub fn colour_description(&self) -> Option<&ColourDescription> {
self.vui_parameters.as_ref().and_then(|vui_params| vui_params.colour_description())
}
pub fn profile(&self) -> Option<Profile> {
Profile::from_profile_tier_level(&self.profile_tier_level)
}
pub fn tier(&self) -> Tier {
Tier::from(self.profile_tier_level.general_tier_flag)
}
pub fn level(&self) -> f32 {
self.profile_tier_level.level()
}
pub fn level_idc(&self) -> u8 {
self.profile_tier_level.general_level_idc
}
}