From 230addb92486ce767fb3b959acfbaa658d7929ed Mon Sep 17 00:00:00 2001 From: Andrey Tkachenko Date: Fri, 3 Feb 2023 23:15:20 +0400 Subject: [PATCH] Initial commit --- .drone.yml | 14 ++ Cargo.toml | 12 ++ README.md | 2 + examples/parser_demo.rs | 5 + src/h265parser/consts.rs | 35 +++++ src/h265parser/mod.rs | 6 + src/h265parser/pps.rs | 65 ++++++++ src/h265parser/slice.rs | 95 ++++++++++++ src/h265parser/sps.rs | 69 +++++++++ src/h265parser/vps.rs | 324 +++++++++++++++++++++++++++++++++++++++ src/h265parser/vui.rs | 48 ++++++ src/lib.rs | 33 ++++ 12 files changed, 708 insertions(+) create mode 100644 .drone.yml create mode 100644 Cargo.toml create mode 100644 examples/parser_demo.rs create mode 100644 src/h265parser/consts.rs create mode 100644 src/h265parser/mod.rs create mode 100644 src/h265parser/pps.rs create mode 100644 src/h265parser/slice.rs create mode 100644 src/h265parser/sps.rs create mode 100644 src/h265parser/vps.rs create mode 100644 src/h265parser/vui.rs create mode 100644 src/lib.rs diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..484f2d5 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,14 @@ +kind: pipeline +name: default + +steps: +- name: build + image: rust + commands: + - cargo build --verbose --all + +- name: fmt-check + image: rust + commands: + - rustup component add rustfmt + - cargo fmt --all -- --check diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..02febe1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "h265parser" +authors = ["Andrey Tkachenko "] +version = "0.0.1" +edition = "2021" + +[dependencies] +anyhow = "1.0.66" +bitstream-io = "1.5.0" +bytes = "1.2.1" +log = "0.4.17" +thiserror = "1.0.37" diff --git a/README.md b/README.md index 933d595..3f05b73 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://ci.aidev.ru/api/badges/andrey/h265parser/status.svg)](https://ci.aidev.ru/andrey/h265parser) + # h265parser h265 NAL parser \ No newline at end of file diff --git a/examples/parser_demo.rs b/examples/parser_demo.rs new file mode 100644 index 0000000..a7aafca --- /dev/null +++ b/examples/parser_demo.rs @@ -0,0 +1,5 @@ +use h265parser::slice; + +fn main() { + println!("todo!") +} diff --git a/src/h265parser/consts.rs b/src/h265parser/consts.rs new file mode 100644 index 0000000..4d4b43d --- /dev/null +++ b/src/h265parser/consts.rs @@ -0,0 +1,35 @@ +pub(crate) static DEFAULT_SCALING_LIST0: [u8; 16] = [ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, +]; + +pub(crate) static DEFAULT_SCALING_LIST1: [u8; 64] = [ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 17, 16, 17, 18, 17, 18, 18, 17, 18, 21, 19, 20, + 21, 20, 19, 21, 24, 22, 22, 24, 24, 22, 22, 24, 25, 25, 27, 30, 27, 25, 25, 29, 31, 35, 35, 31, + 29, 36, 41, 44, 41, 36, 47, 54, 54, 47, 65, 70, 65, 88, 88, 115, +]; + +pub(crate) static DEFAULT_SCALING_LIST2: [u8; 64] = [ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 20, 20, 20, + 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 28, 28, 28, 28, 28, + 28, 33, 33, 33, 33, 33, 41, 41, 41, 41, 54, 54, 54, 71, 71, 91, +]; + +pub(crate) static ASPECT_RATIOS: [(u32, u32); 17] = [ + (0, 0), + (1, 1), + (12, 11), + (10, 11), + (16, 11), + (40, 33), + (24, 11), + (20, 11), + (32, 11), + (80, 33), + (18, 11), + (15, 11), + (64, 33), + (160, 99), + (4, 3), + (3, 2), + (2, 1), +]; diff --git a/src/h265parser/mod.rs b/src/h265parser/mod.rs new file mode 100644 index 0000000..71dd4a7 --- /dev/null +++ b/src/h265parser/mod.rs @@ -0,0 +1,6 @@ +pub mod consts; +pub mod pps; +pub mod slice; +pub mod sps; +pub mod vps; +pub mod vui; diff --git a/src/h265parser/pps.rs b/src/h265parser/pps.rs new file mode 100644 index 0000000..7b1d0e3 --- /dev/null +++ b/src/h265parser/pps.rs @@ -0,0 +1,65 @@ +use crate::sps::H265SPS; + +#[derive(Debug, Copy, Clone)] +pub struct H265ScalingList { + pub scaling_list_dc_coef_minus8_16x16: [i16; 6], + pub scaling_list_dc_coef_minus8_32x32: [i16; 2], + pub scaling_lists_4x4: [[u8; 16]; 6], + pub scaling_lists_8x8: [[u8; 64]; 6], + pub scaling_lists_16x16: [[u8; 64]; 6], + pub scaling_lists_32x32: [[u8; 64]; 2], +} + +impl Default for H265ScalingList { + fn default() -> Self { + todo!() + } +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct H265PPS { + pub id: u32, + pub sps: H265SPS, + pub dependent_slice_segments_enabled_flag: bool, + pub output_flag_present_flag: bool, + pub num_extra_slice_header_bits: u8, + pub sign_data_hiding_enabled_flag: bool, + pub cabac_init_present_flag: bool, + pub num_ref_idx_l0_default_active_minus1: u8, + pub num_ref_idx_l1_default_active_minus1: u8, + pub init_qp_minus26: i8, + 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: u8, + pub cb_qp_offset: i8, + pub cr_qp_offset: i8, + pub slice_chroma_qp_offsets_present_flag: bool, + pub weighted_pred_flag: bool, + pub weighted_bipred_flag: bool, + pub transquant_bypass_enabled_flag: bool, + pub tiles_enabled_flag: bool, + pub entropy_coding_sync_enabled_flag: bool, + pub num_tile_columns_minus1: u8, + pub num_tile_rows_minus1: u8, + pub uniform_spacing_flag: bool, + pub column_width_minus1: [u32; 19], + pub row_height_minus1: [u32; 21], + pub loop_filter_across_tiles_enabled_flag: bool, + pub loop_filter_across_slices_enabled_flag: bool, + pub deblocking_filter_control_present_flag: bool, + pub deblocking_filter_override_enabled_flag: bool, + pub deblocking_filter_disabled_flag: bool, + pub beta_offset_div2: i8, + pub tc_offset_div2: i8, + pub scaling_list_data_present_flag: bool, + pub scaling_list: H265ScalingList, + pub lists_modification_present_flag: bool, + pub log2_parallel_merge_level_minus2: u8, + pub slice_segment_header_extension_present_flag: bool, + pub pps_extension_flag: bool, +} + +impl H265PPS { + pub fn parse() {} +} diff --git a/src/h265parser/slice.rs b/src/h265parser/slice.rs new file mode 100644 index 0000000..4bf639e --- /dev/null +++ b/src/h265parser/slice.rs @@ -0,0 +1,95 @@ +use crate::pps::H265PPS; + +#[derive(Debug, Copy, Clone, Default)] +pub struct H265ShortTermRefPicSet { + pub inter_ref_pic_set_prediction_flag: bool, + pub delta_idx_minus1: u8, + pub delta_rps_sign: u8, + pub abs_delta_rps_minus1: u16, + pub num_delta_pocs: u8, + pub num_negative_pics: u8, + pub num_positive_pics: u8, + pub used_by_curr_pic_s0: [u8; 16], + pub used_by_curr_pic_s1: [u8; 16], + pub delta_poc_s0: [i32; 16], + pub delta_poc_s1: [i32; 16], +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct H265RefPicListModification { + pub ref_pic_list_modification_flag_l0: bool, + pub list_entry_l0: [u32; 15], + + pub ref_pic_list_modification_flag_l1: bool, + pub list_entry_l1: [u32; 15], +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct H265PredWeightTable { + pub luma_log2_weight_denom: u8, + pub delta_chroma_log2_weight_denom: i8, + pub luma_weight_l0_flag: [bool; 15], + pub chroma_weight_l0_flag: [bool; 15], + pub delta_luma_weight_l0: [i8; 15], + pub luma_offset_l0: [i8; 15], + pub delta_chroma_weight_l0: [[i8; 2]; 15], + pub delta_chroma_offset_l0: [[i16; 2]; 15], + pub luma_weight_l1_flag: [bool; 15], + pub chroma_weight_l1_flag: [bool; 15], + pub delta_luma_weight_l1: [i8; 15], + pub luma_offset_l1: [i8; 15], + pub delta_chroma_weight_l1: [[i8; 2]; 15], + pub delta_chroma_offset_l1: [[i16; 2]; 15], +} + +#[derive(Debug, Clone)] +pub struct H265SliceHdr { + pub first_slice_segment_in_pic_flag: bool, + pub no_output_of_prior_pics_flag: bool, + pub pps: H265PPS, + pub dependent_slice_segment_flag: bool, + pub segment_address: u32, + pub type_0: u8, + pub pic_output_flag: bool, + pub colour_plane_id: u8, + pub pic_order_cnt_lsb: u16, + pub short_term_ref_pic_set_sps_flag: bool, + pub short_term_ref_pic_sets: H265ShortTermRefPicSet, + pub short_term_ref_pic_set_idx: u8, + pub num_long_term_sps: u8, + pub num_long_term_pics: u8, + pub lt_idx_sps: [u8; 16], + pub poc_lsb_lt: [u32; 16], + pub used_by_curr_pic_lt_flag: [bool; 16], + pub delta_poc_msb_present_flag: [bool; 16], + pub delta_poc_msb_cycle_lt: [u32; 16], + pub temporal_mvp_enabled_flag: bool, + pub sao_luma_flag: bool, + pub sao_chroma_flag: bool, + pub num_ref_idx_active_override_flag: bool, + pub num_ref_idx_l0_active_minus1: u8, + pub num_ref_idx_l1_active_minus1: u8, + pub ref_pic_list_modification: H265RefPicListModification, + pub mvd_l1_zero_flag: bool, + pub cabac_init_flag: bool, + pub collocated_from_l0_flag: bool, + pub collocated_ref_idx: u8, + pub pred_weight_table: H265PredWeightTable, + pub five_minus_max_num_merge_cand: u8, + pub qp_delta: i8, + pub cb_qp_offset: i8, + pub cr_qp_offset: i8, + pub deblocking_filter_override_flag: bool, + pub deblocking_filter_disabled_flag: bool, + pub beta_offset_div2: i8, + pub tc_offset_div2: i8, + pub loop_filter_across_slices_enabled_flag: bool, + pub num_entry_point_offsets: u32, + pub offset_len_minus1: u8, + pub entry_point_offset_minus1: *mut u32, + pub num_poc_total_curr: i32, + pub header_size: u32, + pub n_emulation_prevention_bytes: u32, + pub num_short_term_picture_slice_header_bits: u32, + pub num_long_term_picture_slice_header_bits: u32, +} diff --git a/src/h265parser/sps.rs b/src/h265parser/sps.rs new file mode 100644 index 0000000..4bc5899 --- /dev/null +++ b/src/h265parser/sps.rs @@ -0,0 +1,69 @@ +use crate::{slice::H265ShortTermRefPicSet, vui::H265VUIParams}; + +use super::{ + pps::H265ScalingList, + vps::{H265ProfileTierLevel, H265VPS}, +}; + +#[derive(Debug, Copy, Clone)] +pub struct H265SPS { + pub id: u8, + pub vps: H265VPS, + pub max_sub_layers_minus1: u8, + pub temporal_id_nesting_flag: bool, + pub profile_tier_level: H265ProfileTierLevel, + pub chroma_format_idc: u8, + pub separate_colour_plane_flag: bool, + pub pic_width_in_luma_samples: u16, + pub pic_height_in_luma_samples: u16, + 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_minus8: u8, + pub bit_depth_chroma_minus8: u8, + pub log2_max_pic_order_cnt_lsb_minus4: u8, + pub sub_layer_ordering_info_present_flag: bool, + pub max_dec_pic_buffering_minus1: [u8; 8], + pub max_num_reorder_pics: [u8; 8], + pub max_latency_increase_plus1: [u8; 8], + pub log2_min_luma_coding_block_size_minus3: u8, + pub log2_diff_max_min_luma_coding_block_size: u8, + pub log2_min_transform_block_size_minus2: u8, + pub log2_diff_max_min_transform_block_size: u8, + pub max_transform_hierarchy_depth_inter: u8, + pub max_transform_hierarchy_depth_intra: u8, + pub scaling_list_enabled_flag: bool, + pub scaling_list_data_present_flag: bool, + pub scaling_list: H265ScalingList, + pub amp_enabled_flag: bool, + pub sample_adaptive_offset_enabled_flag: bool, + pub pcm_enabled_flag: bool, + pub pcm_sample_bit_depth_luma_minus1: u8, + pub pcm_sample_bit_depth_chroma_minus1: u8, + pub log2_min_pcm_luma_coding_block_size_minus3: u8, + pub log2_diff_max_min_pcm_luma_coding_block_size: u8, + pub pcm_loop_filter_disabled_flag: bool, + pub num_short_term_ref_pic_sets: u8, + pub short_term_ref_pic_set: [H265ShortTermRefPicSet; 65], + pub long_term_ref_pics_present_flag: bool, + pub num_long_term_ref_pics_sps: u8, + pub lt_ref_pic_poc_lsb_sps: [u16; 32], + pub used_by_curr_pic_lt_sps_flag: [u8; 32], + pub temporal_mvp_enabled_flag: bool, + pub strong_intra_smoothing_enabled_flag: bool, + pub vui_parameters_present_flag: bool, + pub vui_params: Option, + pub sps_extension_flag: bool, + pub chroma_array_type: u8, + pub width: i32, + pub height: i32, + pub fps: (i32, i32), +} + +impl Default for H265SPS { + fn default() -> Self { + todo!() + } +} diff --git a/src/h265parser/vps.rs b/src/h265parser/vps.rs new file mode 100644 index 0000000..2cb5938 --- /dev/null +++ b/src/h265parser/vps.rs @@ -0,0 +1,324 @@ +use std::io; + +use anyhow::Error; +use bitstream_io::{BigEndian, BitRead, BitReader}; + +use crate::GolombCode; + +#[derive(Debug, Default, Copy, Clone)] +pub struct H265ProfileTierLevel { + pub profile_space: u8, + pub tier_flag: bool, + pub profile_idc: u8, + pub profile_compatibility_flag: [bool; 32], + pub progressive_source_flag: bool, + pub interlaced_source_flag: bool, + pub non_packed_constraint_flag: bool, + pub frame_only_constraint_flag: bool, + pub level_idc: u8, + pub sub_layer_profile_present_flag: [bool; 6], + pub sub_layer_level_present_flag: [bool; 6], + pub sub_layer_profile_space: [u8; 6], + pub sub_layer_tier_flag: [bool; 6], + pub sub_layer_profile_idc: [u8; 6], + pub sub_layer_profile_compatibility_flag: [[bool; 32]; 6], + pub sub_layer_progressive_source_flag: [bool; 6], + pub sub_layer_interlaced_source_flag: [bool; 6], + pub sub_layer_non_packed_constraint_flag: [bool; 6], + pub sub_layer_frame_only_constraint_flag: [bool; 6], + pub sub_layer_level_idc: [u8; 6], +} + +impl H265ProfileTierLevel { + pub fn parse( + bs: &mut BitReader, + max_num_sub_layers_minus1: u8, + ) -> Result { + let mut ptl = H265ProfileTierLevel::default(); + + ptl.profile_space = bs.read(2)?; + ptl.tier_flag = bs.read_bit()?; + ptl.profile_idc = bs.read(5)?; + + for j in 0..32 { + ptl.profile_compatibility_flag[j] = bs.read_bit()?; + } + + ptl.progressive_source_flag = bs.read_bit()?; + ptl.interlaced_source_flag = bs.read_bit()?; + ptl.non_packed_constraint_flag = bs.read_bit()?; + ptl.frame_only_constraint_flag = bs.read_bit()?; + + let _ = bs.read::(44)?; + ptl.level_idc = bs.read(8)?; + + for j in 0..max_num_sub_layers_minus1 as usize { + ptl.sub_layer_profile_present_flag[j] = bs.read_bit()?; + ptl.sub_layer_level_present_flag[j] = bs.read_bit()?; + } + + if max_num_sub_layers_minus1 > 0 { + for _ in max_num_sub_layers_minus1..8 { + let _ = bs.read::(2)?; + } + } + + for i in 0..max_num_sub_layers_minus1 as usize { + if ptl.sub_layer_profile_present_flag[i] { + ptl.sub_layer_profile_space[i] = bs.read(2)?; + ptl.sub_layer_tier_flag[i] = bs.read_bit()?; + ptl.sub_layer_profile_idc[i] = bs.read(5)?; + + for j in 0..32 { + ptl.sub_layer_profile_compatibility_flag[i][j] = bs.read_bit()?; + } + + ptl.sub_layer_progressive_source_flag[i] = bs.read_bit()?; + ptl.sub_layer_interlaced_source_flag[i] = bs.read_bit()?; + ptl.sub_layer_non_packed_constraint_flag[i] = bs.read_bit()?; + ptl.sub_layer_frame_only_constraint_flag[i] = bs.read_bit()?; + + let _ = bs.read::(44)?; + } + + if ptl.sub_layer_level_present_flag[i] { + ptl.sub_layer_level_idc[i] = bs.read(8)?; + } + } + + Ok(ptl) + } +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct H265HRDParams { + pub nal_hrd_parameters_present_flag: bool, + pub vcl_hrd_parameters_present_flag: bool, + pub sub_pic_hrd_params_present_flag: bool, + pub tick_divisor_minus2: u8, + pub du_cpb_removal_delay_increment_length_minus1: u8, + pub sub_pic_cpb_params_in_pic_timing_sei_flag: bool, + pub dpb_output_delay_du_length_minus1: u8, + pub bit_rate_scale: u8, + pub cpb_size_scale: u8, + pub cpb_size_du_scale: u8, + pub initial_cpb_removal_delay_length_minus1: u8, + pub au_cpb_removal_delay_length_minus1: u8, + pub dpb_output_delay_length_minus1: u8, + pub fixed_pic_rate_general_flag: [bool; 7], + pub fixed_pic_rate_within_cvs_flag: [bool; 7], + pub elemental_duration_in_tc_minus1: [u16; 7], + pub low_delay_hrd_flag: [bool; 7], + pub cpb_cnt_minus1: [u8; 7], + pub sublayer_hrd_params: [Option; 7], +} + +impl H265HRDParams { + pub fn parse( + bs: &mut BitReader, + common_inf_present_flag: bool, + max_num_sub_layers_minus1: u8, + ) -> Result { + let mut hrd = Self::default(); + + hrd.initial_cpb_removal_delay_length_minus1 = 23u8; + hrd.au_cpb_removal_delay_length_minus1 = 23u8; + hrd.dpb_output_delay_length_minus1 = 23u8; + + if common_inf_present_flag { + hrd.nal_hrd_parameters_present_flag = bs.read_bit()?; + hrd.vcl_hrd_parameters_present_flag = bs.read_bit()?; + + if hrd.nal_hrd_parameters_present_flag || hrd.vcl_hrd_parameters_present_flag { + hrd.sub_pic_hrd_params_present_flag = bs.read_bit()?; + + if hrd.sub_pic_hrd_params_present_flag { + hrd.tick_divisor_minus2 = bs.read(8)?; + hrd.du_cpb_removal_delay_increment_length_minus1 = bs.read(5)?; + hrd.sub_pic_cpb_params_in_pic_timing_sei_flag = bs.read_bit()?; + hrd.dpb_output_delay_du_length_minus1 = bs.read(5)?; + } + + hrd.bit_rate_scale = bs.read(4)?; + hrd.cpb_size_scale = bs.read(4)?; + if hrd.sub_pic_hrd_params_present_flag { + hrd.cpb_size_du_scale = bs.read(4)?; + } + + hrd.initial_cpb_removal_delay_length_minus1 = bs.read(5)?; + hrd.au_cpb_removal_delay_length_minus1 = bs.read(5)?; + hrd.dpb_output_delay_length_minus1 = bs.read(5)?; + } + } + + for i in 0..=max_num_sub_layers_minus1 as usize { + hrd.fixed_pic_rate_general_flag[i] = bs.read_bit()?; + if !hrd.fixed_pic_rate_general_flag[i] { + hrd.fixed_pic_rate_within_cvs_flag[i] = bs.read_bit()?; + } else { + hrd.fixed_pic_rate_within_cvs_flag[i] = true; + } + + if hrd.fixed_pic_rate_within_cvs_flag[i] { + hrd.elemental_duration_in_tc_minus1[i] = bs.read_ue()? as _; // <= 2047 + } else { + hrd.low_delay_hrd_flag[i] = bs.read_bit()?; + } + + if !hrd.low_delay_hrd_flag[i] { + hrd.cpb_cnt_minus1[i] = bs.read_ue()? as _; // <= 31 + } + + if hrd.vcl_hrd_parameters_present_flag || hrd.nal_hrd_parameters_present_flag { + hrd.sublayer_hrd_params[i] = Some(H265SubLayerHRDParams::parse( + bs, + hrd.cpb_cnt_minus1[i], + hrd.sub_pic_hrd_params_present_flag, + )?); + } + } + + Ok(hrd) + } +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct H265SubLayerHRDParams { + pub bit_rate_value_minus1: [u32; 32], + pub cpb_size_value_minus1: [u32; 32], + pub cpb_size_du_value_minus1: [u32; 32], + pub bit_rate_du_value_minus1: [u32; 32], + pub cbr_flag: [bool; 32], +} + +impl H265SubLayerHRDParams { + pub fn parse( + bs: &mut BitReader, + cpb_cnt: u8, + sub_pic_hrd_params_present_flag: bool, + ) -> Result { + let mut sub_hrd = H265SubLayerHRDParams::default(); + + for i in 0..=cpb_cnt as usize { + sub_hrd.bit_rate_value_minus1[i] = bs.read_ue()?; + sub_hrd.cpb_size_value_minus1[i] = bs.read_ue()?; + + if sub_pic_hrd_params_present_flag { + sub_hrd.cpb_size_du_value_minus1[i] = bs.read_ue()? as _; + sub_hrd.bit_rate_du_value_minus1[i] = bs.read_ue()? as _; + } + + sub_hrd.cbr_flag[i] = bs.read_bit()?; + } + + Ok(sub_hrd) + } +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct H265VPS { + pub id: u8, + pub max_layers_minus1: u8, + pub max_sub_layers_minus1: u8, + pub temporal_id_nesting_flag: bool, + pub profile_tier_level: H265ProfileTierLevel, + pub sub_layer_ordering_info_present_flag: bool, + pub max_dec_pic_buffering_minus1: [u8; 8], + pub max_num_reorder_pics: [u8; 8], + pub max_latency_increase_plus1: [u32; 8], + pub max_layer_id: u8, + pub num_layer_sets_minus1: u16, + pub timing_info_present_flag: bool, + pub num_units_in_tick: u32, + pub time_scale: u32, + pub poc_proportional_to_timing_flag: bool, + pub num_ticks_poc_diff_one_minus1: u32, + pub num_hrd_parameters: u16, + pub hrd_layer_set_idx: u16, + pub cprms_present_flag: bool, + pub hrd_params: H265HRDParams, + pub vps_extension: bool, + pub valid: bool, +} + +impl H265VPS { + pub fn parse(bs: &mut BitReader) -> Result { + let mut vps = H265VPS::default(); + vps.cprms_present_flag = true; + vps.id = bs.read(4)?; + let _: u8 = bs.read(2)?; + + vps.max_layers_minus1 = bs.read(6)?; + vps.max_sub_layers_minus1 = bs.read(3)?; + vps.temporal_id_nesting_flag = bs.read_bit()?; + + let _: u16 = bs.read(16)?; + + vps.profile_tier_level = H265ProfileTierLevel::parse(bs, vps.max_sub_layers_minus1)?; + vps.sub_layer_ordering_info_present_flag = bs.read_bit()?; + + let from = if vps.sub_layer_ordering_info_present_flag { + 0 + } else { + vps.max_sub_layers_minus1 as usize + }; + + for i in from..=vps.max_sub_layers_minus1 as usize { + vps.max_dec_pic_buffering_minus1[i] = bs.read_ue()? as _; + vps.max_num_reorder_pics[i] = bs.read_ue()? as _; + vps.max_dec_pic_buffering_minus1[i] = bs.read_ue()? as _; + vps.max_latency_increase_plus1[i] = bs.read_ue()? as _; + } + + /* setting default values if `vps.sub_layer_ordering_info_present_flag` is zero */ + + if !vps.sub_layer_ordering_info_present_flag && vps.max_sub_layers_minus1 > 0 { + for i in 0..=(vps.max_sub_layers_minus1 - 1) as usize { + vps.max_dec_pic_buffering_minus1[i] = + vps.max_dec_pic_buffering_minus1[vps.max_sub_layers_minus1 as usize]; + vps.max_num_reorder_pics[i] = + vps.max_num_reorder_pics[vps.max_sub_layers_minus1 as usize]; + vps.max_latency_increase_plus1[i] = + vps.max_latency_increase_plus1[vps.max_sub_layers_minus1 as usize]; + } + } + + vps.max_layer_id = bs.read(6)?; + vps.num_layer_sets_minus1 = bs.read_ue()? as _; // <= 1023 + + // CHECK_ALLOWED_MAX (vps.max_layer_id, 0)?; + // CHECK_ALLOWED_MAX (vps.num_layer_sets_minus1, 0)?; + + for _ in 1..=vps.num_layer_sets_minus1 { + for _ in 0..=vps.max_layer_id { + let _ = bs.read_bit()?; + } + } + vps.timing_info_present_flag = bs.read_bit()?; + + if vps.timing_info_present_flag { + vps.num_units_in_tick = bs.read(32)?; + vps.time_scale = bs.read(32)?; + vps.poc_proportional_to_timing_flag = bs.read_bit()?; + + if vps.poc_proportional_to_timing_flag { + vps.num_ticks_poc_diff_one_minus1 = bs.read_ue()? as _; + } + + vps.num_hrd_parameters = bs.read_ue()? as _; + // CHECK_ALLOWED_MAX (vps.num_hrd_parameters, 1)?; + + if vps.num_hrd_parameters > 0 { + vps.hrd_layer_set_idx = bs.read_ue()? as _; // <= 1023 + // CHECK_ALLOWED_MAX (vps.hrd_layer_set_idx, 0); + + vps.hrd_params = + H265HRDParams::parse(bs, vps.cprms_present_flag, vps.max_sub_layers_minus1)? + } + } + + vps.vps_extension = bs.read_bit()?; + + Ok(vps) + } +} diff --git a/src/h265parser/vui.rs b/src/h265parser/vui.rs new file mode 100644 index 0000000..cbb64f8 --- /dev/null +++ b/src/h265parser/vui.rs @@ -0,0 +1,48 @@ +use super::vps::H265HRDParams; + +#[derive(Debug, Copy, Clone, Default)] +pub struct H265VUIParams { + pub aspect_ratio_info_present_flag: bool, + pub aspect_ratio_idc: u8, + pub sar_width: u16, + pub sar_height: 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_characteristics: u8, + pub matrix_coefficients: u8, + pub chroma_loc_info_present_flag: bool, + pub chroma_sample_loc_type_top_field: u8, + pub chroma_sample_loc_type_bottom_field: u8, + 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_present_flag: bool, + pub num_units_in_tick: u32, + pub time_scale: u32, + pub poc_proportional_to_timing_flag: bool, + pub num_ticks_poc_diff_one_minus1: u32, + pub hrd_parameters_present_flag: bool, + pub hrd_params: Option, + 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: u16, + pub max_bytes_per_pic_denom: u8, + pub max_bits_per_min_cu_denom: u8, + pub log2_max_mv_length_horizontal: u8, + pub log2_max_mv_length_vertical: u8, + pub aspect_ratio: (u32, u32), +} + +impl H265VUIParams {} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ee6ff1f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,33 @@ +use std::io; + +use anyhow::Error; +use bitstream_io::{BitRead, BitReader, Endianness}; + +mod h265parser; + +pub use h265parser::*; + +trait GolombCode { + fn read_ue(&mut self) -> Result; + fn read_se(&mut self) -> Result { + let val = self.read_ue()?; + let sign = (((val & 0x1) as i32) << 1) - 1; + + Ok(((val >> 1) as i32 + (val & 0x1) as i32) * sign) + } +} + +impl GolombCode for BitReader { + fn read_ue(&mut self) -> Result { + let count = self.read_unary1()?; + + if count > 31 { + anyhow::bail!("exp golomb too large") + } else if count > 0 { + let val = self.read::(count)?; + Ok((1 << count) - 1 + val) + } else { + Ok(0) + } + } +}