HEVC如何计算Cu_Qp_Delta

本文深入分析了HEVC标准中CuQpDelta的计算方式,揭示其科学合理性。通过对比HEVC与AVS2在QP分配上的不同,解释了HEVC如何通过限定CuQpDelta的取值范围实现更有效的码率控制。

  研究HEVC标准发现,HEVC对cu_qp_delta的处理虽然有点晦涩难懂,但还是挺科学合理的。

  HEVC中计算CuQpDelta的公式如下:

  CuQpDelta = (qp - qp_pred + 78 + qpBdOffsetY + (qpBdOffsetY  / 2)) % (52 + qpBdOffsetY) - 26 - (qpBdOffsetY / 2)   ------ (1)

  按照直观的思路,计算CuQpDelta的公式应该是

  CuQpDelta = qp - qp_pred      ------- (2)

  为何HEVC要采用公式(1)计算CuQpDelta而没有采用(2)?

  接下来分析一下8bit编码时的情况,此时qpBdOffsetY = 0。 

  按照公式(2),CuQpDelta的取值范围为【-51, 51】

  按照公式(1),CuQpDelta的取值范围为【-26, 25】,(1)比(2)的取值范围缩小了一半,众所周知,更小的取值范围更利于节省编码码字。

  假设一个语法元素的取值范围为N,但是在已有的解码信息的帮助下,该语法元素的取值范围有可能进一步缩小。HEVC在设计中到处包含这样的思路。所以这里需要理解的是,对解码器而言,在计算当前CU的QP时,qp_pred是已知的了,那么qp - qp_pred的取值范围是【-51,51】的一个子区间,该子区间的最大值与最小值之间的范围必定小于等于51。所以需要通过一个round的操作(即公式(1)),将CuQpDelta的值限定到【-26, 25】的范围。

  最后需要强调的是,根据以上分析,虽然HEVC解码器要求语法元素CuQpDelta在【-26,25】之间,但其实对当前cu的qp是没有任何限制的,即没有要求qp在qp_pred的附近的某个范围以内。编码器在进行码率控制时,可以给当前CU分配【0-51】以内任意QP

  解码器解码CuQpDelta之后,计算QP的公式如下:

  Qp = (qp_pred + CuQpDelta + 52 + 2 * qpBdOffsetY) % (52 + qpBdOffsetY) - qpBdOffsetY

 

  对比AVS2的设计,CuQpDelta采用了公式(2)的方法计算,所以AVS2中当前CU的QP是不能任意分配的,有一定的限制。

/*---------------------------------------------------------------------- | AP4_HevcVuiParameterSet::AP4_HevcVuiParameterSet +---------------------------------------------------------------------*/ AP4_Result AP4_HevcVuiParameterSet::Parse(AP4_BitReader& bits) { aspect_ratio_info_present_flag = bits.ReadBit(); if (aspect_ratio_info_present_flag) { aspect_ratio_idc = bits.ReadBits(8); // Appendix E. Table E-1 Meaning of sample aspect ratio indicator if (aspect_ratio_idc == 255/*Extended_SAR*/) { sar_width = bits.ReadBits(16); sar_height = bits.ReadBits(16); } } overscan_info_present_flag = bits.ReadBit(); if (overscan_info_present_flag) { overscan_appropriate_flag = bits.ReadBit(); } video_signal_type_present_flag = bits.ReadBit(); if (video_signal_type_present_flag) { video_format = bits.ReadBits(3); video_full_range_flag = bits.ReadBit(); colour_description_present_flag = bits.ReadBit(); if (colour_description_present_flag) { colour_primaries = bits.ReadBits(8); transfer_characteristics = bits.ReadBits(8); matrix_coefficients = bits.ReadBits(8); } } chroma_loc_info_present_flag = bits.ReadBit(); if (chroma_loc_info_present_flag) { chroma_sample_loc_type_top_field = ReadGolomb(bits); chroma_sample_loc_type_bottom_field = ReadGolomb(bits); } neutral_chroma_indication_flag = bits.ReadBit(); field_seq_flag = bits.ReadBit(); frame_field_info_present_flag = bits.ReadBit(); default_display_window_flag = bits.ReadBit(); if (default_display_window_flag) { def_disp_win_left_offset = ReadGolomb(bits); def_disp_win_right_offset = ReadGolomb(bits); def_disp_win_top_offset = ReadGolomb(bits); def_disp_win_bottom_offset = ReadGolomb(bits); } timing_info_present_flag = bits.ReadBit(); if (timing_info_present_flag) { num_units_in_tick = bits.ReadBits(32); time_scale = bits.ReadBits(32); vui_poc_proportional_to_timing_flag = bits.ReadBit(); if (vui_poc_proportional_to_timing_flag) { vui_num_ticks_poc_diff_one_minus1 = ReadGolomb(bits); } } return AP4_SUCCESS; } /*---------------------------------------------------------------------- | AP4_HevcPictureParameterSet::AP4_HevcPictureParameterSet +---------------------------------------------------------------------*/ AP4_HevcPictureParameterSet::AP4_HevcPictureParameterSet() : pps_pic_parameter_set_id(0), pps_seq_parameter_set_id(0), dependent_slice_segments_enabled_flag(0), output_flag_present_flag(0), num_extra_slice_header_bits(0), sign_data_hiding_enabled_flag(0), cabac_init_present_flag(0), num_ref_idx_l0_default_active_minus1(0), num_ref_idx_l1_default_active_minus1(0), init_qp_minus26(0), constrained_intra_pred_flag(0), transform_skip_enabled_flag(0), cu_qp_delta_enabled_flag(0), diff_cu_qp_delta_depth(0), pps_cb_qp_offset(0), pps_cr_qp_offset(0), pps_slice_chroma_qp_offsets_present_flag(0), weighted_pred_flag(0), weighted_bipred_flag(0), transquant_bypass_enabled_flag(0), tiles_enabled_flag(0), entropy_coding_sync_enabled_flag(0), num_tile_columns_minus1(0), num_tile_rows_minus1(0), uniform_spacing_flag(1), loop_filter_across_tiles_enabled_flag(0), pps_loop_filter_across_slices_enabled_flag(0), deblocking_filter_control_present_flag(0), deblocking_filter_override_enabled_flag(0), pps_deblocking_filter_disabled_flag(0), pps_beta_offset_div2(0), pps_tc_offset_div2(0), pps_scaling_list_data_present_flag(0), lists_modification_present_flag(0), log2_parallel_merge_level_minus2(0), slice_segment_header_extension_present_flag(0) { } /*---------------------------------------------------------------------- | AP4_HevcPictureParameterSet::Parse +---------------------------------------------------------------------*/ AP4_Result AP4_HevcPictureParameterSet::Parse(const unsigned char* data, unsigned int data_size) { raw_bytes.SetData(data, data_size); AP4_DataBuffer unescaped(data, data_size); AP4_NalParser::Unescape(unescaped); AP4_BitReader bits(unescaped.GetData(), unescaped.GetDataSize()); bits.SkipBits(16); // NAL Unit Header pps_pic_parameter_set_id = ReadGolomb(bits); if (pps_pic_parameter_set_id > AP4_HEVC_PPS_MAX_ID) { DBG_PRINTF_2("pps_pic_parameter_set_id[%d] > AP4_HEVC_PPS_MAX_ID[%d]\n", pps_pic_parameter_set_id, AP4_HEVC_PPS_MAX_ID); return AP4_ERROR_INVALID_FORMAT; } pps_seq_parameter_set_id = ReadGolomb(bits); if (pps_seq_parameter_set_id > AP4_HEVC_SPS_MAX_ID) { DBG_PRINTF_2("pps_seq_parameter_set_id[%d] > AP4_HEVC_SPS_MAX_ID[%d]\n", pps_seq_parameter_set_id, AP4_HEVC_SPS_MAX_ID); return AP4_ERROR_INVALID_FORMAT; } dependent_slice_segments_enabled_flag = bits.ReadBit(); output_flag_present_flag = bits.ReadBit(); num_extra_slice_header_bits = bits.ReadBits(3); sign_data_hiding_enabled_flag = bits.ReadBit(); cabac_init_present_flag = bits.ReadBit(); num_ref_idx_l0_default_active_minus1 = ReadGolomb(bits); num_ref_idx_l1_default_active_minus1 = ReadGolomb(bits); init_qp_minus26 = SignedGolomb(ReadGolomb(bits)); constrained_intra_pred_flag = bits.ReadBit(); transform_skip_enabled_flag = bits.ReadBit(); cu_qp_delta_enabled_flag = bits.ReadBit(); if (cu_qp_delta_enabled_flag) { diff_cu_qp_delta_depth = ReadGolomb(bits); } pps_cb_qp_offset = SignedGolomb(ReadGolomb(bits)); pps_cr_qp_offset = SignedGolomb(ReadGolomb(bits)); pps_slice_chroma_qp_offsets_present_flag = bits.ReadBit(); weighted_pred_flag = bits.ReadBit(); weighted_bipred_flag = bits.ReadBit(); transquant_bypass_enabled_flag = bits.ReadBit(); tiles_enabled_flag = bits.ReadBit(); entropy_coding_sync_enabled_flag = bits.ReadBit(); if (tiles_enabled_flag) { num_tile_columns_minus1 = ReadGolomb(bits); num_tile_rows_minus1 = ReadGolomb(bits); uniform_spacing_flag = bits.ReadBit(); if (!uniform_spacing_flag) { for (unsigned int i=0; i<num_tile_columns_minus1; i++) { ReadGolomb(bits); // column_width_minus1[i] } for (unsigned int i = 0; i < num_tile_rows_minus1; i++) { ReadGolomb(bits); // row_height_minus1[i] } } loop_filter_across_tiles_enabled_flag = bits.ReadBit(); } pps_loop_filter_across_slices_enabled_flag = bits.ReadBit(); deblocking_filter_control_present_flag = bits.ReadBit(); if (deblocking_filter_control_present_flag) { deblocking_filter_override_enabled_flag = bits.ReadBit(); pps_deblocking_filter_disabled_flag = bits.ReadBit(); if (!pps_deblocking_filter_disabled_flag) { pps_beta_offset_div2 = SignedGolomb(ReadGolomb(bits)); pps_tc_offset_div2 = SignedGolomb(ReadGolomb(bits)); } } pps_scaling_list_data_present_flag = bits.ReadBit(); if (pps_scaling_list_data_present_flag) { scaling_list_data(bits); } lists_modification_present_flag = bits.ReadBit(); log2_parallel_merge_level_minus2 = ReadGolomb(bits); slice_segment_header_extension_present_flag = bits.ReadBit(); return AP4_SUCCESS; } 详细注释上述代码
09-25
#ifndef _AP4_HEVC_PARSER_H_ #define _AP4_HEVC_PARSER_H_ /*---------------------------------------------------------------------- | includes +---------------------------------------------------------------------*/ #include "Ap4Types.h" #include "Ap4Results.h" #include "Ap4DataBuffer.h" #include "Ap4NalParser.h" #include "Ap4Utils.h" /*---------------------------------------------------------------------- | constants +---------------------------------------------------------------------*/ const unsigned int AP4_HEVC_NALU_TYPE_TRAIL_N = 0; const unsigned int AP4_HEVC_NALU_TYPE_TRAIL_R = 1; const unsigned int AP4_HEVC_NALU_TYPE_TSA_N = 2; const unsigned int AP4_HEVC_NALU_TYPE_TSA_R = 3; const unsigned int AP4_HEVC_NALU_TYPE_STSA_N = 4; const unsigned int AP4_HEVC_NALU_TYPE_STSA_R = 5; const unsigned int AP4_HEVC_NALU_TYPE_RADL_N = 6; const unsigned int AP4_HEVC_NALU_TYPE_RADL_R = 7; const unsigned int AP4_HEVC_NALU_TYPE_RASL_N = 8; const unsigned int AP4_HEVC_NALU_TYPE_RASL_R = 9; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL_N10 = 10; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL_R11 = 11; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL_N12 = 12; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL_R13 = 13; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL_N14 = 14; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL_R15 = 15; const unsigned int AP4_HEVC_NALU_TYPE_BLA_W_LP = 16; const unsigned int AP4_HEVC_NALU_TYPE_BLA_W_RADL = 17; const unsigned int AP4_HEVC_NALU_TYPE_BLA_N_LP = 18; const unsigned int AP4_HEVC_NALU_TYPE_IDR_W_RADL = 19; const unsigned int AP4_HEVC_NALU_TYPE_IDR_N_LP = 20; const unsigned int AP4_HEVC_NALU_TYPE_CRA_NUT = 21; const unsigned int AP4_HEVC_NALU_TYPE_RSV_IRAP_VCL22 = 22; const unsigned int AP4_HEVC_NALU_TYPE_RSV_IRAP_VCL23 = 23; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL24 = 24; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL25 = 25; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL26 = 26; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL27 = 27; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL28 = 28; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL29 = 29; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL30 = 30; const unsigned int AP4_HEVC_NALU_TYPE_RSV_VCL31 = 31; const unsigned int AP4_HEVC_NALU_TYPE_VPS_NUT = 32; const unsigned int AP4_HEVC_NALU_TYPE_SPS_NUT = 33; const unsigned int AP4_HEVC_NALU_TYPE_PPS_NUT = 34; const unsigned int AP4_HEVC_NALU_TYPE_AUD_NUT = 35; const unsigned int AP4_HEVC_NALU_TYPE_EOS_NUT = 36; const unsigned int AP4_HEVC_NALU_TYPE_EOB_NUT = 37; const unsigned int AP4_HEVC_NALU_TYPE_FD_NUT = 38; const unsigned int AP4_HEVC_NALU_TYPE_PREFIX_SEI_NUT = 39; const unsigned int AP4_HEVC_NALU_TYPE_SUFFIX_SEI_NUT = 40; const unsigned int AP4_HEVC_NALU_TYPE_RSV_NVCL41 = 41; const unsigned int AP4_HEVC_NALU_TYPE_RSV_NVCL42 = 42; const unsigned int AP4_HEVC_NALU_TYPE_RSV_NVCL43 = 43; const unsigned int AP4_HEVC_NALU_TYPE_RSV_NVCL44 = 44; const unsigned int AP4_HEVC_NALU_TYPE_RSV_NVCL45 = 45; const unsigned int AP4_HEVC_NALU_TYPE_RSV_NVCL46 = 46; const unsigned int AP4_HEVC_NALU_TYPE_RSV_NVCL47 = 47; const unsigned int AP4_HEVC_PPS_MAX_ID = 63; const unsigned int AP4_HEVC_SPS_MAX_ID = 15; const unsigned int AP4_HEVC_VPS_MAX_ID = 15; const unsigned int AP4_HEVC_SPS_MAX_RPS = 64; const unsigned int AP4_HEVC_MAX_LT_REFS = 32; const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_IDR = 0x01; const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_IRAP = 0x02; const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_BLA = 0x04; const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_RADL = 0x08; const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_RASL = 0x10; const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_SUBLAYER_NON_REF = 0x20; const unsigned int AP4_HEVC_SLICE_TYPE_B = 0; const unsigned int AP4_HEVC_SLICE_TYPE_P = 1; const unsigned int AP4_HEVC_SLICE_TYPE_I = 2; /*---------------------------------------------------------------------- | class references +---------------------------------------------------------------------*/ struct AP4_HevcSliceSegmentHeader; 注释上述代码
最新发布
09-25
```cpp /*---------------------------------------------------------------------- | AP4_HEVC_PARSER 头文件 | 功能: HEVC/H.265视频码流解析器定义 | 核心内容: | - HEVC NAL单元类型枚举 | - 参数集ID范围定义 | - 访问单元标志位定义 | - 切片类型定义 +---------------------------------------------------------------------*/ #ifndef _AP4_HEVC_PARSER_H_ #define _AP4_HEVC_PARSER_H_ /*---------------------------------------------------------------------- | 包含文件 +---------------------------------------------------------------------*/ #include "Ap4Types.h" // 基础类型定义 #include "Ap4Results.h" // 结果码定义 #include "Ap4DataBuffer.h"// 数据缓冲区 #include "Ap4NalParser.h" // NAL解析基类 #include "Ap4Utils.h" // 工具函数 /*---------------------------------------------------------------------- | HEVC NAL单元类型定义 | 分类说明: | - VCL类型(0-31): 视频编码层单元 | - 非VCL类型(32-47): 参数集/辅助信息等 | 关键类型: | - VPS/SPS/PPS(32-34): 视频/序列/图像参数集 | - IDR/BLA(16-21): 关键帧类型 | - SEI(39-40): 补充增强信息 +---------------------------------------------------------------------*/ const unsigned int AP4_HEVC_NALU_TYPE_TRAIL_N = 0; // 非关键帧(前导) const unsigned int AP4_HEVC_NALU_TYPE_TRAIL_R = 1; // 非关键帧(参考) // ...(中间类型省略)... const unsigned int AP4_HEVC_NALU_TYPE_VPS_NUT = 32; // 视频参数集 const unsigned int AP4_HEVC_NALU_TYPE_SPS_NUT = 33; // 序列参数集 const unsigned int AP4_HEVC_NALU_TYPE_PPS_NUT = 34; // 图像参数集 const unsigned int AP4_HEVC_NALU_TYPE_PREFIX_SEI_NUT = 39; // 前缀SEI /*---------------------------------------------------------------------- | HEVC参数集ID范围限制 | 设计考虑: | - PPS_ID范围较大(0-63)以适应多图像参数 | - SPS/VPS范围较小(0-15)因变更频率低 +---------------------------------------------------------------------*/ const unsigned int AP4_HEVC_PPS_MAX_ID = 63; // PPS最大ID const unsigned int AP4_HEVC_SPS_MAX_ID = 15; // SPS最大ID const unsigned int AP4_HEVC_VPS_MAX_ID = 15; // VPS最大ID /*---------------------------------------------------------------------- | 访问单元标志位定义 | 用途: 描述帧属性用于解码控制 | 典型组合: | - IDR帧: IS_IDR|IS_IRAP | - BLA帧: IS_BLA|IS_IRAP +---------------------------------------------------------------------*/ const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_IDR = 0x01; // 即时解码刷新帧 const unsigned int AP4_HEVC_ACCESS_UNIT_FLAG_IS_IRAP = 0x02; // 随机访问点 /*---------------------------------------------------------------------- | 切片类型定义 | 编码特性: | - I切片: 帧内预测 | - P切片: 单向帧间预测 | - B切片: 双向帧间预测 +---------------------------------------------------------------------*/ const unsigned int AP4_HEVC_SLICE_TYPE_B = 0; // B切片 const unsigned int AP4_HEVC_SLICE_TYPE_P = 1; // P切片 const unsigned int AP4_HEVC_SLICE_TYPE_I = 2; // I切片 /*---------------------------------------------------------------------- | 前向声明(避免循环依赖) +---------------------------------------------------------------------*/ struct AP4_HevcSliceSegmentHeader; // 切片分段头结构体 ``` ### 关键设计说明: 1. **NAL单元类型体系**: ```mermaid graph LR A[VCL单元] --> B[非参考帧] A --> C[参考帧] A --> D[关键帧] E[非VCL单元] --> F[参数集] E --> G[SEI] E --> H[辅助信息] ``` 2. **参数集管理特点**: - VPS/SPS/PPS三级分层结构 - PPS数量配置更灵活(64个) - 通过最大ID限制内存占用 3. **标志位使用场景**: ```cpp // 判断是否为关键帧 if (flags & AP4_HEVC_ACCESS_UNIT_FLAG_IS_IRAP) { // 处理随机访问点 } ``` 4. **切片类型与预测关系**: | 类型 | 预测方式 | 参考帧需求 | |------|-------------------|------------| | I | 帧内预测 | 不需要 | | P | 前向帧间预测 | 1个 | | B | 双向帧间预测 | 2个 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ITRonnie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值