├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── hevc ├── config.rs ├── hrd_parameters.rs ├── mod.rs ├── pps.rs ├── profile_tier_level.rs ├── scaling_list_data.rs ├── sei.rs ├── short_term_rps.rs ├── slice.rs ├── sps.rs ├── vps.rs └── vui_parameters.rs ├── io ├── mod.rs └── processor.rs ├── lib.rs └── utils.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | ci: 11 | name: Check, test, rustfmt and clippy 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Install Rust, clippy and rustfmt 17 | uses: dtolnay/rust-toolchain@stable 18 | with: 19 | components: clippy, rustfmt 20 | 21 | - name: Check 22 | run: | 23 | cargo check --workspace --all-features 24 | 25 | - name: Test 26 | run: | 27 | cargo test --workspace --all-features 28 | 29 | - name: Rustfmt 30 | run: | 31 | cargo fmt --all --check 32 | 33 | - name: Clippy 34 | run: | 35 | cargo clippy --workspace --all-features --all-targets --tests -- --deny warnings 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hevc_parser" 3 | version = "0.6.8" 4 | authors = ["quietvoid"] 5 | edition = "2024" 6 | rust-version = "1.85.0" 7 | license = "MIT" 8 | description = "HEVC format parser, incomplete" 9 | repository = "https://github.com/quietvoid/hevc_parser" 10 | 11 | [dependencies] 12 | nom = "8.0.0" 13 | bitvec_helpers = { version = "3.1.6", default-features = false, features = ["bitstream-io"] } 14 | anyhow = "1.0.96" 15 | regex = { version = "1.11.1", optional = true } 16 | 17 | # Matroska support 18 | matroska-demuxer = { version = "0.6.0", optional = true } 19 | 20 | [features] 21 | hevc_io = ["dep:regex", "dep:matroska-demuxer"] 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 quietvoid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## HEVC format parser 2 | 3 | Might never become a simple API, mostly for my own tools. -------------------------------------------------------------------------------- /src/hevc/config.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; 3 | 4 | /// HEVC Decoder Configuration Record 5 | /// ISO/IEC 14496-15 6 | #[derive(Default, Debug, PartialEq, Clone, Eq)] 7 | pub struct HEVCDecoderConfigurationRecord { 8 | pub configuration_version: u8, 9 | pub general_profile_space: u8, 10 | pub general_tier_flag: bool, 11 | pub general_profile_idc: u8, 12 | pub general_profile_compatibility_flags: u32, 13 | pub general_constraint_indicator_flags: u64, 14 | pub general_level_idc: u8, 15 | pub min_spatial_segmentation_idc: u16, 16 | pub parallelism_type: u8, 17 | pub chroma_format_idc: u8, 18 | pub bit_depth_luma_minus8: u8, 19 | pub bit_depth_chroma_minus8: u8, 20 | pub avg_frame_rate: u16, 21 | pub constant_frame_rate: u8, 22 | pub num_temporal_layers: u8, 23 | pub temporal_id_nested: bool, 24 | pub length_size_minus_one: u8, 25 | // nalu_arrays ignored 26 | } 27 | 28 | impl HEVCDecoderConfigurationRecord { 29 | pub fn parse(bs: &mut BsIoSliceReader) -> Result { 30 | let mut config = HEVCDecoderConfigurationRecord { 31 | configuration_version: bs.get_n(8)?, 32 | general_profile_space: bs.get_n(2)?, 33 | general_tier_flag: bs.get()?, 34 | general_profile_idc: bs.get_n(5)?, 35 | general_profile_compatibility_flags: bs.get_n(32)?, 36 | general_constraint_indicator_flags: bs.get_n(48)?, 37 | general_level_idc: bs.get_n(8)?, 38 | ..Default::default() 39 | }; 40 | 41 | bs.skip_n(4)?; // reserved 4bits 42 | config.min_spatial_segmentation_idc = bs.get_n(12)?; 43 | 44 | bs.skip_n(6)?; // reserved 6 bits 45 | config.parallelism_type = bs.get_n(2)?; 46 | 47 | bs.skip_n(6)?; // reserved 6 bits 48 | config.chroma_format_idc = bs.get_n(2)?; 49 | 50 | bs.skip_n(5)?; // reserved 5 bits 51 | config.bit_depth_luma_minus8 = bs.get_n(3)?; 52 | 53 | bs.skip_n(5)?; // reserved 5 bits 54 | config.bit_depth_chroma_minus8 = bs.get_n(3)?; 55 | 56 | config.avg_frame_rate = bs.get_n(16)?; 57 | config.constant_frame_rate = bs.get_n(2)?; 58 | config.num_temporal_layers = bs.get_n(3)?; 59 | config.temporal_id_nested = bs.get()?; 60 | config.length_size_minus_one = bs.get_n(2)?; 61 | 62 | Ok(config) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/hevc/hrd_parameters.rs: -------------------------------------------------------------------------------- 1 | use super::BsIoVecReader; 2 | use anyhow::Result; 3 | 4 | #[derive(Default)] 5 | pub struct HrdParameters {} 6 | 7 | pub struct SubLayerHrdParameter {} 8 | 9 | impl HrdParameters { 10 | pub fn parse( 11 | bs: &mut BsIoVecReader, 12 | common_inf_present: bool, 13 | vps_max_sub_layers: u8, 14 | ) -> Result<()> { 15 | let mut nal_params_present = false; 16 | let mut vcl_params_present = false; 17 | let mut subpic_params_present = false; 18 | 19 | if common_inf_present { 20 | nal_params_present = bs.get()?; 21 | vcl_params_present = bs.get()?; 22 | 23 | if nal_params_present || vcl_params_present { 24 | subpic_params_present = bs.get()?; 25 | 26 | if subpic_params_present { 27 | bs.skip_n(8)?; // tick_divisor_minus2 28 | bs.skip_n(5)?; // du_cpb_removal_delay_increment_length_minus1 29 | bs.skip_n(1)?; // sub_pic_cpb_params_in_pic_timing_sei_flag 30 | bs.skip_n(5)?; // dpb_output_delay_du_length_minus1 31 | } 32 | 33 | bs.skip_n(4)?; // bit_rate_scale 34 | bs.skip_n(4)?; // cpb_size_scale 35 | 36 | if subpic_params_present { 37 | bs.skip_n(4)?; // cpb_size_du_scale 38 | } 39 | 40 | bs.skip_n(5)?; // initial_cpb_removal_delay_length_minus1 41 | bs.skip_n(5)?; // au_cpb_removal_delay_length_minus1 42 | bs.skip_n(5)?; // dpb_output_delay_length_minus1 43 | } 44 | } 45 | 46 | for _ in 0..vps_max_sub_layers { 47 | let mut low_delay = false; 48 | let mut nb_cpb = 1; 49 | let mut fixed_rate = bs.get()?; 50 | 51 | if !fixed_rate { 52 | fixed_rate = bs.get()?; 53 | } 54 | 55 | if fixed_rate { 56 | bs.get_ue()?; 57 | } else { 58 | low_delay = bs.get()?; 59 | } 60 | 61 | if !low_delay { 62 | nb_cpb = bs.get_ue()? + 1; 63 | } 64 | 65 | if nal_params_present { 66 | SubLayerHrdParameter::parse(bs, nb_cpb, subpic_params_present)?; 67 | } 68 | 69 | if vcl_params_present { 70 | SubLayerHrdParameter::parse(bs, nb_cpb, subpic_params_present)?; 71 | } 72 | } 73 | 74 | Ok(()) 75 | } 76 | } 77 | 78 | impl SubLayerHrdParameter { 79 | pub fn parse(bs: &mut BsIoVecReader, nb_cpb: u64, subpic_params_present: bool) -> Result<()> { 80 | for _ in 0..nb_cpb { 81 | bs.get_ue()?; // bit_rate_value_minus1 82 | bs.get_ue()?; // cpb_size_value_minus1 83 | 84 | if subpic_params_present { 85 | bs.get_ue()?; // cpb_size_du_value_minus1 86 | bs.get_ue()?; // bit_rate_du_value_minus1 87 | } 88 | 89 | bs.skip_n(1)?; // cbr_flag 90 | } 91 | 92 | Ok(()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/hevc/mod.rs: -------------------------------------------------------------------------------- 1 | use self::slice::SliceNAL; 2 | 3 | use super::{BsIoVecReader, NALUStartCode}; 4 | 5 | pub mod config; 6 | pub(crate) mod hrd_parameters; 7 | pub(crate) mod pps; 8 | pub(crate) mod profile_tier_level; 9 | pub(crate) mod scaling_list_data; 10 | pub mod sei; 11 | pub(crate) mod short_term_rps; 12 | pub(crate) mod slice; 13 | pub(crate) mod sps; 14 | pub(crate) mod vps; 15 | pub(crate) mod vui_parameters; 16 | 17 | // https://github.com/virinext/hevcesbrowser/blob/master/hevcparser/include/Hevc.h 18 | pub const NAL_TRAIL_N: u8 = 0; 19 | pub const NAL_TRAIL_R: u8 = 1; 20 | pub const NAL_TSA_N: u8 = 2; 21 | pub const NAL_TSA_R: u8 = 3; 22 | pub const NAL_STSA_N: u8 = 4; 23 | pub const NAL_STSA_R: u8 = 5; 24 | pub const NAL_RADL_N: u8 = 6; 25 | pub const NAL_RADL_R: u8 = 7; 26 | pub const NAL_RASL_N: u8 = 8; 27 | pub const NAL_RASL_R: u8 = 9; 28 | pub const NAL_BLA_W_LP: u8 = 16; 29 | pub const NAL_BLA_W_RADL: u8 = 17; 30 | pub const NAL_BLA_N_LP: u8 = 18; 31 | pub const NAL_IDR_W_RADL: u8 = 19; 32 | pub const NAL_IDR_N_LP: u8 = 20; 33 | pub const NAL_CRA_NUT: u8 = 21; 34 | pub const NAL_IRAP_VCL23: u8 = 23; 35 | pub const NAL_VPS: u8 = 32; 36 | pub const NAL_SPS: u8 = 33; 37 | pub const NAL_PPS: u8 = 34; 38 | pub const NAL_AUD: u8 = 35; 39 | pub const NAL_EOS_NUT: u8 = 36; 40 | pub const NAL_EOB_NUT: u8 = 37; 41 | pub const NAL_FD_NUT: u8 = 38; 42 | pub const NAL_SEI_PREFIX: u8 = 39; 43 | pub const NAL_SEI_SUFFIX: u8 = 40; 44 | pub const NAL_UNSPEC62: u8 = 62; 45 | pub const NAL_UNSPEC63: u8 = 63; 46 | 47 | pub const USER_DATA_REGISTERED_ITU_T_35: u8 = 4; 48 | 49 | pub use sei::SeiMessage; 50 | 51 | #[derive(Default, Debug, Clone)] 52 | pub struct NALUnit { 53 | pub start: usize, 54 | pub end: usize, 55 | 56 | pub nal_type: u8, 57 | pub nuh_layer_id: u8, 58 | pub temporal_id: u8, 59 | 60 | pub start_code: NALUStartCode, 61 | 62 | #[deprecated(since = "0.4.0", note = "Please use `start_code` instead")] 63 | pub start_code_len: u8, 64 | 65 | pub decoded_frame_index: u64, 66 | } 67 | 68 | #[derive(Default, Debug, Clone)] 69 | pub struct Frame { 70 | pub decoded_number: u64, 71 | pub presentation_number: u64, 72 | pub frame_type: u64, 73 | 74 | pub nals: Vec, 75 | pub first_slice: SliceNAL, 76 | } 77 | 78 | impl NALUnit { 79 | pub fn is_type_slice(nal_type: u8) -> bool { 80 | matches!( 81 | nal_type, 82 | NAL_TRAIL_R 83 | | NAL_TRAIL_N 84 | | NAL_TSA_N 85 | | NAL_TSA_R 86 | | NAL_STSA_N 87 | | NAL_STSA_R 88 | | NAL_BLA_W_LP 89 | | NAL_BLA_W_RADL 90 | | NAL_BLA_N_LP 91 | | NAL_IDR_W_RADL 92 | | NAL_IDR_N_LP 93 | | NAL_CRA_NUT 94 | | NAL_RADL_N 95 | | NAL_RADL_R 96 | | NAL_RASL_N 97 | | NAL_RASL_R 98 | ) 99 | } 100 | 101 | pub fn is_slice(&self) -> bool { 102 | Self::is_type_slice(self.nal_type) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/hevc/pps.rs: -------------------------------------------------------------------------------- 1 | use super::{BsIoVecReader, scaling_list_data::ScalingListData}; 2 | use anyhow::Result; 3 | 4 | #[allow(clippy::upper_case_acronyms)] 5 | #[derive(Default, Debug, PartialEq, Eq)] 6 | pub struct PPSNAL { 7 | pub(crate) pps_id: u64, 8 | pub(crate) sps_id: u64, 9 | pub(crate) dependent_slice_segments_enabled_flag: bool, 10 | pub(crate) output_flag_present_flag: bool, 11 | pub(crate) num_extra_slice_header_bits: u8, 12 | sign_data_hiding_flag: bool, 13 | cabac_init_present_flag: bool, 14 | num_ref_idx_l0_default_active: u64, 15 | num_ref_idx_l1_default_active: u64, 16 | pic_init_qp_minus26: i64, 17 | constrained_intra_pred_flag: bool, 18 | transform_skip_enabled_flag: bool, 19 | cu_qp_delta_enabled_flag: bool, 20 | diff_cu_qp_delta_depth: u64, 21 | cb_qp_offset: i64, 22 | cr_qp_offset: i64, 23 | pic_slice_level_chroma_qp_offsets_present_flag: bool, 24 | weighted_pred_flag: bool, 25 | weighted_bipred_flag: bool, 26 | transquant_bypass_enable_flag: bool, 27 | tiles_enabled_flag: bool, 28 | entropy_coding_sync_enabled_flag: bool, 29 | 30 | num_tile_columns: u64, 31 | num_tile_rows: u64, 32 | uniform_spacing_flag: bool, 33 | 34 | column_widths: Vec, 35 | row_heights: Vec, 36 | 37 | loop_filter_across_tiles_enabled_flag: bool, 38 | seq_loop_filter_across_slices_enabled_flag: bool, 39 | deblocking_filter_control_present_flag: bool, 40 | deblocking_filter_override_enabled_flag: bool, 41 | disable_dbf: bool, 42 | beta_offset: i64, 43 | tc_offset: i64, 44 | 45 | scaling_list_data_present_flag: bool, 46 | scaling_list_data: ScalingListData, 47 | 48 | lists_modification_present_flag: bool, 49 | log2_parallel_merge_level: u64, 50 | slice_header_extension_present_flag: bool, 51 | pps_extension_present_flag: bool, 52 | } 53 | 54 | impl PPSNAL { 55 | pub fn parse(bs: &mut BsIoVecReader) -> Result { 56 | let mut pps = PPSNAL { 57 | pps_id: bs.get_ue()?, 58 | sps_id: bs.get_ue()?, 59 | ..Default::default() 60 | }; 61 | 62 | pps.dependent_slice_segments_enabled_flag = bs.get()?; 63 | pps.output_flag_present_flag = bs.get()?; 64 | pps.num_extra_slice_header_bits = bs.get_n(3)?; 65 | pps.sign_data_hiding_flag = bs.get()?; 66 | pps.cabac_init_present_flag = bs.get()?; 67 | pps.num_ref_idx_l0_default_active = bs.get_ue()? + 1; 68 | pps.num_ref_idx_l1_default_active = bs.get_ue()? + 1; 69 | pps.pic_init_qp_minus26 = bs.get_se()?; 70 | pps.constrained_intra_pred_flag = bs.get()?; 71 | pps.transform_skip_enabled_flag = bs.get()?; 72 | pps.cu_qp_delta_enabled_flag = bs.get()?; 73 | 74 | pps.diff_cu_qp_delta_depth = if pps.cu_qp_delta_enabled_flag { 75 | bs.get_ue()? 76 | } else { 77 | 0 78 | }; 79 | 80 | pps.cb_qp_offset = bs.get_se()?; 81 | pps.cr_qp_offset = bs.get_se()?; 82 | 83 | pps.pic_slice_level_chroma_qp_offsets_present_flag = bs.get()?; 84 | pps.weighted_pred_flag = bs.get()?; 85 | pps.weighted_bipred_flag = bs.get()?; 86 | 87 | pps.transquant_bypass_enable_flag = bs.get()?; 88 | pps.tiles_enabled_flag = bs.get()?; 89 | pps.entropy_coding_sync_enabled_flag = bs.get()?; 90 | 91 | if pps.tiles_enabled_flag { 92 | pps.num_tile_columns = bs.get_ue()? + 1; 93 | pps.num_tile_rows = bs.get_ue()? + 1; 94 | 95 | pps.uniform_spacing_flag = bs.get()?; 96 | 97 | if !pps.uniform_spacing_flag { 98 | for _ in 0..pps.num_tile_columns - 1 { 99 | pps.column_widths.push(bs.get_ue()? + 1); 100 | } 101 | 102 | for _ in 0..pps.num_tile_rows - 1 { 103 | pps.row_heights.push(bs.get_ue()? + 1); 104 | } 105 | } 106 | 107 | pps.loop_filter_across_tiles_enabled_flag = bs.get()?; 108 | } 109 | 110 | pps.seq_loop_filter_across_slices_enabled_flag = bs.get()?; 111 | pps.deblocking_filter_control_present_flag = bs.get()?; 112 | 113 | if pps.deblocking_filter_control_present_flag { 114 | pps.deblocking_filter_override_enabled_flag = bs.get()?; 115 | pps.disable_dbf = bs.get()?; 116 | 117 | if !pps.disable_dbf { 118 | pps.beta_offset = 2 * bs.get_se()?; 119 | pps.tc_offset = 2 * bs.get_se()?; 120 | } 121 | } 122 | 123 | pps.scaling_list_data_present_flag = bs.get()?; 124 | if pps.scaling_list_data_present_flag { 125 | pps.scaling_list_data = ScalingListData::parse(bs)?; 126 | } 127 | 128 | pps.lists_modification_present_flag = bs.get()?; 129 | pps.log2_parallel_merge_level = bs.get_ue()? + 2; 130 | 131 | pps.slice_header_extension_present_flag = bs.get()?; 132 | pps.pps_extension_present_flag = bs.get()?; 133 | 134 | Ok(pps) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/hevc/profile_tier_level.rs: -------------------------------------------------------------------------------- 1 | use super::BsIoVecReader; 2 | use anyhow::Result; 3 | 4 | #[derive(Default, Debug, PartialEq, Clone, Eq)] 5 | pub struct ProfileTierLevel { 6 | pub general_profile_space: u8, 7 | pub general_tier_flag: bool, 8 | pub general_profile_idc: u8, 9 | pub general_profile_compatibility_flag: Vec, 10 | pub general_progressive_source_flag: bool, 11 | pub general_interlaced_source_flag: bool, 12 | pub general_non_packed_constraint_flag: bool, 13 | pub general_frame_only_constraint_flag: bool, 14 | pub general_level_idc: u8, 15 | 16 | pub sub_layer_profile_present_flag: Vec, 17 | pub sub_layer_level_present_flag: Vec, 18 | pub sub_layer_profile_space: Vec, 19 | pub sub_layer_tier_flag: Vec, 20 | pub sub_layer_profile_idc: Vec, 21 | pub sub_layer_profile_compatibility_flag: Vec, 22 | pub sub_layer_progressive_source_flag: Vec, 23 | pub sub_layer_interlaced_source_flag: Vec, 24 | pub sub_layer_non_packed_constraint_flag: Vec, 25 | pub sub_layer_frame_only_constraint_flag: Vec, 26 | pub sub_layer_level_idc: Vec, 27 | } 28 | 29 | impl ProfileTierLevel { 30 | pub fn parse(&mut self, bs: &mut BsIoVecReader, max_sub_layers: u8) -> Result<()> { 31 | self.general_profile_space = bs.get_n(2)?; 32 | self.general_tier_flag = bs.get()?; 33 | self.general_profile_idc = bs.get_n(5)?; 34 | 35 | for _ in 0..32 { 36 | self.general_profile_compatibility_flag.push(bs.get()?); 37 | } 38 | 39 | self.general_progressive_source_flag = bs.get()?; 40 | self.general_interlaced_source_flag = bs.get()?; 41 | self.general_non_packed_constraint_flag = bs.get()?; 42 | self.general_frame_only_constraint_flag = bs.get()?; 43 | bs.skip_n(32)?; 44 | bs.skip_n(12)?; 45 | self.general_level_idc = bs.get_n(8)?; 46 | 47 | let max_sub_layers_minus1 = max_sub_layers - 1; 48 | for _ in 0..max_sub_layers_minus1 { 49 | self.sub_layer_profile_present_flag.push(bs.get()?); 50 | self.sub_layer_level_present_flag.push(bs.get()?); 51 | } 52 | 53 | if max_sub_layers_minus1 > 0 { 54 | for _ in max_sub_layers_minus1..8 { 55 | bs.skip_n(2)?; 56 | } 57 | } 58 | 59 | for i in 0..max_sub_layers_minus1 as usize { 60 | if self.sub_layer_profile_present_flag[i] { 61 | self.sub_layer_profile_space.push(bs.get_n(2)?); 62 | self.sub_layer_tier_flag.push(bs.get()?); 63 | self.sub_layer_profile_idc.push(bs.get_n(5)?); 64 | 65 | for _ in 0..32 { 66 | self.sub_layer_profile_compatibility_flag.push(bs.get()?); 67 | } 68 | 69 | self.sub_layer_progressive_source_flag.push(bs.get()?); 70 | self.sub_layer_interlaced_source_flag.push(bs.get()?); 71 | self.sub_layer_non_packed_constraint_flag.push(bs.get()?); 72 | self.sub_layer_frame_only_constraint_flag.push(bs.get()?); 73 | 74 | bs.skip_n(32)?; 75 | bs.skip_n(12)?; 76 | } 77 | 78 | if self.sub_layer_level_present_flag[i] { 79 | self.sub_layer_level_idc.push(bs.get_n(8)?); 80 | } else { 81 | self.sub_layer_level_idc.push(1); 82 | } 83 | } 84 | 85 | Ok(()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/hevc/scaling_list_data.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::cmp::min; 3 | 4 | use super::BsIoVecReader; 5 | 6 | #[derive(Default, Debug, PartialEq, Clone, Eq)] 7 | pub struct ScalingListData { 8 | scaling_list_pred_mode_flag: Vec>, 9 | scaling_list_pred_matrix_id_delta: Vec>, 10 | scaling_list_dc_coef_minus8: Vec>, 11 | scaling_list_delta_coef: Vec>>, 12 | } 13 | 14 | impl ScalingListData { 15 | pub fn parse(bs: &mut BsIoVecReader) -> Result { 16 | let mut scl = ScalingListData::default(); 17 | 18 | scl.scaling_list_pred_mode_flag.resize(4, Vec::new()); 19 | scl.scaling_list_pred_matrix_id_delta.resize(4, Vec::new()); 20 | scl.scaling_list_dc_coef_minus8.resize(2, Vec::new()); 21 | scl.scaling_list_delta_coef.resize(4, Vec::new()); 22 | 23 | for size_id in 0..4 { 24 | let matrix_size = if size_id == 3 { 2 } else { 6 }; 25 | 26 | scl.scaling_list_pred_mode_flag[size_id].resize(matrix_size, false); 27 | scl.scaling_list_pred_matrix_id_delta[size_id].resize(matrix_size, 0); 28 | scl.scaling_list_delta_coef[size_id].resize(matrix_size, Vec::new()); 29 | 30 | if size_id >= 2 { 31 | scl.scaling_list_dc_coef_minus8[size_id - 2].resize(matrix_size, 0); 32 | } 33 | 34 | for matrix_id in 0..matrix_size { 35 | scl.scaling_list_pred_mode_flag[size_id][matrix_id] = bs.get()?; 36 | 37 | if !scl.scaling_list_pred_mode_flag[size_id][matrix_id] { 38 | scl.scaling_list_pred_matrix_id_delta[size_id][matrix_id] = bs.get_ue()?; 39 | } else { 40 | let _next_coef = 8; 41 | let coef_num = min(64, 1 << (4 + (size_id << 1))); 42 | 43 | if size_id > 1 { 44 | scl.scaling_list_dc_coef_minus8[size_id - 2][matrix_id] = bs.get_se()?; 45 | } 46 | 47 | scl.scaling_list_delta_coef[size_id][matrix_id].resize(coef_num, 0); 48 | 49 | for i in 0..coef_num { 50 | scl.scaling_list_delta_coef[size_id][matrix_id][i] = bs.get_se()?; 51 | } 52 | } 53 | } 54 | } 55 | 56 | Ok(scl) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/hevc/sei.rs: -------------------------------------------------------------------------------- 1 | use super::{NAL_EOB_NUT, NAL_EOS_NUT, NAL_SEI_PREFIX, NAL_SEI_SUFFIX}; 2 | use anyhow::{Result, bail}; 3 | use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; 4 | 5 | #[derive(Default, Debug, Clone)] 6 | pub struct SeiMessage { 7 | num_payload_type_ff_bytes: usize, 8 | last_payload_type_byte: u8, 9 | 10 | num_payload_size_ff_bytes: usize, 11 | last_payload_size_byte: u8, 12 | 13 | // Offset of the messame in the input slice 14 | pub msg_offset: usize, 15 | 16 | pub payload_type: u8, 17 | pub payload_offset: usize, 18 | pub payload_size: usize, 19 | } 20 | 21 | impl SeiMessage { 22 | /// Assumes the data does not contain any `emulation_prevention_three_byte`s 23 | pub fn parse_sei_rbsp(data: &[u8]) -> Result> { 24 | let mut reader = BsIoSliceReader::from_slice(data); 25 | 26 | // forbidden_zero_bit 27 | reader.skip_n(1)?; 28 | 29 | let nal_type = reader.get_n::(6)?; 30 | 31 | if nal_type != NAL_SEI_PREFIX && nal_type != NAL_SEI_SUFFIX { 32 | bail!("NAL type {} is not SEI", nal_type); 33 | } 34 | 35 | if reader.available()? < 9 && matches!(nal_type, NAL_EOS_NUT | NAL_EOB_NUT) { 36 | } else { 37 | reader.skip_n(6)?; // nuh_layer_id 38 | reader.skip_n(3)?; // temporal_id 39 | } 40 | 41 | let mut messages = Vec::new(); 42 | 43 | loop { 44 | messages.push(Self::parse_sei_message(&mut reader)?); 45 | 46 | if reader.available()? <= 8 { 47 | break; 48 | } 49 | } 50 | 51 | Ok(messages) 52 | } 53 | 54 | fn parse_sei_message(reader: &mut BsIoSliceReader) -> Result { 55 | let mut msg = SeiMessage { 56 | msg_offset: (reader.position()? / 8) as usize, 57 | last_payload_type_byte: reader.get_n(8)?, 58 | ..Default::default() 59 | }; 60 | 61 | while msg.last_payload_type_byte == 0xFF { 62 | msg.num_payload_type_ff_bytes += 1; 63 | msg.last_payload_type_byte = reader.get_n(8)?; 64 | 65 | msg.payload_type += 255; 66 | } 67 | 68 | msg.payload_type += msg.last_payload_type_byte; 69 | 70 | msg.last_payload_size_byte = reader.get_n(8)?; 71 | while msg.last_payload_size_byte == 0xFF { 72 | msg.num_payload_size_ff_bytes += 1; 73 | msg.last_payload_size_byte = reader.get_n(8)?; 74 | 75 | msg.payload_size += 255; 76 | } 77 | 78 | msg.payload_size += msg.last_payload_size_byte as usize; 79 | msg.payload_offset = (reader.position()? / 8) as usize; 80 | 81 | if msg.payload_size > reader.available()? as usize { 82 | bail!("Payload size is larger than NALU size"); 83 | } 84 | 85 | reader.skip_n(msg.payload_size as u32 * 8)?; 86 | 87 | Ok(msg) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/hevc/short_term_rps.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use super::BsIoVecReader; 4 | use super::sps::SPSNAL; 5 | 6 | #[derive(Default, Debug, PartialEq, Clone, Eq)] 7 | pub struct ShortTermRPS { 8 | inter_ref_pic_set_prediction_flag: bool, 9 | delta_idx: u64, 10 | delta_rps_sign: bool, 11 | abs_delta_rps: u64, 12 | used_by_curr_pic_flags: Vec, 13 | use_delta_flags: Vec, 14 | num_delta_pocs: u64, 15 | num_negative_pics: u64, 16 | num_positive_pics: u64, 17 | 18 | delta_poc_s0: Vec, 19 | used_by_curr_pic_s0_flags: Vec, 20 | delta_poc_s1: Vec, 21 | used_by_curr_pic_s1_flags: Vec, 22 | } 23 | 24 | impl ShortTermRPS { 25 | pub fn parse( 26 | bs: &mut BsIoVecReader, 27 | sps: &SPSNAL, 28 | st_rps_idx: usize, 29 | nb_st_rps: u64, 30 | is_slice_header: bool, 31 | ) -> Result { 32 | let mut rps = ShortTermRPS::default(); 33 | 34 | if st_rps_idx > 0 && nb_st_rps > 0 { 35 | rps.inter_ref_pic_set_prediction_flag = bs.get()?; 36 | } 37 | 38 | if rps.inter_ref_pic_set_prediction_flag { 39 | let ref_pic_sets = &sps.short_term_ref_pic_sets; 40 | 41 | if st_rps_idx == nb_st_rps as usize || is_slice_header { 42 | rps.delta_idx = bs.get_ue()?; 43 | } 44 | 45 | rps.delta_rps_sign = bs.get()?; 46 | rps.abs_delta_rps = bs.get_ue()? + 1; 47 | 48 | let ref_rps_idx = st_rps_idx - (rps.delta_idx as usize + 1); 49 | let mut num_delta_pocs: usize = 0; 50 | let ref_rps = &ref_pic_sets[ref_rps_idx]; 51 | 52 | if ref_rps.inter_ref_pic_set_prediction_flag { 53 | for i in 0..ref_rps.used_by_curr_pic_flags.len() { 54 | if ref_rps.used_by_curr_pic_flags[i] || ref_rps.use_delta_flags[i] { 55 | num_delta_pocs += 1; 56 | } 57 | } 58 | } else { 59 | num_delta_pocs = (ref_rps.num_negative_pics + ref_rps.num_positive_pics) as usize; 60 | } 61 | 62 | rps.used_by_curr_pic_flags.resize(num_delta_pocs + 1, false); 63 | rps.use_delta_flags.resize(num_delta_pocs + 1, true); 64 | 65 | for i in 0..=num_delta_pocs { 66 | rps.used_by_curr_pic_flags[i] = bs.get()?; 67 | 68 | if !rps.used_by_curr_pic_flags[i] { 69 | rps.use_delta_flags[i] = bs.get()?; 70 | } 71 | } 72 | } else { 73 | rps.num_negative_pics = bs.get_ue()?; 74 | rps.num_positive_pics = bs.get_ue()?; 75 | 76 | for _ in 0..rps.num_negative_pics { 77 | rps.delta_poc_s0.push(bs.get_ue()? + 1); 78 | rps.used_by_curr_pic_s0_flags.push(bs.get()?); 79 | } 80 | 81 | for _ in 0..rps.num_positive_pics { 82 | rps.delta_poc_s1.push(bs.get_ue()? + 1); 83 | rps.used_by_curr_pic_s1_flags.push(bs.get()?); 84 | } 85 | } 86 | 87 | Ok(rps) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/hevc/slice.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Result, format_err}; 2 | 3 | use super::BsIoVecReader; 4 | use super::*; 5 | use super::{NALUnit, pps::PPSNAL, sps::SPSNAL}; 6 | 7 | #[derive(Default, Debug, PartialEq, Clone, Eq)] 8 | pub struct SliceNAL { 9 | pub first_slice_in_pic_flag: bool, 10 | pub key_frame: bool, 11 | pps_id: u64, 12 | pub slice_type: u64, 13 | 14 | dependent_slice_segment_flag: bool, 15 | slice_segment_addr: u64, 16 | 17 | pic_order_cnt_lsb: u64, 18 | pub output_picture_number: u64, 19 | } 20 | 21 | impl SliceNAL { 22 | pub fn parse( 23 | bs: &mut BsIoVecReader, 24 | sps_list: &[SPSNAL], 25 | pps_list: &[PPSNAL], 26 | nal: &NALUnit, 27 | poc_tid0: &mut u64, 28 | poc: &mut u64, 29 | ) -> Result { 30 | let mut slice = SliceNAL { 31 | first_slice_in_pic_flag: bs.get()?, 32 | ..Default::default() 33 | }; 34 | 35 | if is_irap_nal(nal) { 36 | slice.key_frame = true; 37 | bs.skip_n(1)?; // no_output_of_prior_pics_flag 38 | } 39 | 40 | slice.pps_id = bs.get_ue()?; 41 | let pps = pps_list 42 | .get(slice.pps_id as usize) 43 | .ok_or_else(|| format_err!("Invalid PPS index"))?; 44 | let sps = sps_list 45 | .get(pps.sps_id as usize) 46 | .ok_or_else(|| format_err!("Invalid SPS index"))?; 47 | 48 | if !slice.first_slice_in_pic_flag { 49 | if pps.dependent_slice_segments_enabled_flag { 50 | slice.dependent_slice_segment_flag = bs.get()?; 51 | } else { 52 | slice.dependent_slice_segment_flag = false; 53 | } 54 | 55 | let pic_size = (sps.ctb_width * sps.ctb_height) as f64; 56 | let slice_address_length = pic_size.log2().ceil() as u32; 57 | 58 | slice.slice_segment_addr = bs.get_n(slice_address_length)?; 59 | } else { 60 | slice.dependent_slice_segment_flag = false; 61 | } 62 | 63 | if slice.dependent_slice_segment_flag { 64 | return Ok(slice); 65 | } 66 | 67 | for _ in 0..pps.num_extra_slice_header_bits { 68 | bs.skip_n(1)?; // slice_reserved_undetermined_flag 69 | } 70 | 71 | slice.slice_type = bs.get_ue()?; 72 | 73 | if pps.output_flag_present_flag { 74 | bs.skip_n(1)?; 75 | } 76 | 77 | if sps.separate_colour_plane_flag { 78 | bs.skip_n(2)?; 79 | } 80 | 81 | if !is_idr_nal(nal) { 82 | slice.pic_order_cnt_lsb = bs.get_n(sps.log2_max_poc_lsb as u32)?; 83 | slice.output_picture_number = compute_poc(sps, *poc_tid0, slice.pic_order_cnt_lsb, nal); 84 | } else { 85 | slice.output_picture_number = 0; 86 | } 87 | 88 | *poc = slice.output_picture_number; 89 | 90 | if nal.temporal_id == 0 91 | && nal.nal_type != NAL_TRAIL_N 92 | && nal.nal_type != NAL_TSA_N 93 | && nal.nal_type != NAL_STSA_N 94 | && nal.nal_type != NAL_RADL_N 95 | && nal.nal_type != NAL_RASL_N 96 | && nal.nal_type != NAL_RADL_R 97 | && nal.nal_type != NAL_RASL_R 98 | { 99 | *poc_tid0 = *poc; 100 | } 101 | 102 | Ok(slice) 103 | } 104 | } 105 | 106 | fn is_irap_nal(nal: &NALUnit) -> bool { 107 | nal.nal_type >= 16 && nal.nal_type <= 23 108 | } 109 | 110 | fn is_idr_nal(nal: &NALUnit) -> bool { 111 | nal.nal_type == NAL_IDR_W_RADL || nal.nal_type == NAL_IDR_N_LP 112 | } 113 | 114 | fn compute_poc(sps: &SPSNAL, poc_tid0: u64, poc_lsb: u64, nal: &NALUnit) -> u64 { 115 | let max_poc_lsb = 1 << sps.log2_max_poc_lsb; 116 | let prev_poc_lsb = poc_tid0 % max_poc_lsb; 117 | let prev_poc_msb = poc_tid0 - prev_poc_lsb; 118 | 119 | let mut poc_msb = if poc_lsb < prev_poc_lsb && prev_poc_lsb - poc_lsb >= max_poc_lsb / 2 { 120 | prev_poc_msb + max_poc_lsb 121 | } else if poc_lsb > prev_poc_lsb && poc_lsb - prev_poc_lsb > max_poc_lsb / 2 { 122 | prev_poc_msb - max_poc_lsb 123 | } else { 124 | prev_poc_msb 125 | }; 126 | 127 | if nal.nal_type == NAL_BLA_W_LP 128 | || nal.nal_type == NAL_BLA_W_RADL 129 | || nal.nal_type == NAL_BLA_N_LP 130 | { 131 | poc_msb = 0; 132 | } 133 | 134 | poc_msb + poc_lsb 135 | } 136 | -------------------------------------------------------------------------------- /src/hevc/sps.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use super::BsIoVecReader; 4 | use super::profile_tier_level::ProfileTierLevel; 5 | use super::scaling_list_data::ScalingListData; 6 | use super::short_term_rps::ShortTermRPS; 7 | use super::vui_parameters::VuiParameters; 8 | 9 | #[allow(clippy::upper_case_acronyms)] 10 | #[derive(Default, Debug, PartialEq, Clone, Eq)] 11 | pub struct SPSNAL { 12 | pub(crate) vps_id: u8, 13 | max_sub_layers: u8, 14 | temporal_id_nesting_flag: bool, 15 | 16 | ptl: ProfileTierLevel, 17 | pub(crate) sps_id: u64, 18 | chroma_format_idc: u64, 19 | pub(crate) separate_colour_plane_flag: bool, 20 | width: u64, 21 | height: u64, 22 | 23 | pic_conformance_flag: bool, 24 | conf_win_left_offset: u64, 25 | conf_win_right_offset: u64, 26 | conf_win_top_offset: u64, 27 | conf_win_bottom_offset: u64, 28 | 29 | bit_depth: u64, 30 | bit_depth_chroma: u64, 31 | pub(crate) log2_max_poc_lsb: u64, 32 | sublayer_ordering_info: bool, 33 | max_dec_pic_buffering: Vec, 34 | num_reorder_pics: Vec, 35 | max_latency_increase: Vec, 36 | 37 | log2_min_cb_size: u64, 38 | log2_diff_max_min_coding_block_size: u64, 39 | log2_min_tb_size: u64, 40 | log2_diff_max_min_transform_block_size: u64, 41 | max_transform_hierarchy_depth_inter: u64, 42 | max_transform_hierarchy_depth_intra: u64, 43 | 44 | scaling_list_enabled_flag: bool, 45 | scaling_list_data_present_flag: bool, 46 | scaling_list_data: ScalingListData, 47 | 48 | amp_enabled_flag: bool, 49 | sao_enabled_flag: bool, 50 | pcm_enabled_flag: bool, 51 | pcm_bit_depth: u8, 52 | pcm_bit_depth_chroma: u8, 53 | pcm_log2_min_pcm_cb_size: u64, 54 | pcm_log2_max_pcm_cb_size: u64, 55 | pcm_loop_filter_disable_flag: bool, 56 | 57 | nb_st_rps: u64, 58 | pub(crate) short_term_ref_pic_sets: Vec, 59 | 60 | long_term_ref_pics_present_flag: bool, 61 | num_long_term_ref_pics_sps: u64, 62 | lt_ref_pic_poc_lsb_sps: Vec, 63 | used_by_curr_pic_lt_sps_flag: Vec, 64 | 65 | sps_temporal_mvp_enabled_flag: bool, 66 | sps_strong_intra_smoothing_enable_flag: bool, 67 | 68 | vui_present: bool, 69 | vui_parameters: VuiParameters, 70 | 71 | sps_extension_flag: bool, 72 | 73 | // Computed values 74 | pub(crate) log2_ctb_size: u64, 75 | pub(crate) log2_min_pu_size: u64, 76 | pub(crate) ctb_width: u64, 77 | pub(crate) ctb_height: u64, 78 | pub(crate) ctb_size: u64, 79 | pub(crate) min_cb_width: u64, 80 | pub(crate) min_cb_height: u64, 81 | pub(crate) min_tb_width: u64, 82 | pub(crate) min_tb_height: u64, 83 | pub(crate) min_pu_width: u64, 84 | pub(crate) min_pu_height: u64, 85 | pub(crate) tb_mask: u64, 86 | } 87 | 88 | impl SPSNAL { 89 | pub fn parse(bs: &mut BsIoVecReader) -> Result { 90 | let mut sps = SPSNAL { 91 | vps_id: bs.get_n(4)?, 92 | ..Default::default() 93 | }; 94 | 95 | sps.max_sub_layers = bs.get_n::(3)? + 1; 96 | sps.temporal_id_nesting_flag = bs.get()?; 97 | 98 | sps.ptl.parse(bs, sps.max_sub_layers)?; 99 | 100 | sps.sps_id = bs.get_ue()?; 101 | sps.chroma_format_idc = bs.get_ue()?; 102 | 103 | if sps.chroma_format_idc == 3 { 104 | sps.separate_colour_plane_flag = bs.get()?; 105 | } 106 | 107 | if sps.separate_colour_plane_flag { 108 | sps.chroma_format_idc = 0; 109 | } 110 | 111 | sps.width = bs.get_ue()?; 112 | sps.height = bs.get_ue()?; 113 | sps.pic_conformance_flag = bs.get()?; 114 | 115 | if sps.pic_conformance_flag { 116 | sps.conf_win_left_offset = bs.get_ue()?; 117 | sps.conf_win_right_offset = bs.get_ue()?; 118 | sps.conf_win_top_offset = bs.get_ue()?; 119 | sps.conf_win_bottom_offset = bs.get_ue()?; 120 | } 121 | 122 | sps.bit_depth = bs.get_ue()? + 8; 123 | sps.bit_depth_chroma = bs.get_ue()? + 8; 124 | sps.log2_max_poc_lsb = bs.get_ue()? + 4; 125 | sps.sublayer_ordering_info = bs.get()?; 126 | 127 | let start = if sps.sublayer_ordering_info { 128 | 0 129 | } else { 130 | sps.max_sub_layers - 1 131 | }; 132 | 133 | for _ in start..sps.max_sub_layers { 134 | sps.max_dec_pic_buffering.push(bs.get_ue()? + 1); 135 | sps.num_reorder_pics.push(bs.get_ue()?); 136 | 137 | let mut max_latency_increase = bs.get_ue()?; 138 | max_latency_increase = max_latency_increase.saturating_sub(1); 139 | 140 | sps.max_latency_increase.push(max_latency_increase); 141 | } 142 | 143 | sps.log2_min_cb_size = bs.get_ue()? + 3; 144 | sps.log2_diff_max_min_coding_block_size = bs.get_ue()?; 145 | sps.log2_min_tb_size = bs.get_ue()? + 2; 146 | sps.log2_diff_max_min_transform_block_size = bs.get_ue()?; 147 | 148 | sps.max_transform_hierarchy_depth_inter = bs.get_ue()?; 149 | sps.max_transform_hierarchy_depth_intra = bs.get_ue()?; 150 | 151 | sps.scaling_list_enabled_flag = bs.get()?; 152 | 153 | if sps.scaling_list_enabled_flag { 154 | sps.scaling_list_data_present_flag = bs.get()?; 155 | 156 | if sps.scaling_list_data_present_flag { 157 | sps.scaling_list_data = ScalingListData::parse(bs)?; 158 | } 159 | } 160 | 161 | sps.amp_enabled_flag = bs.get()?; 162 | sps.sao_enabled_flag = bs.get()?; 163 | sps.pcm_enabled_flag = bs.get()?; 164 | 165 | if sps.pcm_enabled_flag { 166 | sps.pcm_bit_depth = bs.get_n::(4)? + 1; 167 | sps.pcm_bit_depth_chroma = bs.get_n::(4)? + 1; 168 | sps.pcm_log2_min_pcm_cb_size = bs.get_ue()? + 3; 169 | sps.pcm_log2_max_pcm_cb_size = bs.get_ue()? + sps.pcm_log2_min_pcm_cb_size; 170 | 171 | sps.pcm_loop_filter_disable_flag = bs.get()?; 172 | } 173 | 174 | sps.nb_st_rps = bs.get_ue()?; 175 | 176 | sps.short_term_ref_pic_sets 177 | .resize_with(sps.nb_st_rps as usize, Default::default); 178 | for i in 0..sps.nb_st_rps as usize { 179 | sps.short_term_ref_pic_sets[i] = 180 | ShortTermRPS::parse(bs, &sps, i, sps.nb_st_rps, false)?; 181 | } 182 | 183 | sps.long_term_ref_pics_present_flag = bs.get()?; 184 | 185 | if sps.long_term_ref_pics_present_flag { 186 | sps.num_long_term_ref_pics_sps = bs.get_ue()?; 187 | 188 | for _ in 0..sps.num_long_term_ref_pics_sps { 189 | sps.lt_ref_pic_poc_lsb_sps 190 | .push(bs.get_n(sps.log2_max_poc_lsb as u32)?); 191 | sps.used_by_curr_pic_lt_sps_flag.push(bs.get()?); 192 | } 193 | } 194 | 195 | sps.sps_temporal_mvp_enabled_flag = bs.get()?; 196 | sps.sps_strong_intra_smoothing_enable_flag = bs.get()?; 197 | 198 | sps.vui_present = bs.get()?; 199 | 200 | if sps.vui_present { 201 | sps.vui_parameters = VuiParameters::parse(bs, sps.max_sub_layers)?; 202 | } 203 | 204 | sps.sps_extension_flag = bs.get()?; 205 | 206 | // Computed values 207 | sps.log2_ctb_size = sps.log2_min_cb_size + sps.log2_diff_max_min_coding_block_size; 208 | sps.log2_min_pu_size = sps.log2_min_cb_size - 1; 209 | 210 | sps.ctb_width = (sps.width + (1 << sps.log2_ctb_size) - 1) >> sps.log2_ctb_size; 211 | sps.ctb_height = (sps.height + (1 << sps.log2_ctb_size) - 1) >> sps.log2_ctb_size; 212 | sps.ctb_size = sps.ctb_width * sps.ctb_height; 213 | 214 | sps.min_cb_width = sps.width >> sps.log2_min_cb_size; 215 | sps.min_cb_height = sps.height >> sps.log2_min_cb_size; 216 | sps.min_tb_width = sps.width >> sps.log2_min_tb_size; 217 | sps.min_tb_height = sps.height >> sps.log2_min_tb_size; 218 | sps.min_pu_width = sps.width >> sps.log2_min_pu_size; 219 | sps.min_pu_height = sps.height >> sps.log2_min_pu_size; 220 | sps.tb_mask = (1 << (sps.log2_ctb_size - sps.log2_min_tb_size)) - 1; 221 | 222 | Ok(sps) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/hevc/vps.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use super::BsIoVecReader; 4 | use super::hrd_parameters::HrdParameters; 5 | use super::profile_tier_level::ProfileTierLevel; 6 | 7 | #[allow(clippy::upper_case_acronyms)] 8 | #[derive(Default, Debug, PartialEq, Eq)] 9 | pub struct VPSNAL { 10 | pub(crate) vps_id: u8, 11 | vps_max_layers: u8, 12 | vps_max_sub_layers: u8, 13 | vps_temporal_id_nesting_flag: bool, 14 | ptl: ProfileTierLevel, 15 | vps_sub_layer_ordering_info_present_flag: bool, 16 | vps_max_dec_pic_buffering: Vec, 17 | vps_num_reorder_pics: Vec, 18 | vps_max_latency_increase: Vec, 19 | vps_max_layer_id: u8, 20 | vps_num_layer_sets: u64, 21 | vps_timing_info_present_flag: bool, 22 | vps_num_units_in_tick: u32, 23 | vps_time_scale: u32, 24 | vps_poc_proportional_to_timing_flag: bool, 25 | vps_num_ticks_poc_diff_one: u64, 26 | vps_num_hrd_parameters: u64, 27 | } 28 | 29 | impl VPSNAL { 30 | pub fn parse(bs: &mut BsIoVecReader) -> Result { 31 | let mut vps = VPSNAL { 32 | vps_id: bs.get_n(4)?, 33 | ..Default::default() 34 | }; 35 | 36 | // vps_reserved_three_2bits 37 | assert!(bs.get_n::(2)? == 3); 38 | 39 | vps.vps_max_layers = bs.get_n::(6)? + 1; 40 | vps.vps_max_sub_layers = bs.get_n::(3)? + 1; 41 | vps.vps_temporal_id_nesting_flag = bs.get()?; 42 | 43 | // vps_reserved_ffff_16bits 44 | assert!(bs.get_n::(16)? == 0xFFFF); 45 | 46 | vps.ptl.parse(bs, vps.vps_max_sub_layers)?; 47 | 48 | vps.vps_sub_layer_ordering_info_present_flag = bs.get()?; 49 | 50 | let i = if vps.vps_sub_layer_ordering_info_present_flag { 51 | 0 52 | } else { 53 | vps.vps_max_sub_layers - 1 54 | }; 55 | 56 | for _ in i..vps.vps_max_sub_layers { 57 | vps.vps_max_dec_pic_buffering.push(bs.get_ue()? + 1); 58 | vps.vps_num_reorder_pics.push(bs.get_ue()?); 59 | 60 | let mut vps_max_latency_increase = bs.get_ue()?; 61 | vps_max_latency_increase = vps_max_latency_increase.saturating_sub(1); 62 | 63 | vps.vps_max_latency_increase.push(vps_max_latency_increase); 64 | } 65 | 66 | vps.vps_max_layer_id = bs.get_n(6)?; 67 | vps.vps_num_layer_sets = bs.get_ue()? + 1; 68 | 69 | for _ in 1..vps.vps_num_layer_sets { 70 | for _ in 0..=vps.vps_max_layer_id { 71 | bs.skip_n(1)?; // layer_id_included_flag[i][j] 72 | } 73 | } 74 | 75 | vps.vps_timing_info_present_flag = bs.get()?; 76 | 77 | if vps.vps_timing_info_present_flag { 78 | vps.vps_num_units_in_tick = bs.get_n(32)?; 79 | vps.vps_time_scale = bs.get_n(32)?; 80 | vps.vps_poc_proportional_to_timing_flag = bs.get()?; 81 | 82 | if vps.vps_poc_proportional_to_timing_flag { 83 | vps.vps_num_ticks_poc_diff_one = bs.get_ue()? + 1; 84 | } 85 | 86 | vps.vps_num_hrd_parameters = bs.get_ue()?; 87 | 88 | for i in 0..vps.vps_num_hrd_parameters { 89 | let mut common_inf_present = false; 90 | bs.get_ue()?; // hrd_layer_set_idx 91 | 92 | if i > 0 { 93 | common_inf_present = bs.get()?; 94 | } 95 | 96 | HrdParameters::parse(bs, common_inf_present, vps.vps_max_sub_layers)?; 97 | } 98 | } 99 | 100 | bs.skip_n(1)?; // vps_extension_flag 101 | 102 | Ok(vps) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/hevc/vui_parameters.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use super::BsIoVecReader; 4 | use super::hrd_parameters::HrdParameters; 5 | 6 | #[derive(Default, Debug, PartialEq, Clone, Eq)] 7 | pub struct VuiParameters { 8 | sar_present: bool, 9 | sar_idx: u8, 10 | sar_num: u16, 11 | sar_den: u16, 12 | overscan_info_present_flag: bool, 13 | overscan_appropriate_flag: bool, 14 | video_signal_type_present_flag: bool, 15 | 16 | video_format: u8, 17 | video_full_range_flag: bool, 18 | colour_description_present_flag: bool, 19 | colour_primaries: u8, 20 | transfer_characteristic: u8, 21 | matrix_coeffs: u8, 22 | 23 | chroma_loc_info_present_flag: bool, 24 | chroma_sample_loc_type_top_field: u64, 25 | chroma_sample_loc_type_bottom_field: u64, 26 | neutral_chroma_indication_flag: bool, 27 | field_seq_flag: bool, 28 | frame_field_info_present_flag: bool, 29 | 30 | default_display_window_flag: bool, 31 | def_disp_win_left_offset: u64, 32 | def_disp_win_right_offset: u64, 33 | def_disp_win_top_offset: u64, 34 | def_disp_win_bottom_offset: u64, 35 | 36 | vui_timing_info_present_flag: bool, 37 | vui_num_units_in_tick: u32, 38 | vui_time_scale: u32, 39 | vui_poc_proportional_to_timing_flag: bool, 40 | vui_num_ticks_poc_diff_one_minus1: u64, 41 | vui_hrd_parameters_present_flag: bool, 42 | 43 | bitstream_restriction_flag: bool, 44 | tiles_fixed_structure_flag: bool, 45 | motion_vectors_over_pic_boundaries_flag: bool, 46 | restricted_ref_pic_lists_flag: bool, 47 | 48 | min_spatial_segmentation_idc: u64, 49 | max_bytes_per_pic_denom: u64, 50 | max_bits_per_min_cu_denom: u64, 51 | log2_max_mv_length_horizontal: u64, 52 | log2_max_mv_length_vertical: u64, 53 | } 54 | 55 | impl VuiParameters { 56 | pub fn parse(bs: &mut BsIoVecReader, max_sub_layers: u8) -> Result { 57 | let mut vui = VuiParameters { 58 | sar_present: bs.get()?, 59 | ..Default::default() 60 | }; 61 | 62 | if vui.sar_present { 63 | vui.sar_idx = bs.get_n(8)?; 64 | 65 | if vui.sar_idx == 255 { 66 | vui.sar_num = bs.get_n(16)?; 67 | vui.sar_den = bs.get_n(16)?; 68 | } 69 | } 70 | 71 | vui.overscan_info_present_flag = bs.get()?; 72 | if vui.overscan_info_present_flag { 73 | vui.overscan_appropriate_flag = bs.get()?; 74 | } 75 | 76 | vui.video_signal_type_present_flag = bs.get()?; 77 | if vui.video_signal_type_present_flag { 78 | vui.video_format = bs.get_n(3)?; 79 | vui.video_full_range_flag = bs.get()?; 80 | vui.colour_description_present_flag = bs.get()?; 81 | 82 | if vui.colour_description_present_flag { 83 | vui.colour_primaries = bs.get_n(8)?; 84 | vui.transfer_characteristic = bs.get_n(8)?; 85 | vui.matrix_coeffs = bs.get_n(8)?; 86 | } 87 | } 88 | 89 | vui.chroma_loc_info_present_flag = bs.get()?; 90 | if vui.chroma_loc_info_present_flag { 91 | vui.chroma_sample_loc_type_top_field = bs.get_ue()?; 92 | vui.chroma_sample_loc_type_bottom_field = bs.get_ue()?; 93 | } 94 | 95 | vui.neutral_chroma_indication_flag = bs.get()?; 96 | vui.field_seq_flag = bs.get()?; 97 | vui.frame_field_info_present_flag = bs.get()?; 98 | vui.default_display_window_flag = bs.get()?; 99 | 100 | if vui.default_display_window_flag { 101 | vui.def_disp_win_left_offset = bs.get_ue()?; 102 | vui.def_disp_win_right_offset = bs.get_ue()?; 103 | vui.def_disp_win_top_offset = bs.get_ue()?; 104 | vui.def_disp_win_bottom_offset = bs.get_ue()?; 105 | } 106 | 107 | vui.vui_timing_info_present_flag = bs.get()?; 108 | if vui.vui_timing_info_present_flag { 109 | vui.vui_num_units_in_tick = bs.get_n(32)?; 110 | vui.vui_time_scale = bs.get_n(32)?; 111 | 112 | vui.vui_poc_proportional_to_timing_flag = bs.get()?; 113 | if vui.vui_poc_proportional_to_timing_flag { 114 | vui.vui_num_ticks_poc_diff_one_minus1 = bs.get_ue()?; 115 | } 116 | 117 | vui.vui_hrd_parameters_present_flag = bs.get()?; 118 | if vui.vui_hrd_parameters_present_flag { 119 | HrdParameters::parse(bs, true, max_sub_layers)?; 120 | } 121 | } 122 | 123 | vui.bitstream_restriction_flag = bs.get()?; 124 | if vui.bitstream_restriction_flag { 125 | vui.tiles_fixed_structure_flag = bs.get()?; 126 | vui.motion_vectors_over_pic_boundaries_flag = bs.get()?; 127 | vui.restricted_ref_pic_lists_flag = bs.get()?; 128 | 129 | vui.min_spatial_segmentation_idc = bs.get_ue()?; 130 | vui.max_bytes_per_pic_denom = bs.get_ue()?; 131 | vui.max_bits_per_min_cu_denom = bs.get_ue()?; 132 | vui.log2_max_mv_length_horizontal = bs.get_ue()?; 133 | vui.log2_max_mv_length_vertical = bs.get_ue()?; 134 | } 135 | 136 | Ok(vui) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/io/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::Write, 3 | path::{Path, PathBuf}, 4 | }; 5 | 6 | use anyhow::{Result, bail, format_err}; 7 | use regex::Regex; 8 | 9 | pub mod processor; 10 | 11 | use super::{HevcParser, NALUStartCode, NALUnit, hevc::*}; 12 | 13 | pub const FOUR_SIZED_NALU_TYPES: &[u8] = &[NAL_VPS, NAL_SPS, NAL_PPS, NAL_AUD, NAL_UNSPEC62]; 14 | 15 | #[derive(Debug, PartialEq, Clone, Eq)] 16 | pub enum IoFormat { 17 | Raw, 18 | RawStdin, 19 | Matroska, 20 | } 21 | 22 | #[derive(Debug, Clone, Copy)] 23 | pub enum StartCodePreset { 24 | Four, 25 | AnnexB, 26 | } 27 | 28 | pub trait IoProcessor { 29 | /// Input path 30 | fn input(&self) -> &PathBuf; 31 | /// If the processor has a progress bar, this updates every megabyte read 32 | fn update_progress(&mut self, delta: u64); 33 | 34 | /// NALU processing callback 35 | /// This is called after reading a 100kB chunk of the file 36 | /// The resulting NALs are always complete and unique 37 | /// 38 | /// The data can be access through `chunk`, using the NAL start/end indices 39 | fn process_nals(&mut self, parser: &HevcParser, nals: &[NALUnit], chunk: &[u8]) -> Result<()>; 40 | 41 | /// Finalize callback, when the stream is done being read 42 | /// Called at the end of `HevcProcessor::process_io` 43 | fn finalize(&mut self, parser: &HevcParser) -> Result<()>; 44 | } 45 | 46 | /// Data for a frame, with its decoded index 47 | #[derive(Debug, Clone)] 48 | pub struct FrameBuffer { 49 | pub frame_number: u64, 50 | pub nals: Vec, 51 | } 52 | 53 | /// Data for a NALU, with type 54 | /// The data does not include the start code 55 | #[derive(Debug, Clone)] 56 | pub struct NalBuffer { 57 | pub nal_type: u8, 58 | pub start_code: NALUStartCode, 59 | pub data: Vec, 60 | } 61 | 62 | pub fn format_from_path(input: &Path) -> Result { 63 | let regex = Regex::new(r"\.(hevc|.?265|mkv)")?; 64 | let file_name = match input.file_name() { 65 | Some(file_name) => file_name 66 | .to_str() 67 | .ok_or_else(|| format_err!("Invalid file name"))?, 68 | None => "", 69 | }; 70 | 71 | if file_name == "-" { 72 | Ok(IoFormat::RawStdin) 73 | } else if regex.is_match(file_name) && input.is_file() { 74 | if file_name.ends_with(".mkv") { 75 | Ok(IoFormat::Matroska) 76 | } else { 77 | Ok(IoFormat::Raw) 78 | } 79 | } else if file_name.is_empty() { 80 | bail!("Missing input.") 81 | } else if !input.is_file() { 82 | bail!("Input file doesn't exist.") 83 | } else { 84 | bail!("Invalid input file type.") 85 | } 86 | } 87 | 88 | impl std::fmt::Display for IoFormat { 89 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 90 | match *self { 91 | IoFormat::Matroska => write!(f, "Matroska file"), 92 | IoFormat::Raw => write!(f, "HEVC file"), 93 | IoFormat::RawStdin => write!(f, "HEVC pipe"), 94 | } 95 | } 96 | } 97 | 98 | impl NALUnit { 99 | pub fn write_with_preset( 100 | writer: &mut dyn Write, 101 | data: &[u8], 102 | preset: StartCodePreset, 103 | nal_type: u8, 104 | first_nal: bool, 105 | ) -> Result<()> { 106 | let start_code = match preset { 107 | StartCodePreset::Four => NALUStartCode::Length4, 108 | StartCodePreset::AnnexB => { 109 | if FOUR_SIZED_NALU_TYPES.contains(&nal_type) || first_nal { 110 | NALUStartCode::Length4 111 | } else { 112 | NALUStartCode::Length3 113 | } 114 | } 115 | }; 116 | 117 | writer.write_all(start_code.slice())?; 118 | writer.write_all(data)?; 119 | 120 | Ok(()) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/io/processor.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{BufRead, BufReader, Read}, 4 | path::Path, 5 | }; 6 | 7 | use anyhow::{Result, bail, ensure}; 8 | use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; 9 | use matroska_demuxer::{MatroskaFile, TrackType}; 10 | 11 | use crate::{MAX_PARSE_SIZE, NALUStartCode, config::HEVCDecoderConfigurationRecord, hevc::NALUnit}; 12 | 13 | use super::{HevcParser, IoFormat, IoProcessor}; 14 | 15 | /// Base HEVC stream processor 16 | pub struct HevcProcessor { 17 | opts: HevcProcessorOpts, 18 | 19 | format: IoFormat, 20 | parser: HevcParser, 21 | 22 | chunk_size: usize, 23 | 24 | main_buf: Vec, 25 | sec_buf: Vec, 26 | consumed: usize, 27 | 28 | chunk: Vec, 29 | end: Vec, 30 | offsets: Vec, 31 | 32 | last_buffered_frame: u64, 33 | } 34 | 35 | /// Options for the processor 36 | pub struct HevcProcessorOpts { 37 | /// Buffer a frame when using `parse_nalus`. 38 | /// This stops the stream reading as soon as a full frame has been parsed. 39 | pub buffer_frame: bool, 40 | /// Parse the NALs, required for `buffer_frame` 41 | /// Provides frame presentation order 42 | pub parse_nals: bool, 43 | 44 | /// Stop reading the stream after N frames 45 | /// The number of processed frames may differ. 46 | pub limit: Option, 47 | } 48 | 49 | impl HevcProcessor { 50 | /// Initialize a HEVC stream processor 51 | pub fn new(format: IoFormat, opts: HevcProcessorOpts, chunk_size: usize) -> Self { 52 | let sec_buf = if format == IoFormat::RawStdin { 53 | vec![0; 50_000] 54 | } else { 55 | Vec::new() 56 | }; 57 | 58 | Self { 59 | opts, 60 | format, 61 | parser: HevcParser::default(), 62 | 63 | chunk_size, 64 | main_buf: vec![0; chunk_size], 65 | sec_buf, 66 | consumed: 0, 67 | 68 | chunk: Vec::with_capacity(chunk_size), 69 | end: Vec::with_capacity(chunk_size), 70 | offsets: Vec::with_capacity(2048), 71 | 72 | last_buffered_frame: 0, 73 | } 74 | } 75 | 76 | /// Fully parse the input stream 77 | pub fn process_io( 78 | &mut self, 79 | reader: &mut dyn Read, 80 | processor: &mut dyn IoProcessor, 81 | ) -> Result<()> { 82 | self.parse_nalus(reader, processor)?; 83 | 84 | self.parser.finish(); 85 | 86 | processor.finalize(&self.parser)?; 87 | 88 | Ok(()) 89 | } 90 | 91 | /// Fully parse a file or input stream. 92 | /// If `file_path` is `None`, the format must be `RawStdin`. 93 | pub fn process_file>( 94 | &mut self, 95 | processor: &mut dyn IoProcessor, 96 | file_path: Option

, 97 | ) -> Result<()> { 98 | if let Some(input) = file_path { 99 | let file = File::open(input)?; 100 | 101 | match self.format { 102 | IoFormat::Matroska => self.process_matroska_file(processor, file), 103 | IoFormat::Raw => { 104 | let mut reader = Box::new(BufReader::with_capacity(100_000, file)); 105 | self.process_io(&mut reader, processor) 106 | } 107 | _ => unreachable!(), 108 | } 109 | } else if let IoFormat::RawStdin = self.format { 110 | let stdin = std::io::stdin(); 111 | let mut reader = Box::new(stdin.lock()) as Box; 112 | 113 | self.process_io(&mut reader, processor) 114 | } else { 115 | bail!("Invalid params"); 116 | } 117 | } 118 | 119 | /// Parse NALUs from the stream 120 | /// Depending on the options, this either: 121 | /// - Loops the entire stream until EOF 122 | /// - Loops until a complete frame has been parsed 123 | /// 124 | /// In both cases, the processor callback is called when a NALU payload is ready. 125 | pub fn parse_nalus( 126 | &mut self, 127 | reader: &mut dyn Read, 128 | processor: &mut dyn IoProcessor, 129 | ) -> Result<()> { 130 | while let Ok(n) = reader.read(&mut self.main_buf) { 131 | let mut read_bytes = n; 132 | if read_bytes == 0 && self.end.is_empty() && self.chunk.is_empty() { 133 | break; 134 | } 135 | 136 | if self.format == IoFormat::RawStdin { 137 | self.chunk.extend_from_slice(&self.main_buf[..read_bytes]); 138 | 139 | loop { 140 | let num = reader.read(&mut self.sec_buf)?; 141 | if num > 0 { 142 | read_bytes += num; 143 | 144 | self.chunk.extend_from_slice(&self.sec_buf[..num]); 145 | 146 | if read_bytes >= self.chunk_size { 147 | break; 148 | } 149 | } else { 150 | break; 151 | } 152 | } 153 | } else if read_bytes < self.chunk_size { 154 | self.chunk.extend_from_slice(&self.main_buf[..read_bytes]); 155 | } else { 156 | self.chunk.extend_from_slice(&self.main_buf); 157 | } 158 | 159 | self.parser.get_offsets(&self.chunk, &mut self.offsets); 160 | 161 | if self.offsets.is_empty() { 162 | if read_bytes == 0 { 163 | break; 164 | } 165 | continue; 166 | } 167 | 168 | let last = if read_bytes < self.chunk_size { 169 | *self.offsets.last().unwrap() 170 | } else { 171 | let last = self.offsets.pop().unwrap(); 172 | 173 | self.end.clear(); 174 | self.end.extend_from_slice(&self.chunk[last..]); 175 | 176 | last 177 | }; 178 | 179 | let nals = 180 | self.parser 181 | .split_nals(&self.chunk, &self.offsets, last, self.opts.parse_nals)?; 182 | 183 | // Process NALUs 184 | processor.process_nals(&self.parser, &nals, &self.chunk)?; 185 | 186 | self.chunk.clear(); 187 | 188 | if !self.end.is_empty() { 189 | self.chunk.extend_from_slice(&self.end); 190 | self.end.clear() 191 | } 192 | 193 | self.consumed += read_bytes; 194 | 195 | if self.consumed >= 100_000_000 { 196 | processor.update_progress(1); 197 | self.consumed = 0; 198 | } 199 | 200 | let check_current_frame_idx = self.opts.buffer_frame || self.opts.limit.is_some(); 201 | if check_current_frame_idx { 202 | let max_frame_idx = nals.iter().map(|nal| nal.decoded_frame_index).max(); 203 | 204 | if let Some(frame_idx) = max_frame_idx { 205 | if self.opts.limit.is_some_and(|limit| frame_idx > limit) { 206 | // Must be higher than limit, to make sure that the AU was fully read 207 | break; 208 | } 209 | 210 | if self.opts.buffer_frame && frame_idx > self.last_buffered_frame { 211 | self.last_buffered_frame = frame_idx; 212 | break; 213 | } 214 | } 215 | } 216 | } 217 | 218 | Ok(()) 219 | } 220 | 221 | fn process_matroska_file(&mut self, processor: &mut dyn IoProcessor, file: File) -> Result<()> { 222 | let mut mkv = MatroskaFile::open(file)?; 223 | let track = mkv 224 | .tracks() 225 | .iter() 226 | .find(|t| t.track_type() == TrackType::Video && t.codec_id() == "V_MPEGH/ISO/HEVC"); 227 | 228 | ensure!(track.is_some(), "No HEVC video track found in file"); 229 | 230 | let track = track.unwrap(); 231 | let track_id = track.track_number().get(); 232 | 233 | let config = if let Some(codec_private) = track.codec_private() { 234 | let mut bs = BsIoSliceReader::from_slice(codec_private); 235 | HEVCDecoderConfigurationRecord::parse(&mut bs)? 236 | } else { 237 | bail!("Missing HEVC codec private data"); 238 | }; 239 | 240 | let nalu_size_length = (config.length_size_minus_one as usize) + 1; 241 | let mut frame = matroska_demuxer::Frame::default(); 242 | 243 | let mut frame_idx = 0; 244 | 245 | let mut frame_nals = Vec::with_capacity(16); 246 | 247 | while let Ok(res) = mkv.next_frame(&mut frame) { 248 | if !res { 249 | break; 250 | } else if frame.track != track_id { 251 | continue; 252 | } 253 | 254 | if self.opts.limit.is_some_and(|limit| frame_idx >= limit) { 255 | // last frame was already processed so we can break 256 | break; 257 | } 258 | 259 | frame_idx += 1; 260 | 261 | let data = frame.data.as_slice(); 262 | let mut pos = 0; 263 | let end = data.len() - 1; 264 | 265 | // Not exactly going to be accurate since only frame data is considered 266 | self.consumed += data.len(); 267 | 268 | while (pos + nalu_size_length) <= end { 269 | let nalu_size = 270 | u32::from_be_bytes(data[pos..pos + nalu_size_length].try_into()?) as usize; 271 | 272 | if nalu_size == 0 { 273 | continue; 274 | } else if (pos + nalu_size) > end { 275 | break; 276 | } 277 | 278 | pos += nalu_size_length; 279 | 280 | let end = pos + nalu_size; 281 | let parsing_end = if nalu_size > MAX_PARSE_SIZE { 282 | pos + MAX_PARSE_SIZE 283 | } else { 284 | end 285 | }; 286 | 287 | let buf = &data[pos..parsing_end]; 288 | 289 | let nal = NALUnit { 290 | start: pos, 291 | end, 292 | decoded_frame_index: self.parser.decoded_index, 293 | start_code: NALUStartCode::Length4, 294 | ..Default::default() 295 | }; 296 | 297 | let nal = 298 | self.parser 299 | .handle_nal_without_start_code(buf, nal, self.opts.parse_nals)?; 300 | frame_nals.push(nal); 301 | 302 | pos += nalu_size; 303 | } 304 | 305 | processor.process_nals(&self.parser, &frame_nals, data)?; 306 | frame_nals.clear(); 307 | 308 | if self.consumed >= 100_000_000 { 309 | processor.update_progress(1); 310 | self.consumed = 0; 311 | } 312 | } 313 | 314 | self.parser.finish(); 315 | 316 | processor.finalize(&self.parser)?; 317 | 318 | Ok(()) 319 | } 320 | } 321 | 322 | impl Default for HevcProcessorOpts { 323 | fn default() -> Self { 324 | Self { 325 | buffer_frame: false, 326 | parse_nals: true, 327 | limit: Default::default(), 328 | } 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use nom::{IResult, bytes::complete::take_until}; 3 | 4 | use bitvec_helpers::bitstream_io_reader::BsIoVecReader; 5 | 6 | pub mod hevc; 7 | pub mod utils; 8 | 9 | #[cfg(feature = "hevc_io")] 10 | pub mod io; 11 | 12 | use hevc::*; 13 | use pps::PPSNAL; 14 | use slice::SliceNAL; 15 | use sps::SPSNAL; 16 | use vps::VPSNAL; 17 | 18 | use utils::clear_start_code_emulation_prevention_3_byte; 19 | 20 | // We don't want to parse large slices because the memory is copied 21 | const MAX_PARSE_SIZE: usize = 2048; 22 | 23 | const HEADER_LEN_3: usize = 3; 24 | const HEADER_LEN_4: usize = 4; 25 | const NAL_START_CODE_3: &[u8] = &[0, 0, 1]; 26 | const NAL_START_CODE_4: &[u8] = &[0, 0, 0, 1]; 27 | 28 | #[derive(Debug, Default, Copy, Clone)] 29 | pub enum NALUStartCode { 30 | #[default] 31 | Length3, 32 | Length4, 33 | } 34 | 35 | #[derive(Default)] 36 | pub struct HevcParser { 37 | reader: BsIoVecReader, 38 | pub nalu_start_code: NALUStartCode, 39 | 40 | nals: Vec, 41 | vps: Vec, 42 | sps: Vec, 43 | pps: Vec, 44 | ordered_frames: Vec, 45 | frames: Vec, 46 | 47 | poc: u64, 48 | poc_tid0: u64, 49 | 50 | current_frame: Frame, 51 | decoded_index: u64, 52 | presentation_index: u64, 53 | } 54 | 55 | impl HevcParser { 56 | fn take_until_nal<'a>(tag: &[u8], data: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> { 57 | take_until(tag)(data) 58 | } 59 | 60 | pub fn with_nalu_start_code(start_code: NALUStartCode) -> HevcParser { 61 | HevcParser { 62 | nalu_start_code: start_code, 63 | ..Default::default() 64 | } 65 | } 66 | 67 | pub fn get_offsets(&mut self, data: &[u8], offsets: &mut Vec) { 68 | offsets.clear(); 69 | 70 | let mut consumed = 0; 71 | 72 | let nal_start_tag = self.nalu_start_code.slice(); 73 | 74 | loop { 75 | match Self::take_until_nal(nal_start_tag, &data[consumed..]) { 76 | Ok(nal) => { 77 | // Byte count before the NAL is the offset 78 | consumed += nal.1.len(); 79 | 80 | offsets.push(consumed); 81 | 82 | // nom consumes the tag, so add it back 83 | consumed += self.nalu_start_code.size(); 84 | } 85 | _ => return, 86 | } 87 | } 88 | } 89 | 90 | pub fn split_nals( 91 | &mut self, 92 | data: &[u8], 93 | offsets: &[usize], 94 | last: usize, 95 | parse_nals: bool, 96 | ) -> Result> { 97 | let count = offsets.len(); 98 | 99 | let mut nals = Vec::with_capacity(count); 100 | 101 | for (index, offset) in offsets.iter().enumerate() { 102 | let size = if offset == &last { 103 | data.len() - offset 104 | } else { 105 | let size = if index == count - 1 { 106 | last - offset 107 | } else { 108 | offsets[index + 1] - offset 109 | }; 110 | 111 | match &data[offset + size - 1..offset + size + 3] { 112 | [0, 0, 0, 1] => size - 1, 113 | _ => size, 114 | } 115 | }; 116 | 117 | let nal = self.parse_nal(data, *offset, size, parse_nals)?; 118 | 119 | nals.push(nal); 120 | } 121 | 122 | Ok(nals) 123 | } 124 | 125 | fn parse_nal( 126 | &mut self, 127 | data: &[u8], 128 | offset: usize, 129 | size: usize, 130 | parse_nal: bool, 131 | ) -> Result { 132 | let mut nal = NALUnit::default(); 133 | 134 | // Assuming [0, 0, 1] header 135 | // Offset is at first element 136 | let pos = offset + HEADER_LEN_3; 137 | let end = offset + size; 138 | 139 | let parsing_end = if size > MAX_PARSE_SIZE { 140 | offset + MAX_PARSE_SIZE 141 | } else { 142 | end 143 | }; 144 | 145 | nal.start = pos; 146 | nal.end = end; 147 | nal.decoded_frame_index = self.decoded_index; 148 | 149 | nal.start_code = if offset > 0 { 150 | // Previous byte is 0, offset..offset + 3 is [0, 0, 1] 151 | // Actual start code is length 4 152 | if data[offset - 1] == 0 { 153 | NALUStartCode::Length4 154 | } else { 155 | NALUStartCode::Length3 156 | } 157 | } else { 158 | NALUStartCode::Length3 159 | }; 160 | 161 | #[allow(deprecated)] 162 | { 163 | nal.start_code_len = nal.start_code.size() as u8; 164 | } 165 | 166 | let buf = &data[pos..parsing_end]; 167 | self.handle_nal_without_start_code(buf, nal, parse_nal) 168 | } 169 | 170 | fn parse_nal_header(&mut self, nal: &mut NALUnit) -> Result<()> { 171 | // forbidden_zero_bit 172 | self.reader.get()?; 173 | 174 | nal.nal_type = self.reader.get_n(6)?; 175 | 176 | if self.reader.available()? < 9 && matches!(nal.nal_type, NAL_EOS_NUT | NAL_EOB_NUT) { 177 | } else { 178 | nal.nuh_layer_id = self.reader.get_n(6)?; 179 | nal.temporal_id = self.reader.get_n::(3)? - 1; 180 | } 181 | 182 | Ok(()) 183 | } 184 | 185 | fn handle_nal_without_start_code( 186 | &mut self, 187 | data: &[u8], 188 | mut nal: NALUnit, 189 | parse_nal: bool, 190 | ) -> Result { 191 | if parse_nal { 192 | let bytes = clear_start_code_emulation_prevention_3_byte(data); 193 | self.reader.replace_vec(bytes); 194 | 195 | self.parse_nal_header(&mut nal)?; 196 | } else { 197 | nal.nal_type = data[0] >> 1; 198 | } 199 | 200 | if nal.nuh_layer_id > 0 { 201 | return Ok(nal); 202 | } 203 | 204 | if parse_nal { 205 | self.parse_nal_internal(&mut nal)?; 206 | self.nals.push(nal.clone()); 207 | } 208 | 209 | Ok(nal) 210 | } 211 | 212 | fn parse_nal_internal(&mut self, nal: &mut NALUnit) -> Result<()> { 213 | match nal.nal_type { 214 | NAL_VPS => self.parse_vps()?, 215 | NAL_SPS => self.parse_sps()?, 216 | NAL_PPS => self.parse_pps()?, 217 | 218 | NAL_TRAIL_R | NAL_TRAIL_N | NAL_TSA_N | NAL_TSA_R | NAL_STSA_N | NAL_STSA_R 219 | | NAL_BLA_W_LP | NAL_BLA_W_RADL | NAL_BLA_N_LP | NAL_IDR_W_RADL | NAL_IDR_N_LP 220 | | NAL_CRA_NUT | NAL_RADL_N | NAL_RADL_R | NAL_RASL_N | NAL_RASL_R => { 221 | self.parse_slice(nal)?; 222 | 223 | self.current_frame.nals.push(nal.clone()); 224 | } 225 | NAL_SEI_SUFFIX | NAL_UNSPEC62 | NAL_UNSPEC63 | NAL_EOS_NUT | NAL_EOB_NUT 226 | | NAL_FD_NUT => { 227 | // Dolby NALs are suffixed to the slices 228 | // And EOS, EOB, FD should be contained within the current AU 229 | self.current_frame.nals.push(nal.clone()); 230 | } 231 | _ => { 232 | self.add_current_frame(); 233 | 234 | nal.decoded_frame_index = self.decoded_index; 235 | self.current_frame.nals.push(nal.clone()); 236 | } 237 | }; 238 | 239 | // Parameter sets also mean a new frame 240 | match nal.nal_type { 241 | NAL_VPS | NAL_SPS | NAL_PPS => { 242 | self.add_current_frame(); 243 | 244 | nal.decoded_frame_index = self.decoded_index; 245 | self.current_frame.nals.push(nal.clone()); 246 | } 247 | _ => (), 248 | }; 249 | 250 | Ok(()) 251 | } 252 | 253 | fn parse_vps(&mut self) -> Result<()> { 254 | let vps = VPSNAL::parse(&mut self.reader)?; 255 | 256 | self.remove_vps(&vps); 257 | 258 | self.vps.push(vps); 259 | 260 | Ok(()) 261 | } 262 | 263 | fn parse_sps(&mut self) -> Result<()> { 264 | let sps = SPSNAL::parse(&mut self.reader)?; 265 | self.remove_sps(&sps); 266 | 267 | self.sps.push(sps); 268 | 269 | Ok(()) 270 | } 271 | 272 | fn parse_pps(&mut self) -> Result<()> { 273 | let pps = PPSNAL::parse(&mut self.reader)?; 274 | 275 | self.remove_pps(&pps); 276 | 277 | self.pps.push(pps); 278 | 279 | Ok(()) 280 | } 281 | 282 | fn parse_slice(&mut self, nal: &mut NALUnit) -> Result<()> { 283 | let slice = SliceNAL::parse( 284 | &mut self.reader, 285 | &self.sps, 286 | &self.pps, 287 | nal, 288 | &mut self.poc_tid0, 289 | &mut self.poc, 290 | )?; 291 | 292 | // Consecutive slice NALs cases 293 | if self.current_frame.first_slice.first_slice_in_pic_flag && slice.first_slice_in_pic_flag { 294 | nal.decoded_frame_index = self.decoded_index + 1; 295 | self.add_current_frame(); 296 | } 297 | 298 | if slice.key_frame { 299 | self.reorder_frames(); 300 | } 301 | 302 | if slice.first_slice_in_pic_flag { 303 | self.current_frame.first_slice = slice; 304 | 305 | self.current_frame.decoded_number = self.decoded_index; 306 | } 307 | 308 | Ok(()) 309 | } 310 | 311 | fn remove_vps(&mut self, vps: &VPSNAL) { 312 | let id = vps.vps_id as usize; 313 | 314 | if let Some(existing_vps) = self.vps.get(id) { 315 | if existing_vps == vps { 316 | self.vps.remove(id); 317 | 318 | let sps_to_remove: Vec = self 319 | .sps 320 | .clone() 321 | .into_iter() 322 | .filter(|sps| sps.vps_id == vps.vps_id) 323 | .collect(); 324 | 325 | sps_to_remove.iter().for_each(|sps| self.remove_sps(sps)); 326 | } 327 | } 328 | } 329 | 330 | fn remove_sps(&mut self, sps: &SPSNAL) { 331 | let id = sps.sps_id as usize; 332 | 333 | if let Some(existing_sps) = self.sps.get(id) { 334 | if existing_sps == sps { 335 | self.sps.remove(id); 336 | 337 | // Remove all dependent pps 338 | self.pps.retain(|pps| pps.sps_id != sps.sps_id); 339 | } 340 | } 341 | } 342 | 343 | fn remove_pps(&mut self, pps: &PPSNAL) { 344 | // Remove if same id 345 | if let Some(existing_pps) = self.pps.get(pps.pps_id as usize) { 346 | if existing_pps == pps { 347 | self.pps.remove(pps.pps_id as usize); 348 | } 349 | } 350 | } 351 | 352 | // If we're here, the last slice of a frame was found already 353 | fn add_current_frame(&mut self) { 354 | if self.current_frame.first_slice.first_slice_in_pic_flag { 355 | self.decoded_index += 1; 356 | 357 | self.current_frame.presentation_number = 358 | self.current_frame.first_slice.output_picture_number; 359 | 360 | self.current_frame.frame_type = self.current_frame.first_slice.slice_type; 361 | 362 | self.frames.push(self.current_frame.clone()); 363 | 364 | self.current_frame = Frame::default(); 365 | } 366 | } 367 | 368 | fn reorder_frames(&mut self) { 369 | let mut offset = self.presentation_index; 370 | 371 | self.frames.sort_by_key(|f| f.presentation_number); 372 | self.frames.iter_mut().for_each(|f| { 373 | f.presentation_number = offset; 374 | offset += 1; 375 | }); 376 | 377 | self.presentation_index = offset; 378 | self.ordered_frames.extend_from_slice(&self.frames); 379 | self.frames.clear(); 380 | } 381 | 382 | pub fn display(&self) { 383 | println!("{} frames", &self.ordered_frames.len()); 384 | for frame in &self.ordered_frames { 385 | let pict_type = match frame.frame_type { 386 | 2 => "I", 387 | 1 => "P", 388 | 0 => "B", 389 | _ => "", 390 | }; 391 | 392 | println!( 393 | "{} display order {} poc {} pos {}", 394 | pict_type, 395 | frame.presentation_number, 396 | frame.first_slice.output_picture_number, 397 | frame.decoded_number 398 | ); 399 | } 400 | } 401 | 402 | pub fn finish(&mut self) { 403 | self.add_current_frame(); 404 | self.reorder_frames(); 405 | } 406 | 407 | /// Processed frames in the current GOP 408 | /// Cleared every key frame 409 | pub fn processed_frames(&self) -> &Vec { 410 | &self.frames 411 | } 412 | 413 | pub fn ordered_frames(&self) -> &Vec { 414 | &self.ordered_frames 415 | } 416 | 417 | pub fn get_nals(&self) -> &Vec { 418 | &self.nals 419 | } 420 | } 421 | 422 | impl NALUStartCode { 423 | pub const fn slice(&self) -> &[u8] { 424 | match self { 425 | NALUStartCode::Length3 => NAL_START_CODE_3, 426 | NALUStartCode::Length4 => NAL_START_CODE_4, 427 | } 428 | } 429 | 430 | pub const fn size(&self) -> usize { 431 | match self { 432 | NALUStartCode::Length3 => HEADER_LEN_3, 433 | NALUStartCode::Length4 => HEADER_LEN_4, 434 | } 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Result, anyhow}; 2 | use bitvec_helpers::bitstream_io_writer::BitstreamIoWriter; 3 | 4 | use super::{Frame, NAL_AUD, NALUStartCode}; 5 | 6 | pub fn clear_start_code_emulation_prevention_3_byte(data: &[u8]) -> Vec { 7 | let len = data.len(); 8 | 9 | if len > 2 { 10 | let mut unescaped_bytes: Vec = Vec::with_capacity(len); 11 | unescaped_bytes.push(data[0]); 12 | unescaped_bytes.push(data[1]); 13 | 14 | for i in 2..len { 15 | if !(data[i - 2] == 0 && data[i - 1] == 0 && data[i] == 3) { 16 | unescaped_bytes.push(data[i]); 17 | } 18 | } 19 | 20 | unescaped_bytes 21 | } else { 22 | data.to_owned() 23 | } 24 | } 25 | 26 | /// Within the NAL unit, the following three-byte sequences shall not occur at any byte-aligned position: 27 | /// - 0x000000 28 | /// - 0x000001 29 | /// - 0x000002 30 | pub fn add_start_code_emulation_prevention_3_byte(data: &mut Vec) { 31 | let mut count = data.len(); 32 | let mut i = 0; 33 | 34 | while i < count { 35 | if i > 2 && data[i - 2] == 0 && data[i - 1] == 0 && data[i] <= 3 { 36 | data.insert(i, 3); 37 | count += 1; 38 | } 39 | 40 | i += 1; 41 | } 42 | } 43 | 44 | pub fn aud_for_frame(frame: &Frame, start_code: Option) -> Result> { 45 | let pic_type: u8 = match &frame.frame_type { 46 | 2 => 0, // I 47 | 1 => 1, // P, I 48 | 0 => 2, // B, P, I 49 | _ => 7, 50 | }; 51 | 52 | let mut data = if let Some(sc) = start_code { 53 | sc.slice().to_vec() 54 | } else { 55 | Vec::new() 56 | }; 57 | 58 | let mut writer = BitstreamIoWriter::with_capacity(24); 59 | 60 | writer.write(false)?; // forbidden_zero_bit 61 | 62 | writer.write_n(&NAL_AUD, 6)?; // nal_unit_type 63 | writer.write_n(&0_u8, 6)?; // nuh_layer_id 64 | writer.write_n(&1_u8, 3)?; // nuh_temporal_id_plus1 65 | 66 | writer.write_n(&pic_type, 3)?; // pic_type 67 | 68 | // rbsp_trailing_bits() 69 | writer.write(true)?; // rbsp_stop_one_bit 70 | 71 | // rbsp_alignment_zero_bit 72 | writer.byte_align()?; 73 | 74 | data.extend_from_slice( 75 | writer 76 | .as_slice() 77 | .ok_or_else(|| anyhow!("Unaligned bytes"))?, 78 | ); 79 | 80 | Ok(data) 81 | } 82 | --------------------------------------------------------------------------------