H.264码流分析
码流分析工具:H264visa,Elecard StreamEye Tools
1.SPS,PPS分析
使用码流分析仪打开一个MP4文件:
打开View-info可以看到SPS和PPS的内容
(1)sequence parameter set 序列参数集
SPS中保存了一组视频编码序列的全局参数,一般情况下位于整个码流的起始位置。在做视频播放器时,为了让后续的解码过程可以使用SPS中包含的参数,必须对其中的数据进行解析。
各个参数的含义
参数 | 解释 |
---|---|
profile_idc: Main | 表示当前H.264码流的profile。H.264中定义了三种常用的profile:baseline_profile,main_profile,extended_profile |
level_idc: 41 | 标识当前码流的level。编码的level定义了某种条件下的最大视频分辨率,最大视频帧率等参数,码流所遵从的level由level_idc指定 |
seq_parameter_set_id : 0 | 标识当前的序列参数集的id。通过该id值,图像参数集PPS可以引用其代表的SPS中的参数 |
log2_max_frame_num_minus4: 4(8) | MaxFrameNum 是frame_num的上限值,frame_num是图像序号的一种表示方法,在帧间编码中常用作一种参考帧标记的手段 。MaxFrameNum=2(log2_max_frame_num_minus4+4)。所以该视频MaxFrameNum=28=256 |
pic_order_cnt_type: 0 | 表示解码picture_order_count(POC)的方法。POC是另一种计量图像序号的方式,与frame_num有着不同的计算方法 |
pic_width_in_mbs_minus1: 119 | 用于计算图像的宽度,单位为宏块个数,因此图像的实际宽度为:(60+1)×16=976 |
pic_height_in_mbs_minus1: 60 | 使用PicHeightInMapUnits来度量视频中一帧图像的高度。PicHeightInMapUnits并非图像明确的以像素或以宏块为单位的高度,而需要考虑该宏块是帧编码或场编码。因此 图像的实际高度为(119+1)×16=1920 |
frame_mbs_only_flag:1 | 标识位,说明宏块的编码方式。等于0时表示本序列中所有图像的编码模式都是帧,等于1时表示本序列中的编码模式可能是帧或场或帧场自适应 |
direct_8x8_inference_flag | 标识运动向量的预测方法,frame_mbs_only_flag为0时此处则应为1。 |
frame_cropping_flag | 标识是否需要对输出图像进行裁剪,要裁剪(=1)时需要再声明裁剪的边缘位置frame_cropping_rect_left_offset,frame_cropping_rect_right_offset,frame_cropping_rect_top_offset,frame_cropping_rect_bottom_offset |
vui_parameters | 标识码流中是否有vui子结构。同时,其中 time_scale 与num_unis_in_tick 可以对视频的帧率进行计算。公式为frame = time_scale / num_unis_in_tick |
由frame = time_scale / num_unis_in_tick计算方法,得到本视频的帧率为60/1=60,但在File信息中给出的帧率为30,不一致。
(2)picture parameter set 图像参数集
通常情况下,PPS类似于SPS,在H.264的裸码流中单独保存与一个NAL Unit中,只是PPS NAL Unit的nal_unit_type值为8;而在封装格式中,PPS通常与SPS一起,保存在视频文件的文件头中。
各个参数的含义
参数 | 解释 |
---|---|
pic_parameter_set_id: 0 | 表示当前PPS的id,某个PPS在码流中会被相应的slice引用,slice引用PPS的方式就是在slice header中保存PPS的id值。该值的取值范围为[0,255] |
seq_parameter_set_id: 0 | 表示当前PPS所引用的激活的SPS的id。通过这种方式,PPS中也可以取到对应SPS中的参数。该值的取值范围为[0,31] |
entropy_coding_mode_falg: 1(CABAC) | 熵编码模式标识,该标识位表示码流中熵编码/解码地算法,当该值为0时,选择左边的算法,通常为哥伦布码或者是CAVLC;当该值为1时,选择右边的算法,通常为CABAC |
num_slice_groups_minus1 | 表示某一帧中slice group的个数。当该值为0时,一帧中所有的slice都属于一个slice group |
num_ref_idx_l0_default_active_minus1 | 表示当Slice Header中的num_ref_idx_active_override_flag标识位为0时,P/SP/B slice的语法元素num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1的默认值 |
weighted_pred_flag: 1 | 标识位,表示在P/SP slice中是否开启加权预测 |
weighted_bipred_idc: 0 | 表示在B slice中加权预测地方法,取值范围为[0,2]。0表示默认加权预测,1表示显式加权预测,2表示隐式加权预测 |
pic_init_qp_minus26和pic_init_qs_minus26 | 表示初始的量化参数。实际的量化参数由该参数、slice header中的slice_qp_delta/slice_qs_delta计算得到 |
chroma_qp_index_offset | 用于计算色度分量的量化参数,取值范围为[-12,12] |
deblocking_filter_control_present_flag | 标识位,用于表示Slice header中是否存在用于去块滤波器控制的信息。当该标志位为1时,slice header中包含去块滤波相应的信息;当该标识位为0时,slice header中没有相应的信息 |
constrained_intra_pred_flag | 若该标识为1,表示I宏块在进行帧内预测时只能使用来自I和SI类型宏块的信息;若该标识位0,表示I宏块可以使用来自Inter类型宏块的信息 |
redundant_pic_cnt_present_flag | 标识位,用于表示Slice header中是否存在redundant_pic_cnt语法元素。当该标志位为1时,slice header中包含redundant_pic_cnt;当该标识位为0时,slice header中没有相应的信息 |
2.GOP分析
(1)I帧(帧内编码帧)
使用H264visa打开测试视频,将指针指到第一帧(该帧为I帧,即帧内编码帧)。打开MB Block ,观察宏块信息。可以清楚的看到所有宏块都为I型宏块,大小分别为16×16,8×8,4×4。在summary-statistics中,可以看到宏块的统计数据。I_N×N数量为539表示8×8和4×4的宏块共539个,16×16宏块的个数为920-539=381个。
通过观察该帧图像,可以发现,在图像变化复杂处或突变处,宏块划分的更细;在图像平坦区域,通常使用较大的宏块。
在MB Info中可以看到不同宏块的具体信息。随机选取一个I,P,B帧,查看其具体信息:
宏块类型(I) | 信息 |
---|---|
16×16 | ![]() |
8×8 | ![]() |
4×4 | ![]() |
(2)P帧(前向预测编码帧)
宏块大小有16×16,8×8,4×4,16×8,8×16,但是我们看到这些宏块有不同的颜色,查看绿色宏块和红色宏块的具体信息,可以发现绿色宏块为P类宏块,红色宏块为I型宏块。
查看宏块统计信息,可以看到I型宏块占比较小,大部分还是P型宏块。对于前后图像变化很大的区域,该宏块在之前的帧中找不到相似的宏块,因此使用帧内编码模式。
在P型宏块中,可以看到P_skip类型的宏块占了很大比例,说明参考帧中有完全相同的宏块,大大提高了压缩效率。
(3)B帧(双向预测编码帧)
蓝色宏块为B型宏块,红色宏块为I型宏块,可以看出,大部分宏块为B型宏块,且B型宏块大部分都为B_skip。查看宏块的统计数据:
显示运动向量,该帧右边区域运动向量很多,可能是因为镜头晃动的原因。
(4)图像帧比特分析
使用Elecard StreamEye Tools打开一个mp4文件。可以看到不同类型帧的比特数对比。红色代表I帧,蓝色代表P帧,绿色代表B帧。橙色水平线为视频序列的平均比特数。可以明显的看出,B帧占用的比特数最小,I帧占用的比特数最多。I帧出现的次数很少。
由于B帧需要前面的帧和后面的帧作为参考帧,有帧序乱的问题,需要进行帧重排。在Frame Info中可以看出解码顺序与显示顺序不同的情况。
将该视频序列的统计信息进行保存,使用excel打开,以帧号为横坐标,每帧所用比特数为纵坐标画出曲线图: