1 码流结构
H264 码流实际可以理解为由一个一个的 NALU 单元组成。一个NALU的组成结构如下:
其中
- SODB:就是经过压缩后的原始码流。
- RBSP:由于SODB可能不是8字节对齐,需要加上拖尾字节进行8位对齐
- EBSP:由于NALU是以0x000001或0x00000001开始的,所以码流中有0x000001或0x00000001时,需要在01前面加上03,称为防竞争字节
以上内容,组成了NALU Payload。
NALU Header是一个字节(8 bit)数据。如图:
其中
- forbidden_zero_bit
在网络传输中发生错误时,会被置为 1,告诉接收方丢掉该单元;否则为 0。 - nal_ref_idc
用于表示当前NALU的重要性,值越大,越重要。
解码器在解码处理不过来的时候,可以丢掉重要性为 0 的 NALU。 - nal_unit_type
表示 NALU 数据的类型,有以下几种
typedef enum
{
NAL_UNSPECIFIED = 0,
NAL_CODED_SLICE = 1,
NAL_CODED_SLICE_DP_A = 2,
NAL_CODED_SLICE_DP_B = 3,
NAL_CODED_SLICE_DP_C = 4,
NAL_CODED_SLICE_IDR = 5,
NAL_SEI = 6,
NAL_SEQ_PARAM_SET = 7,
NAL_PIC_PARAM_SET = 8,
NAL_ACCESS_UNIT_DELIMITER = 9,
NAL_END_OF_SEQUENCE = 10,
NAL_END_OF_STREAM = 11,
NAL_FILLER_DATA = 12,
NAL_SPS_EXT = 13,
NAL_CODED_SLICE_AUX = 19,
NAL_MAX_TYPE_VALUE = 31
} nalUnitType_e;
其中比较注意的应该是以下几个:
- 1-4:I/P/B帧,如果 nal_ref_idc 为 0,则表示 I 帧,不为 0 则为 P/B 帧。
- 5:IDR帧,I 帧的一种,告诉解码器,之前依赖的解码参数集合(接下来要出现的 SPS\PPS 等)可以被刷新了。
- 6:SEI,英文全称 Supplemental Enhancement Information,翻译为“补充增强信息”,提供了向视频码流中加入额外信息的方法。
- 7:SPS,全称 Sequence Paramater Set,翻译为“序列参数集”。SPS 中保存了一组编码视频序列(Coded Video Sequence)的全局参数。因此该类型保存的是和编码序列相关的参数。
- 8: PPS,全称 Picture Paramater Set,翻译为“图像参数集”。该类型保存了整体图像相关的参数。
- 9:AU 分隔符,AU 全称 Access Unit,它是一个或者多个 NALU 的集合,代表了一个完整的帧,有时候用于解码中的帧边界识别。
更详细的分层结构
第一层:比特流。该层有两种格式:Annexb 格式和 RTP 格式。
第二层:NAL Unit 层。包含了 NAL Header 和 NAL Body 信息。
第三层:Slice 层。一帧视频图像可编码成一个或者多个片,每片包含整数个宏块,即每片至少 一个宏块,最多时包含整个图像的宏块。
第四层:Slice data 层。Slice 由宏块(macro block, MB)组成。宏块是编码处理的基本单元。
第五层:PCM 类。
第六层:残差层。
实际码流分析
分析其中比较有代表性的3帧:
00 00 00 01 67
00 00 00 01 是一个 NALU 开始,67 是Header, 二进制为 0110 0111, nal_unit_type 为00111,即7为 SPS 帧。
00 00 00 01 68
68 二进制为 0110 1000,nal_unit_type 为01000,即 8 为 PPS 帧。
00 00 00 01 65
65 二进制为 0110 0101,nal_unit_type 为 00101, 即 5 为 IDR 帧。
SPS解析
-
profile_idc:表示档次,profile表示编码使用的技术等级
-
level_idc:表示等级,Level越高,视频的码率、分辨率、fps越高。
-
constraint_set0_flag:为1表示要遵循A.2.1的条件。
-
constraint_set1_flag:为1表示要遵循A.2.2的条件。
-
constraint_set2_flag:为1表示要遵循A.2.3的条件。
-
constraint_set3_flag
-
constraint_set4_flag
-
constraint_set5_flag:表示video需要遵循的额外的限制。详细内容在7.4.2.1.1和附录A。
-
reserved_zero_2bits:保留位,值为0
-
seq_parameter_set_id:表明PPS参数集使用的SPS的id,值在0-31之间。
-
chroma_format_idc:指定色度和亮度的采样格式,chroma_format_idc的值在0-3之间,如果没有指定,则默认为1(4:2:0采样)。
-
separate_colour_plane_flag:值为1,表示颜色分量是单独编码的;值为0,表示非单独编码。没有指定,则默认为0。根据separate_colour_plane_flag的值,计算ChromaArrayType的值:
如果separate_colour_plane_flag
等于0,则ChromaArrayType
等于chroma_format_idc
。
如果separate_colour_plane_flag
等于1,则ChromaArrayType
等于0。
-
bit_depth_luma_minus8:表示亮度分量的比特位宽。值在0-6之间。如果没有指定,则默认为0;
BitDepthY = 8 + bit_depth_luma_minus8
QpBdOffsetY = 6 * bit_depth_luma_minus8
-
bit_depth_chroma_minus8:表示色度分量的比特位宽。值在0-6之间。如果没有指定,则默认为0;
BitDepthC = 8 + bit_depth_chroma_minus8
QpBdOffsetC = 6 * bit_depth_chroma_minus8
-
qpprime_y_zero_transform_bypass_flag:
qpprime_y_zero_transform_bypass_flag
等于 1 是指当 QP’Y 等于 0 时变换系数解码过程的变换旁路操作和图像构建过程将会在 H264 官方文档第 8.5 节给出的去块效应滤波过程之前执行。
qpprime_y_zero_transform_bypass_flag
等于 0 是指变换系数解码过程和图像构建过程在去块效应滤波过程之前执行而不使用变换旁路操作。
当qpprime_y_zero_transform_bypass_flag
没有特别指定时,应推定其值为 0。 -
seq_scaling_matrix_present_flag:
seq_scaling_matrix_present_flag
等于 1 表示存在 i = 0…7 的标志seq_scaling_list_present_flag [i]
。
seq_scaling_matrix_present_flag
等于 0 则表示不存在这些标志并且由 Flat_4x4_16 表示的序列级别的缩放比例列表应被推断出来(对应 i = 0…5),由 Flat_8x8_16 表示的序列级别的缩放比例列表应被推断出来(对应 i = 6…7)。
当 `seq_scaling_matrix_present_flag 没有特别指定时,应推定其值为 0。 -
seq_scaling_list_present_flag[ i ]:seq_scaling_list_present_flag [i] 等于 1 是指视频序列参数集中存在缩放比例列表 i 的语法结构。
-
log2_max_frame_num_minus4:值在0-12之间。用来计算MaxFrameNum。fram_num标识图像的解码顺序。MaxFrameNum表示frame_num的最大值。注意:frame_num是循环计数,达到MaxFrameNum后又从0开始计数。解码器必须有机制检测这种循环。
MaxFrameNum = 2^(log2_max_frame_num_minus4 + 4)
-
pic_order_cnt_type:表示POC(图像的播放顺序)使用的计算方法。值在0-2之间。
-
log2_max_pic_order_cnt_lsb_minus4:用来计算MaxPicOrderCntLsb,当pic_order_cnt_type为0时使用。值在0-12之间
MaxPicOrderCntLsb = 2^(log2_max_pic_order_cnt_lsb_minus4 + 4)
-
delta_pic_order_always_zero_flag:值为1表示 slice headers中没有 delta_pic_order_cnt[ 0 ] 和 delta_pic_order_cnt[ 1 ],值为0表示有delta_pic_order_cnt[ 0 ] ,可能有delta_pic_order_cnt[ 1 ] 。默认值为0.
-
offset_for_non_ref_pic:用于计算非参考帧的播放顺序,值在((-2)31+1)~ (231-1)之间
-
offset_for_top_to_bottom_field:用于计算底场的播放顺序,值在((-2)31+1)~ (231-1)之间
-
num_ref_frames_in_pic_order_cnt_cycle:用于计算POC,值在0-255之间
-
offset_for_ref_frame[ i ]:num_ref_frames_in_pic_order_cnt_cycle值的数组。
当pic_order_cnt_type
是 1,ExpectedDeltaPerPicOrderCntCycle
值为:
ExpectedDeltaPerPicOrderCntCycle = 0 for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) ExpectedDeltaPerPicOrderCntCycle += offset_for_ref_frame[ i ]
-
max_num_ref_frames:表示短期参考帧和长期参考帧的最大数量,值在0到
MaxDpbFrames
之间。用此元素开辟参考帧缓冲区。 -
gaps_in_frame_num_value_allowed_flag:为1时,表示frame_num可以不连续。正常情况下,frame_num是连续的,当frame_num不连续时,表示有丢帧的现象。此时需要解码器用错误掩藏机制近似的还原这些帧。为0时,表示frame_num必须连续。即在任何情况下都不能丢帧。
-
pic_width_in_mbs_minus1:以宏块大小为单位的宽度
PicWidthInMbs = pic_width_in_mbs_minus1 + 1
luma分量宽度:PicWidthInSamplesL = PicWidthInMbs * 16
chroma分量宽度:PicWidthInSamplesC = PicWidthInMbs * MbWidthC
-
pic_height_in_map_units_minus1:
PicHeightInMapUnits = pic_height_in_map_units_minus1 + 1
PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits
-
frame_mbs_only_flag:值为0表明视频序列可以为场或帧,值为1只能编码为帧
如果是场,则height需要乘以2.所以得出新的height计算方式:
PicHeightInMbs=(2-frame_mbs_only_flag) * PicHeightInMapUnits
-
mb_adaptive_frame_field_flag:表示是否属于帧场自适应模式。为1表示帧场自适应模式。
-
direct_8x8_inference_flag:指定在B_Skip, B_Direct_16x16和B_Direct_8x8的亮度运动矢量的推导过程中使用的方法。当frame_mbs_only_flag为0,direct_8x8_inference_flag必须为1.
-
frame_cropping_flag:是否将图像裁剪后输出。
-
frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset:裁剪的左右上下。
-
vui_parameters_present_flag:
PPS解析
- pic_parameter_set_id:
- seq_parameter_set_id:
- entropy_coding_mode_flag:指定熵编码方法,0表示CAVLC,1表示CABAC
- bottom_field_pic_order_in_frame_present_flag:值为1表示 delta_pic_order_cnt_bottom(pic_order_cnt_type=0) 和delta_pic_order_cnt 1 在slice header中。值为0表示不在。
- num_slice_groups_minus1:一帧图像中slice的个数。为0表示只有1个slice。
- slice_group_map_type:slice的映射类型,范围在[0, 6]
- run_length_minus1[ i ]:当slice类型为0时,每个slice连续的map_unit的数量。
- top_left[ i ] and bottom_right[ i ]:当slice类型为2时,矩形区域的左上及右下位置。
- slice_group_change_direction_flag:与slice_group_change_rate_minus1一起使用,当slice_group_map_type为3、4或5时,指定refined映射类型。
- slice_group_change_rate_minus1:与slice_group_change_direction_flag一起使用,当slice_group_map_type为3、4或5时,表示一个slice的大小从一个图像到下一个图像变化的倍数。以map_unit为单位。
SliceGroupChangeRate = slice_group_change_rate_minus1 + 1
- pic_size_in_map_units_minus1:当slice类型为6时,表明图像以map_unit为单位的大小。
pic_size_in_map_units_minus1 = PicSizeInMapUnits − 1
- slice_group_id[ i ]:当slice类型为6时,表明某个map_unit属于哪个slice。
map_unit在帧模式下,表示宏块。帧场自适应时表示宏块对。场模式时,表示宏块。 - num_ref_idx_l0_default_active_minus1:当前参考帧的数量
- num_ref_idx_l1_default_active_minus1:同上。
- weighted_pred_flag:是否允许P slcie和SP slice的加权预测。
- weighted_bipred_idc:是否允许B slice的加权预测。
- pic_init_qp_minus26:亮度分量的量化参数初始值。范围[-26, +25]
- pic_init_qs_minus26:同上,用于SP和SI。
- chroma_qp_index_offset:用于计算色度分量的量化参数。
- deblocking_filter_control_present_flag:为1表示slice header中有去方块滤波的语法元素。为0表示没有。
- constrained_intra_pred_flag:
- redundant_pic_cnt_present_flag:0表示 redundant_pic_cnt不在slice header中,1表示在
- transform_8x8_mode_flag:1表示使用8x8变换处理,0表示不适用。没有指定,则默认为0
- pic_scaling_matrix_present_flag:0表示和SPS的一样,1表示和SPS的不一样,没有指定则默认为0
- pic_scaling_list_present_flag[ i ]:
- second_chroma_qp_index_offset:指定应添加到QPY和QSY的偏移量,以寻址Cr色度分量的QPC值表。没指定时,默认为 chroma_qp_index_offset。
slice header解析
- first_mb_in_slice:slice中的第一个宏块的地址。
- slice_type:
- pic_parameter_set_id:
- colour_plane_id:
- frame_num:标识参考帧的顺序。当gaps_in_frame_num_value_allowed_flag为0时,每个图像的frame_num是其前一个参考帧的frame_num值加1。当gaps_in_frame_num_value_allowed_flag为1时,解码器需要将确实的frame_num和图像补齐。
- field_pic_flag:为1表示场模式。
- bottom_field_flag:为1表示当前图像是底场,为0表示是顶场
- idr_pic_id:IDR图像的ID,范围在[0, 65535]
- pic_order_cnt_lsb:在第一种算法中,计算POC。
- delta_pic_order_cnt_bottom:底场和顶场播放顺序差别。
- delta_pic_order_cnt[ 0 ]:
- delta_pic_order_cnt[ 1 ]:
- redundant_pic_cnt:冗余slice的ID,范围在[0,127]
- direct_spatial_mv_pred_flag:B帧的直接预测是时间预测还是空间预测。
- num_ref_idx_active_override_flag:表示是否重载num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1。
- num_ref_idx_l0_active_minus1:
- num_ref_idx_l1_active_minus1:
- cabac_init_idc:选择cabca初始化表格,范围[0, 2]
- slice_qp_delta:当前slice的所有宏块的量化参数的初始化SliceQPY ,SliceQPY 范围[0, 51]
SliceQPY = 26 + pic_init_qp_minus26 + slice_qp_delta
- sp_for_switch_flag:
- slice_qs_delta:与slice_qp_delta类似。
QSY = 26 + pic_init_qs_minus26 + slice_qs_delta
- disable_deblocking_filter_idc:表明在块的边界是否用滤波。
- slice_alpha_c0_offset_div2:
FilterOffsetA = slice_alpha_c0_offset_div2 << 1
- slice_beta_offset_div2:
FilterOffsetB = slice_beta_offset_div2 << 1
- slice_group_change_cycle:当slice 类型是3.4.5时,计算map_unit的个数
MapUnitsInSliceGroup0 = Min( slice_group_change_cycle * SliceGroupChangeRate, PicSizeInMapUnits )
参考图像序列重排序语法元素
- ref_pic_list_reordering_flag_l0:指明l0是否进行重排序操作。值为1表示进行重排序。
- ref_pic_list_reordering_flag_l1:同上。
- reordering_of_pic_nums_idc:指明执行哪种重排序操作。
- abs_diff_pic_num_minus1:
- long_term_pic_num:
reordering_of_pic_nums_idc | 操作 |
---|---|
0 | 短期参考帧重排序,从当前图像的PicNum减去(abs_diff_pic_num_minus1+1)后指明需要重排序的图像 |
1 | 短期参考帧重排序,从当前图像的PicNum加上(abs_diff_pic_num_minus1+1)后指明需要重排序的图像 |
2 | 长期参考帧重排序,由long_term_pic_num指明需要重排序的图像 |
3 | 结束循环,退出重排序操作 |
以上是一次重排序,当一组图像需要重排序时,在一个循环中处理。
加权预测的语法元素
- luma_log2_weight_denom:给出参考帧列表中参考图像所有亮度的加权系数,取值范围为【0,7】
- chroma_log2_weight_denom:同上,色度
- luma_weight_l0_flag:值为1,表明在参考图像列表l0中的亮度的加权系数存在。为0,不存在。
- luma_weight_l0[i]:
- luma_weight_l[1]:
- luma_offset_l0[0]
- chroma_weight_l0_flag、chroma_weight_l0[i][j]:
参考图像序列标记(Marking)操作的语义
marking操作主要负责及那个参考图像移入或移出参考队列。
- no_output_of_prior_pics_flag:仅在IDR图像时出现,表明是否将前面已解码的图像全部输出。
- long_term_reference_flag:仅在IDR图像时出现,值为1,表明使用长期参考机制,每个IDR解码后自动成为长期参考帧。值为0,每个IDR解码后自动成为短期参考帧。
- adaptive_ref_pic_marking_mode_flag:指明marking操作的模式。值为0表示先入先出模式。值为1表示自适应Marking。后续有一系列语法元素显示指明操作的步骤。
- memory_management_control_operation:在自适应marking模式中,指明本次操作的具体内容。
memory_management_control_operation | Marking操作 |
---|---|
0 | 结束循环,退出marking操作 |
1 | 将一个短期参考图像移出参考帧队列 |
2 | 将一个长期参考图像移出参考帧队列 |
3 | 将一个短期参考图像标为长期参考图像 |
4 | 指明长期参考帧的最大数目 |
5 | 清空参考帧队列,并禁用长期参考机制 |
6 | 将当前图像存为一个长期参考帧 |
- difference_of_pic_nums_minus1:当memory_management_control_operation为1或3时,指明参考帧序号。
- long_term_pic_num:当memory_management_control_operation为2时,指明参考帧序号。
- long_term_frame_idx:当memory_management_control_operation为3或6时,分配一个长期参考帧序号
- max_long_term_frame_idx_plus1:减1,指明长期参考队列的最大数目。
slice data的语义
- cabac_alignment_one_bit:当熵编码为CABAC时,要求数据对齐,即数据从下一个字节的第一个bit开始,如果没有对齐,则将补充若干个cabac_alignment_one_bit。
- mb_skip_run:当图像采用帧间预测时,h264允许在平坦区域使用跳跃块,跳跃块不携带任何数据,通过周围已重建的宏块恢复跳跃块。当时CABAC时,每个跳跃块都由mb_skip_run指定。当是CAVLC时,表明用行程的方法给出紧接着跳跃块的数目。mb_skip_run的取值范围【0,PicSizeInMbs - CurrMbAddr】。
- mb_skip_flag:指明当前宏块是否是跳跃块。
- mb_field_decoding_flag:在帧场自适应图像中,指明当前宏块所属的宏块对是帧模式还是场模式。等于0,表明是帧模式。等于1,表明是场模式。如果一个宏块对的两个宏块都没有出现该语法元素,说明是跳跃块。此时该语法元素由以下决定:
1)如果宏块对与左边的宏块对属于同一个slice,则与左边的相同。
2)否则,如果与上面同属于一个slice,则与上面的相同。
3)如果左边上面都没有,则为0,表明是帧模式 - end_of_slice_flag:指明是否到了slice的结尾。
宏块层的语义
- mb_type:宏块的类型
- pcm_alignment_zero_bit:值为0
- pcm_byte[i]:像素值,
- codecd_block_pattern:CBP,指亮度和色度分量的各小块的残差的编码方案。有以下几种:
1)所有残差(DC AC)都编码
2)值编码DC系数
3)所有残差都不编码 - mb_qp_delta:宏块层中(相邻两个宏块)的量化参数偏移值。值的范围【-26,+25】。
QP = (QP(前一个) + mb_qp_delta + 52) % 52;
其中QP(前一个) = 26 + pic_init_qp_minus26 + slice_qp_delta.
宏块预测的语义
- prev_intra4x4_pred_mode_flag[luma4x4BlkIdx]和rem_intra4x4_pred_mode[luma4x4BlkIdx]:
prev_intra4x4_pred_mode_flag指明帧内预测中亮度分量的预测模式是否是实际预测模式。如果是,就不需要另外再传;如果不是,就由rem_intra4x4_pred_mode指定。 - intra_chroma_pred_mode:帧内预测时指定色度的预测模式。
intra_chroma_pred_mode | 预测模式 |
---|---|
0 | DC |
1 | Horizontal |
2 | Vertical |
3 | Plane |
- ref_idx_l0[mbPartIdx]:用参考帧列表l0进行帧间预测时,参考图像的序号。mbPartIdx是宏块分区的序号
- ref_idx_l1[mbPartIdx]:同上
- mvd_l0[mbPartIdx][0][compIdx]:运动矢量的预测值和实际值之差。compIdx为0时为水平运动矢量,为1时为垂直运动矢量。
- mvd_l1[mbPartIdx][0][compIdx]:同上
子宏块预测的语义
- sub_mb_type[mbPartIdx]:指明子宏块的预测类型。
- ref_idx_l0[mbPartIdx]:用参考帧列表l0进行帧间预测时,参考图像的序号。mbPartIdx是宏块分区的序号
- ref_idx_l1[mbPartIdx]:同上
-
- mvd_l0[mbPartIdx][0][compIdx]:运动矢量的预测值和实际值之差。compIdx为0时为水平运动矢量,为1时为垂直运动矢量。
- mvd_l1[mbPartIdx][0][compIdx]:同上
CAVLC语义
- coeff_token:指明非零系数的个数,拖尾系数的个数。
- trailing_ones_sign_flag:拖尾系数的正负符号,值为0,拖尾系数为+1。值为1,拖尾系数为-1
- levle_prefix和level_suffix:非零系数值的前缀和后缀
- total_zeros:系数中0的总个数
- run_before:在非零系数之前连续零的个数。
#CABAC语义
- coded_ block_flag:当前块是否包含非零系数。值为0,表示不包含。反之,则包含。
- significant_coeff_flag[i]:指出位置i处的变换系数是否为0.值为0,表示为0.反之,则不为0
- last_significant_cioeff_flag[i]:表示当前位置i处的变换系数是否为块中最后一个非零系数。值为1,表示这个块中随后的系数都为0。否则,后面还有其他非零系数。
- coeff_abs_level_minus[i]:系数的绝对值减1.
- coeff_sign_flag[i]:系数的符号位。值为0,为正数。值为1,为负数。