diff --git a/src/hevc/context.rs b/src/hevc/context.rs new file mode 100644 index 0000000..e2d8808 --- /dev/null +++ b/src/hevc/context.rs @@ -0,0 +1,76 @@ +use crate::{vps::VPSNAL, sps::SPSNAL, pps::PPSNAL}; + +/// Contextual data that needs to be tracked between evaluations of different portions of H265 +/// syntax. +pub struct Context { + vid_param_sets: Vec>, + seq_param_sets: Vec>, + pic_param_sets: Vec>, +} +impl Default for Context { + fn default() -> Self { + Self::new() + } +} +impl Context { + pub fn new() -> Self { + let mut vid_param_sets = vec!(); + for _ in 0..16 { vid_param_sets.push(None); } + + let mut seq_param_sets = vec!(); + for _ in 0..16 { seq_param_sets.push(None); } + + let mut pic_param_sets = vec!(); + for _ in 0..64 { pic_param_sets.push(None); } + + Context { + vid_param_sets, + seq_param_sets, + pic_param_sets, + } + } +} +impl Context { + pub fn vps_by_id(&self, id: u8) -> Option<&VPSNAL> { + if id > 15 { + None + } else { + self.vid_param_sets[id as usize].as_ref() + } + } + pub fn vps(&self) -> impl Iterator { + self.vid_param_sets.iter().filter_map(Option::as_ref) + } + pub fn put_vid_param_set(&mut self, vps: VPSNAL) { + let i = vps.vps_id as usize; + self.vid_param_sets[i] = Some(vps); + } + pub fn sps_by_id(&self, id: u64) -> Option<&SPSNAL> { + if id > 15 { + None + } else { + self.seq_param_sets[id as usize].as_ref() + } + } + pub fn sps(&self) -> impl Iterator { + self.seq_param_sets.iter().filter_map(Option::as_ref) + } + pub fn put_seq_param_set(&mut self, sps: SPSNAL) { + let i = sps.sps_id as usize; + self.seq_param_sets[i] = Some(sps); + } + pub fn pps_by_id(&self, id: u64) -> Option<&PPSNAL> { + if id > 63 { + None + } else { + self.pic_param_sets[id as usize].as_ref() + } + } + pub fn pps(&self) -> impl Iterator { + self.pic_param_sets.iter().filter_map(Option::as_ref) + } + pub fn put_pic_param_set(&mut self, pps: PPSNAL) { + let i = pps.pps_id as usize; + self.pic_param_sets[i] = Some(pps); + } +} diff --git a/src/hevc/hvcc.rs b/src/hevc/hvcc.rs new file mode 100644 index 0000000..75a49c9 --- /dev/null +++ b/src/hevc/hvcc.rs @@ -0,0 +1,211 @@ +//! Support for handling _High Efficiency Video Coding Configuration_ data, used in the _ISO Base Media +//! File Format_ (AKA MP4) +//! + +use crate::{ + context::Context, + hevc::{pps, sps, vps, UnitType}, + NalHeader, NalHeaderError, +}; +use std::convert::TryFrom; + +#[derive(Debug)] +pub enum HvccError { + Io(std::io::Error), + NotEnoughData { + expected: usize, + actual: usize, + }, + /// The HevcDecoderConfigurationRecord used a version number other than `1`. + UnsupportedConfigurationVersion(u8), + ParamSet(ParamSetError), +} + +pub struct HevcDecoderConfigurationRecord<'buf> { + data: &'buf [u8], +} +impl<'buf> TryFrom<&'buf [u8]> for HevcDecoderConfigurationRecord<'buf> { + type Error = HvccError; + + fn try_from(data: &'buf [u8]) -> Result { + let hvcc = HevcDecoderConfigurationRecord { data }; + // we must confirm we have enough bytes for all fixed fields before we do anything else, + hvcc.ck(Self::MIN_CONF_SIZE)?; + if hvcc.configuration_version() != 1 { + // The spec requires that decoders ignore streams where the version number is not 1, + // indicating there was an incompatible change in the configuration format, + return Err(HvccError::UnsupportedConfigurationVersion( + hvcc.configuration_version(), + )); + } + + Ok(hvcc) + } +} +impl<'buf> HevcDecoderConfigurationRecord<'buf> { + const MIN_CONF_SIZE: usize = 23; + + fn ck(&self, len: usize) -> Result<(), HvccError> { + if self.data.len() < len { + Err(HvccError::NotEnoughData { + expected: len, + actual: self.data.len(), + }) + } else { + Ok(()) + } + } + pub fn configuration_version(&self) -> u8 { + self.data[0] + } + pub fn num_of_arrays(&self) -> usize { + self.data[22] as usize + } + pub fn general_profile_idc(&self) -> u8 { + self.data[1] & 0b0001_1111 + } + pub fn general_profile_compatibility_flags(&self) -> u32 { + (self.data[2] as u32) << 3 + | (self.data[3] as u32) << 2 + | (self.data[4] as u32) << 1 + | self.data[5] as u32 + } + pub fn general_level_idc(&self) -> u8 { + self.data[12] + } + /// Number of bytes used to specify the length of each NAL unit + /// 0 => 1 byte, 1 => 2 bytes, 2 => 3 bytes, 3 => 4 bytes + pub fn length_size_minus_one(&self) -> u8 { + self.data[21] & 0b0000_0011 + } + pub fn parameter_sets( + &self, + unit_type: UnitType, + ) -> impl Iterator> { + let mut data = &self.data[Self::MIN_CONF_SIZE..]; + let num_arrays = self.num_of_arrays(); + + for _ in 0..num_arrays { + let nal_type = data[0] & 0b0011_1111; + let num_nalus = u16::from(data[1]) << 8 | u16::from(data[2]); + data = &data[3..]; + if nal_type == unit_type.id() { + return ParamSetIter::new(data, unit_type).take(num_nalus as usize); + } + for _ in 0..num_nalus { + let nal_unit_len = u16::from(data[0]) << 8 | u16::from(data[1]); + let offset = nal_unit_len as usize + 2; + data = &data[offset..]; + } + } + + ParamSetIter::new(data, unit_type).take(0) + } + pub fn video_parameter_sets(&self) -> impl Iterator> { + self.parameter_sets(UnitType::NalVps) + } + pub fn sequence_parameter_sets(&self) -> impl Iterator> { + self.parameter_sets(UnitType::NalSps) + } + pub fn picture_parameter_sets(&self) -> impl Iterator> { + self.parameter_sets(UnitType::NalPps) + } + + /// Creates an H265 parser context, using the settings encoded into + /// this `HevcDecoderConfigurationRecord`. + /// + /// In particular, the _sequence parameter set_ and _picture parameter set_ values of this + /// configuration record will be inserted into the resulting context. + pub fn create_context(&self) -> Result { + let mut ctx = Context::new(); + for vps in self.parameter_sets(UnitType::NalVps) { + let vps = vps.map_err(HvccError::ParamSet)?; + let vps = crate::clear_start_code_emulation_prevention_3_byte(vps); + let mut reader = bitvec_helpers::bitstream_io_reader::BsIoVecReader::from_vec(vps); + reader.get_n::(16).map_err(|e| { + HvccError::ParamSet(ParamSetError::Parse(format!( + "failed to read vps header: {e}" + ))) + })?; + let vps = vps::VPSNAL::parse(&mut reader).map_err(|err| { + HvccError::ParamSet(ParamSetError::Parse(format!("failed to parse vps: {err}"))) + })?; + ctx.put_vid_param_set(vps); + } + for sps in self.parameter_sets(UnitType::NalSps) { + let sps = sps.map_err(HvccError::ParamSet)?; + let sps = crate::clear_start_code_emulation_prevention_3_byte(sps); + let mut reader = bitvec_helpers::bitstream_io_reader::BsIoVecReader::from_vec(sps); + reader.get_n::(16).map_err(|e| { + HvccError::ParamSet(ParamSetError::Parse(format!( + "failed to read sps header: {e}" + ))) + })?; + let sps = sps::SPSNAL::parse(&mut reader).map_err(|err| { + HvccError::ParamSet(ParamSetError::Parse(format!("failed to parse sps: {err}"))) + })?; + ctx.put_seq_param_set(sps); + } + for pps in self.parameter_sets(UnitType::NalPps) { + let pps = pps.map_err(HvccError::ParamSet)?; + let pps = crate::clear_start_code_emulation_prevention_3_byte(pps); + let mut reader = bitvec_helpers::bitstream_io_reader::BsIoVecReader::from_vec(pps); + reader.get_n::(16).map_err(|e| { + HvccError::ParamSet(ParamSetError::Parse(format!( + "failed to read pps header: {e}" + ))) + })?; + let pps = pps::PPSNAL::parse(&mut reader).map_err(|err| { + HvccError::ParamSet(ParamSetError::Parse(format!("failed to parse pps: {err}"))) + })?; + ctx.put_pic_param_set(pps); + } + Ok(ctx) + } +} + +#[derive(Debug)] +pub enum ParamSetError { + NalHeader(NalHeaderError), + IncorrectNalType { + expected: UnitType, + actual: UnitType, + }, + Parse(String), +} + +struct ParamSetIter<'buf>(&'buf [u8], UnitType); + +impl<'buf> ParamSetIter<'buf> { + pub fn new(buf: &'buf [u8], unit_type: UnitType) -> ParamSetIter<'buf> { + ParamSetIter(buf, unit_type) + } +} +impl<'buf> Iterator for ParamSetIter<'buf> { + type Item = Result<&'buf [u8], ParamSetError>; + + fn next(&mut self) -> Option { + if self.0.is_empty() { + None + } else { + let len = u16::from(self.0[0]) << 8 | u16::from(self.0[1]); + let data = &self.0[2..]; + let res = match NalHeader::new(u16::from(self.0[2]) << 8 | u16::from(self.0[3])) { + Ok(nal_header) => { + if nal_header.nal_unit_type() == self.1 { + let (data, remainder) = data.split_at(len as usize); + self.0 = remainder; + Ok(data) + } else { + Err(ParamSetError::IncorrectNalType { + expected: self.1, + actual: nal_header.nal_unit_type(), + }) + } + } + Err(err) => Err(ParamSetError::NalHeader(err)), + }; + Some(res) + } + } +} diff --git a/src/hevc/mod.rs b/src/hevc/mod.rs index 8e811cb..c88535f 100644 --- a/src/hevc/mod.rs +++ b/src/hevc/mod.rs @@ -1,16 +1,20 @@ +use std::fmt; + use self::slice::SliceNAL; use super::{BsIoVecReader, NALUStartCode}; +pub mod context; pub(crate) mod hrd_parameters; -pub(crate) mod pps; +pub mod hvcc; +pub mod pps; pub(crate) mod profile_tier_level; pub(crate) mod scaling_list_data; pub mod sei; pub(crate) mod short_term_rps; pub(crate) mod slice; -pub(crate) mod sps; -pub(crate) mod vps; +pub mod sps; +pub mod vps; pub(crate) mod vui_parameters; // https://github.com/virinext/hevcesbrowser/blob/master/hevcparser/include/Hevc.h @@ -47,6 +51,168 @@ pub const USER_DATA_REGISTERED_ITU_T_35: u8 = 4; pub use sei::SeiMessage; +#[derive(PartialEq, Hash, Debug, Copy, Clone)] +pub enum UnitType { + NalTrailN, + NalTrailR, + NalTsaN, + NalTsaR, + NalStsaN, + NalStsaR, + NalRadlN, + NalRadlR, + NalRaslN, + NalRaslR, + NalRsvVclN(u8), + NalRsvVclR(u8), + NalBlaWLp, + NalBlaWRadl, + NalBlaNLp, + NalIdrWRadl, + NalIdrNLp, + NalCraNut, + NalRsvIrapVcl(u8), + NalRsvVcl(u8), + NalVps, + NalSps, + NalPps, + NalAud, + NalEosNut, + NalEobNut, + NalFdNut, + NalSeiPrefix, + NalSeiSuffix, + RsvNvcl(u8), + NalUnspec(u8), +} +impl UnitType { + pub fn for_id(id: u8) -> Result { + let t = match id { + 0 => UnitType::NalTrailN, + 1 => UnitType::NalTrailR, + 2 => UnitType::NalTsaN, + 3 => UnitType::NalTsaR, + 4 => UnitType::NalStsaN, + 5 => UnitType::NalStsaR, + 6 => UnitType::NalRadlN, + 7 => UnitType::NalRadlR, + 8 => UnitType::NalRaslN, + 9 => UnitType::NalRaslR, + 10 | 12 | 14 => UnitType::NalRsvVclN(id), + 11 | 13 | 15 => UnitType::NalRsvVclR(id), + 16 => UnitType::NalBlaWLp, + 17 => UnitType::NalBlaWRadl, + 18 => UnitType::NalBlaNLp, + 19 => UnitType::NalIdrWRadl, + 20 => UnitType::NalIdrNLp, + 21 => UnitType::NalCraNut, + 22 | 23 => UnitType::NalRsvIrapVcl(id), + 24..=31 => UnitType::NalRsvVcl(id), + 32 => UnitType::NalVps, + 33 => UnitType::NalSps, + 34 => UnitType::NalPps, + 35 => UnitType::NalAud, + 36 => UnitType::NalEosNut, + 37 => UnitType::NalEobNut, + 38 => UnitType::NalFdNut, + 39 => UnitType::NalSeiPrefix, + 40 => UnitType::NalSeiSuffix, + 41..=47 => UnitType::RsvNvcl(id), + 48..=63 => UnitType::NalUnspec(id), + _ => return Err(UnitTypeError::ValueOutOfRange(id)), + }; + + Ok(t) + } + + pub fn id(self) -> u8 { + match self { + UnitType::NalTrailN => 0, + UnitType::NalTrailR => 1, + UnitType::NalTsaN => 2, + UnitType::NalTsaR => 3, + UnitType::NalStsaN => 4, + UnitType::NalStsaR => 5, + UnitType::NalRadlN => 6, + UnitType::NalRadlR => 7, + UnitType::NalRaslN => 8, + UnitType::NalRaslR => 9, + UnitType::NalRsvVclN(v) => v, + UnitType::NalRsvVclR(v) => v, + UnitType::NalBlaWLp => 16, + UnitType::NalBlaWRadl => 17, + UnitType::NalBlaNLp => 18, + UnitType::NalIdrWRadl => 19, + UnitType::NalIdrNLp => 20, + UnitType::NalCraNut => 21, + UnitType::NalRsvIrapVcl(v) => v, + UnitType::NalRsvVcl(v) => v, + UnitType::NalVps => 32, + UnitType::NalSps => 33, + UnitType::NalPps => 34, + UnitType::NalAud => 35, + UnitType::NalEosNut => 36, + UnitType::NalEobNut => 37, + UnitType::NalFdNut => 38, + UnitType::NalSeiPrefix => 39, + UnitType::NalSeiSuffix => 40, + UnitType::RsvNvcl(v) => v, + UnitType::NalUnspec(v) => v, + } + } +} + +#[derive(Debug)] +pub enum UnitTypeError { + /// if the value was outside the range `0` - `63`. + ValueOutOfRange(u8), +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct NalHeader(u16); + +#[derive(Debug)] +pub enum NalHeaderError { + /// The most significant bit of the header, called `forbidden_zero_bit`, was set to 1. + ForbiddenZeroBit, +} +impl NalHeader { + pub fn new(header_value: u16) -> Result { + if header_value & 0x8000 != 0 { + Err(NalHeaderError::ForbiddenZeroBit) + } else { + Ok(NalHeader(header_value)) + } + } + + pub fn nal_unit_type(self) -> UnitType { + UnitType::for_id(((self.0 & 0x7E00) >> 9) as u8).unwrap() + } + + pub fn nuh_layer_id(self) -> u8 { + ((self.0 & 0x1F8) >> 3) as u8 + } + + pub fn nuh_temporal_id_plus1(self) -> u8 { + (self.0 & 0x7) as u8 + } +} +impl From for u16 { + fn from(v: NalHeader) -> Self { + v.0 + } +} + +impl fmt::Debug for NalHeader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_struct("NalHeader") + .field("nal_unit_type", &self.nal_unit_type()) + .field("nuh_layer_id", &self.nuh_layer_id()) + .field("nuh_temporal_id_plus1", &self.nuh_temporal_id_plus1()) + .finish() + } +} + #[derive(Default, Debug, Clone)] pub struct NALUnit { pub start: usize, diff --git a/src/hevc/pps.rs b/src/hevc/pps.rs index 0457917..28bd089 100644 --- a/src/hevc/pps.rs +++ b/src/hevc/pps.rs @@ -4,51 +4,51 @@ use anyhow::Result; #[allow(clippy::upper_case_acronyms)] #[derive(Default, Debug, PartialEq, Eq)] pub struct PPSNAL { - pub(crate) pps_id: u64, - pub(crate) sps_id: u64, - pub(crate) dependent_slice_segments_enabled_flag: bool, - pub(crate) output_flag_present_flag: bool, - pub(crate) num_extra_slice_header_bits: u8, - sign_data_hiding_flag: bool, - cabac_init_present_flag: bool, - num_ref_idx_l0_default_active: u64, - num_ref_idx_l1_default_active: u64, - pic_init_qp_minus26: i64, - constrained_intra_pred_flag: bool, - transform_skip_enabled_flag: bool, - cu_qp_delta_enabled_flag: bool, - diff_cu_qp_delta_depth: u64, - cb_qp_offset: i64, - cr_qp_offset: i64, - pic_slice_level_chroma_qp_offsets_present_flag: bool, - weighted_pred_flag: bool, - weighted_bipred_flag: bool, - transquant_bypass_enable_flag: bool, - tiles_enabled_flag: bool, - entropy_coding_sync_enabled_flag: bool, - - num_tile_columns: u64, - num_tile_rows: u64, - uniform_spacing_flag: bool, - - column_widths: Vec, - row_heights: Vec, - - loop_filter_across_tiles_enabled_flag: bool, - seq_loop_filter_across_slices_enabled_flag: bool, - deblocking_filter_control_present_flag: bool, - deblocking_filter_override_enabled_flag: bool, - disable_dbf: bool, - beta_offset: i64, - tc_offset: i64, - - scaling_list_data_present_flag: bool, - scaling_list_data: ScalingListData, - - lists_modification_present_flag: bool, - log2_parallel_merge_level: u64, - slice_header_extension_present_flag: bool, - pps_extension_present_flag: bool, + pub pps_id: u64, + pub sps_id: u64, + pub dependent_slice_segments_enabled_flag: bool, + pub output_flag_present_flag: bool, + pub num_extra_slice_header_bits: u8, + pub sign_data_hiding_flag: bool, + pub cabac_init_present_flag: bool, + pub num_ref_idx_l0_default_active: u64, + pub num_ref_idx_l1_default_active: u64, + pub pic_init_qp_minus26: i64, + pub constrained_intra_pred_flag: bool, + pub transform_skip_enabled_flag: bool, + pub cu_qp_delta_enabled_flag: bool, + pub diff_cu_qp_delta_depth: u64, + pub cb_qp_offset: i64, + pub cr_qp_offset: i64, + pub pic_slice_level_chroma_qp_offsets_present_flag: bool, + pub weighted_pred_flag: bool, + pub weighted_bipred_flag: bool, + pub transquant_bypass_enable_flag: bool, + pub tiles_enabled_flag: bool, + pub entropy_coding_sync_enabled_flag: bool, + + pub num_tile_columns: u64, + pub num_tile_rows: u64, + pub uniform_spacing_flag: bool, + + pub column_widths: Vec, + pub row_heights: Vec, + + pub loop_filter_across_tiles_enabled_flag: bool, + pub seq_loop_filter_across_slices_enabled_flag: bool, + pub deblocking_filter_control_present_flag: bool, + pub deblocking_filter_override_enabled_flag: bool, + pub disable_dbf: bool, + pub beta_offset: i64, + pub tc_offset: i64, + + pub scaling_list_data_present_flag: bool, + pub scaling_list_data: ScalingListData, + + pub lists_modification_present_flag: bool, + pub log2_parallel_merge_level: u64, + pub slice_header_extension_present_flag: bool, + pub pps_extension_present_flag: bool, } impl PPSNAL { diff --git a/src/hevc/sps.rs b/src/hevc/sps.rs index 9674569..b0dc5dc 100644 --- a/src/hevc/sps.rs +++ b/src/hevc/sps.rs @@ -6,83 +6,86 @@ use super::short_term_rps::ShortTermRPS; use super::vui_parameters::VuiParameters; use super::BsIoVecReader; +const SUB_WIDTH_C: &[u64; 4] = &[1, 2, 2, 1]; +const SUB_HEIGHT_C: &[u64; 4] = &[1, 2, 1, 1]; + #[allow(clippy::upper_case_acronyms)] #[derive(Default, Debug, PartialEq, Clone, Eq)] pub struct SPSNAL { - pub(crate) vps_id: u8, - max_sub_layers: u8, - temporal_id_nesting_flag: bool, - - ptl: ProfileTierLevel, - pub(crate) sps_id: u64, - chroma_format_idc: u64, - pub(crate) separate_colour_plane_flag: bool, - width: u64, - height: u64, - - pic_conformance_flag: bool, - conf_win_left_offset: u64, - conf_win_right_offset: u64, - conf_win_top_offset: u64, - conf_win_bottom_offset: u64, - - bit_depth: u64, - bit_depth_chroma: u64, - pub(crate) log2_max_poc_lsb: u64, - sublayer_ordering_info: bool, - max_dec_pic_buffering: Vec, - num_reorder_pics: Vec, - max_latency_increase: Vec, - - log2_min_cb_size: u64, - log2_diff_max_min_coding_block_size: u64, - log2_min_tb_size: u64, - log2_diff_max_min_transform_block_size: u64, - max_transform_hierarchy_depth_inter: u64, - max_transform_hierarchy_depth_intra: u64, - - scaling_list_enabled_flag: bool, - scaling_list_data_present_flag: bool, - scaling_list_data: ScalingListData, - - amp_enabled_flag: bool, - sao_enabled_flag: bool, - pcm_enabled_flag: bool, - pcm_bit_depth: u8, - pcm_bit_depth_chroma: u8, - pcm_log2_min_pcm_cb_size: u64, - pcm_log2_max_pcm_cb_size: u64, - pcm_loop_filter_disable_flag: bool, - - nb_st_rps: u64, - pub(crate) short_term_ref_pic_sets: Vec, - - long_term_ref_pics_present_flag: bool, - num_long_term_ref_pics_sps: u64, - lt_ref_pic_poc_lsb_sps: Vec, - used_by_curr_pic_lt_sps_flag: Vec, - - sps_temporal_mvp_enabled_flag: bool, - sps_strong_intra_smoothing_enable_flag: bool, - - vui_present: bool, - vui_parameters: VuiParameters, - - sps_extension_flag: bool, + pub vps_id: u8, + pub max_sub_layers: u8, + pub temporal_id_nesting_flag: bool, + + pub ptl: ProfileTierLevel, + pub sps_id: u64, + pub chroma_format_idc: u64, + pub separate_colour_plane_flag: bool, + pub width: u64, + pub height: u64, + + pub pic_conformance_flag: bool, + pub conf_win_left_offset: u64, + pub conf_win_right_offset: u64, + pub conf_win_top_offset: u64, + pub conf_win_bottom_offset: u64, + + pub bit_depth: u64, + pub bit_depth_chroma: u64, + pub log2_max_poc_lsb: u64, + pub sublayer_ordering_info: bool, + pub max_dec_pic_buffering: Vec, + pub num_reorder_pics: Vec, + pub max_latency_increase: Vec, + + pub log2_min_cb_size: u64, + pub log2_diff_max_min_coding_block_size: u64, + pub log2_min_tb_size: u64, + pub log2_diff_max_min_transform_block_size: u64, + pub max_transform_hierarchy_depth_inter: u64, + pub max_transform_hierarchy_depth_intra: u64, + + pub scaling_list_enabled_flag: bool, + pub scaling_list_data_present_flag: bool, + pub scaling_list_data: ScalingListData, + + pub amp_enabled_flag: bool, + pub sao_enabled_flag: bool, + pub pcm_enabled_flag: bool, + pub pcm_bit_depth: u8, + pub pcm_bit_depth_chroma: u8, + pub pcm_log2_min_pcm_cb_size: u64, + pub pcm_log2_max_pcm_cb_size: u64, + pub pcm_loop_filter_disable_flag: bool, + + pub nb_st_rps: u64, + pub short_term_ref_pic_sets: Vec, + + pub long_term_ref_pics_present_flag: bool, + pub num_long_term_ref_pics_sps: u64, + pub lt_ref_pic_poc_lsb_sps: Vec, + pub used_by_curr_pic_lt_sps_flag: Vec, + + pub sps_temporal_mvp_enabled_flag: bool, + pub sps_strong_intra_smoothing_enable_flag: bool, + + pub vui_present: bool, + pub vui_parameters: VuiParameters, + + pub sps_extension_flag: bool, // Computed values - pub(crate) log2_ctb_size: u64, - pub(crate) log2_min_pu_size: u64, - pub(crate) ctb_width: u64, - pub(crate) ctb_height: u64, - pub(crate) ctb_size: u64, - pub(crate) min_cb_width: u64, - pub(crate) min_cb_height: u64, - pub(crate) min_tb_width: u64, - pub(crate) min_tb_height: u64, - pub(crate) min_pu_width: u64, - pub(crate) min_pu_height: u64, - pub(crate) tb_mask: u64, + pub log2_ctb_size: u64, + pub log2_min_pu_size: u64, + pub ctb_width: u64, + pub ctb_height: u64, + pub ctb_size: u64, + pub min_cb_width: u64, + pub min_cb_height: u64, + pub min_tb_width: u64, + pub min_tb_height: u64, + pub min_pu_width: u64, + pub min_pu_height: u64, + pub tb_mask: u64, } impl SPSNAL { @@ -221,4 +224,22 @@ impl SPSNAL { Ok(sps) } + + pub fn width(&self) -> u64 { + if self.pic_conformance_flag { + let crop_unit_x = SUB_WIDTH_C[self.chroma_format_idc as usize]; + return self.width - ((self.conf_win_left_offset + self.conf_win_right_offset) * crop_unit_x) + } + + self.width + } + + pub fn height(&self) -> u64 { + if self.pic_conformance_flag { + let crop_unit_y = SUB_HEIGHT_C[self.chroma_format_idc as usize]; + return self.height - ((self.conf_win_top_offset + self.conf_win_bottom_offset) * crop_unit_y) + } + + self.height + } } diff --git a/src/hevc/vps.rs b/src/hevc/vps.rs index acffead..08c2c8a 100644 --- a/src/hevc/vps.rs +++ b/src/hevc/vps.rs @@ -7,23 +7,23 @@ use super::BsIoVecReader; #[allow(clippy::upper_case_acronyms)] #[derive(Default, Debug, PartialEq, Eq)] pub struct VPSNAL { - pub(crate) vps_id: u8, - vps_max_layers: u8, - vps_max_sub_layers: u8, - vps_temporal_id_nesting_flag: bool, - ptl: ProfileTierLevel, - vps_sub_layer_ordering_info_present_flag: bool, - vps_max_dec_pic_buffering: Vec, - vps_num_reorder_pics: Vec, - vps_max_latency_increase: Vec, - vps_max_layer_id: u8, - vps_num_layer_sets: u64, - vps_timing_info_present_flag: bool, - vps_num_units_in_tick: u32, - vps_time_scale: u32, - vps_poc_proportional_to_timing_flag: bool, - vps_num_ticks_poc_diff_one: u64, - vps_num_hrd_parameters: u64, + pub vps_id: u8, + pub vps_max_layers: u8, + pub vps_max_sub_layers: u8, + pub vps_temporal_id_nesting_flag: bool, + pub ptl: ProfileTierLevel, + pub vps_sub_layer_ordering_info_present_flag: bool, + pub vps_max_dec_pic_buffering: Vec, + pub vps_num_reorder_pics: Vec, + pub vps_max_latency_increase: Vec, + pub vps_max_layer_id: u8, + pub vps_num_layer_sets: u64, + pub vps_timing_info_present_flag: bool, + pub vps_num_units_in_tick: u32, + pub vps_time_scale: u32, + pub vps_poc_proportional_to_timing_flag: bool, + pub vps_num_ticks_poc_diff_one: u64, + pub vps_num_hrd_parameters: u64, } impl VPSNAL { diff --git a/src/hevc/vui_parameters.rs b/src/hevc/vui_parameters.rs index b25c788..3039614 100644 --- a/src/hevc/vui_parameters.rs +++ b/src/hevc/vui_parameters.rs @@ -5,51 +5,51 @@ use super::BsIoVecReader; #[derive(Default, Debug, PartialEq, Clone, Eq)] pub struct VuiParameters { - sar_present: bool, - sar_idx: u8, - sar_num: u16, - sar_den: u16, - overscan_info_present_flag: bool, - overscan_appropriate_flag: bool, - video_signal_type_present_flag: bool, - - video_format: u8, - video_full_range_flag: bool, - colour_description_present_flag: bool, - colour_primaries: u8, - transfer_characteristic: u8, - matrix_coeffs: u8, - - chroma_loc_info_present_flag: bool, - chroma_sample_loc_type_top_field: u64, - chroma_sample_loc_type_bottom_field: u64, - neutral_chroma_indication_flag: bool, - field_seq_flag: bool, - frame_field_info_present_flag: bool, - - default_display_window_flag: bool, - def_disp_win_left_offset: u64, - def_disp_win_right_offset: u64, - def_disp_win_top_offset: u64, - def_disp_win_bottom_offset: u64, - - vui_timing_info_present_flag: bool, - vui_num_units_in_tick: u32, - vui_time_scale: u32, - vui_poc_proportional_to_timing_flag: bool, - vui_num_ticks_poc_diff_one_minus1: u64, - vui_hrd_parameters_present_flag: bool, - - bitstream_restriction_flag: bool, - tiles_fixed_structure_flag: bool, - motion_vectors_over_pic_boundaries_flag: bool, - restricted_ref_pic_lists_flag: bool, - - min_spatial_segmentation_idc: u64, - max_bytes_per_pic_denom: u64, - max_bits_per_min_cu_denom: u64, - log2_max_mv_length_horizontal: u64, - log2_max_mv_length_vertical: u64, + pub sar_present: bool, + pub sar_idc: u8, + pub sar_num: u16, + pub sar_den: u16, + pub overscan_info_present_flag: bool, + pub overscan_appropriate_flag: bool, + pub video_signal_type_present_flag: bool, + + pub video_format: u8, + pub video_full_range_flag: bool, + pub colour_description_present_flag: bool, + pub colour_primaries: u8, + pub transfer_characteristic: u8, + pub matrix_coeffs: u8, + + pub chroma_loc_info_present_flag: bool, + pub chroma_sample_loc_type_top_field: u64, + pub chroma_sample_loc_type_bottom_field: u64, + 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: u64, + pub def_disp_win_right_offset: u64, + pub def_disp_win_top_offset: u64, + pub def_disp_win_bottom_offset: u64, + + pub vui_timing_info_present_flag: bool, + 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_minus1: u64, + pub vui_hrd_parameters_present_flag: bool, + + pub bitstream_restriction_flag: bool, + 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: u64, + pub max_bytes_per_pic_denom: u64, + pub max_bits_per_min_cu_denom: u64, + pub log2_max_mv_length_horizontal: u64, + pub log2_max_mv_length_vertical: u64, } impl VuiParameters { @@ -60,9 +60,9 @@ impl VuiParameters { }; if vui.sar_present { - vui.sar_idx = bs.get_n(8)?; + vui.sar_idc = bs.get_n(8)?; - if vui.sar_idx == 255 { + if vui.sar_idc == 255 { vui.sar_num = bs.get_n(16)?; vui.sar_den = bs.get_n(16)?; } @@ -135,4 +135,31 @@ impl VuiParameters { Ok(vui) } + + pub fn aspect_ratio(&self) -> Option<(u16, u16)> { + if !self.sar_present { + return None; + } + + match self.sar_idc { + 1 => Some((1, 1)), + 2 => Some((12, 11)), + 3 => Some((10, 11)), + 4 => Some((16, 11)), + 5 => Some((40, 33)), + 6 => Some((24, 11)), + 7 => Some((20, 11)), + 8 => Some((32, 11)), + 9 => Some((80, 33)), + 10 => Some((18, 11)), + 11 => Some((15, 11)), + 12 => Some((64, 33)), + 13 => Some((160, 99)), + 14 => Some((4, 3)), + 15 => Some((3, 2)), + 16 => Some((2, 1)), + 255 => Some((self.sar_num, self.sar_den)), + _ => None, + } + } } diff --git a/src/lib.rs b/src/lib.rs index 850476c..4cb366c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,13 +9,14 @@ pub mod utils; #[cfg(feature = "hevc_io")] pub mod io; -use hevc::*; +pub use hevc::*; use pps::PPSNAL; use slice::SliceNAL; use sps::SPSNAL; use vps::VPSNAL; -use utils::clear_start_code_emulation_prevention_3_byte; +pub use utils::clear_start_code_emulation_prevention_3_byte; +pub use utils::add_start_code_emulation_prevention_3_byte; // We don't want to parse large slices because the memory is copied const MAX_PARSE_SIZE: usize = 2048;