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;