一.H.264与mp4的区别
h.264和mpeg-2预测、变换、量化、编码模块顺序上并无大的区别,在此基础上采用了以下的新技术:
帧内编码:在空域内进行帧内预测,提高帧内编码精确度。
运动估计和补偿:采用不同尺寸不同形状的块进行划分,有着高分辨率的子像素运动估计(选择多个参考帧进行估计)。
DCT变换:采用整数DCT变换。
增加自适应消块滤波器去除块效应。
熵编码技术:采用通用变长编码、基于上下文的自适应变长码编码、基于上下文的自适应二进制算术编码。
二.H.264的语法结构
NAL用于定义适合于任何信道传输和存储的数据格式,并提供头信息,从而为视频编码提供与外界的统一接口;所有的数据都包含于NAL单元(NAL units)中,每个NAL单元包含整数个字节的数据。其中有两个重要的Unit分别是SPS和PPS,由于软件无法打开自己的mp4视频文件,只能对老师实验下发的demo.264文件进行分析。
三.序列参数集SPS
SPS里主要保存了一组编码视频序列(Coded Video Sequence)的全局参数。所谓的编码视频序列即原始视频的一帧一帧的像素数据经过编码之后的结构组成的序列。而每一帧的编码后数据所依赖的参数保存于图像参数集(PPS)中。
一般情况SPS和PPS的NAL Unit通常位于整个码流的起始位置。
(1)profile_idc:[66]
标识当前H.264码流的profile。H.264中定义了三种常用的档次profile:
基准档次:baseline profile;主要档次:main profile;扩展档次:extended profile;
(2) level_idc:[31]
标识当前码流的Level。编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。
(3) seq_parameter_set_id:[0]
表示当前的序列参数集的id。通过该id值,图像参数集pps可以引用其代表的sps中的参数。
(4)log2_max_frame_num_minus4: [4]
用于计算MaxFrameNum的值,公式为:
MaxFrameNum=2^(log2_max_frame_num_minus4+4),这里的大小为2^8。
MaxFrameNum是frame_num的上限值,frame_num是图像序号的一种表示方法,在帧间编码中常用作一种参考帧标记的手段。
(5) log2_max_pic_order_cnt_lsb_minus4:[2]
用于计算MaxPicOrderCntLsb的值,该值表示POC的上限。计算方法为:
MaxPicOrderCntLsb=2^(log2_max_pic_order_cnt_lsb_minus4+4)
这里的大小为2^6。
(6) num_ref_frames:[2]
用于表示参考帧的最大数目。
(7) pic_width_in_mbs_minus1:[53]
用于计算图像的宽度。单位为宏块个数,因此图像的实际宽度为:
Frame_width=(pic_width_in_mbs_minus1+1)
这里图像的宽为:16*(53+1)=864
(8) pic_height_in_map_units_minus1:[29]
使用PicHeightInMapUnits来度量视频中一帧图像的高度。
PicHeightInMapUnits并非图像明确的以像素或宏块为单位的高度,而需要考虑该宏块是帧编码或场编码,其计算方式为:
PicHeightInMapUnits=16*(pic_height_in_map_units_minus1+1)
这里图像的高是:16*(29+1)=480
(9) frame_mbs_only_flag:[1]
标识位,说明宏块的编码方式。当该标识位为0时,宏块可能为帧编码或场编码;该标识位为1时,所有宏块都采用帧编码。根据该标识位取值不同,PicHeightInMapUnits的含义也不同,为0时表示一场数据按宏块计算的高度,为1时表示一帧数据按宏块计算的高度。
按照宏块计算的图像实际高度FrameHeightInMbs的计算方法为:
FrameHeightInMbs=(2-frame_mbs_only_flag)*PicHeightInMapUnits
这里图像实际的高度为:frame-height=1*30=30
(10) frame_cropping_flag:[1]
标识位,说明是否需要对输出的图像帧进行裁剪。
四.图像参数集PPS
上网找到了帧率的计算公式为:
帧率=time_scale/num_units_in_tick=60000/1001=59.94Hz
(1) pic_parameter_set_id:[0]
指定当前PPS的id。某个PPS在码流中会被相应的slice引用,slice引用PPS的方式就是在Slice header中保存PPS的id值。
(2) seq_parameter_set_id:[0]
表示当前PPS所引用的激活的SPS的id。通过这种方式,PPS中也可以取到对应SPS中的参数。
(3) entropy_coding_mode_flag:[0]
熵编码模式标识,该标识位表示码流中熵编码/解码选择的算法。在baseline profile等设置下采用指数哥伦布编码,在main profile等设置下采用CABAC编码。当该值为0时,选择左边的算法,通常为指数哥伦布编码或者CAVLC;当该值为1时,选择右边的算法,通常为CABAC。
(4) num_slice_groups_minus1:[0]
表示某一帧中slice group的个数。当该值为0时,一帧中所有的slice都属于一个slice group。
(5) weighted_pred_flag:[0]
标识位,表示在P/SP slice中是否开启加权预测。
(6) weighted_bipred_idc:[0]
表示在B Slice中加权预测的方法,取值范围为[0,2]。0表示默认加权预测,1表示显式加权预测,2表示隐式加权预测。
(7) pic_init_qp_minus26和pic_init_qs_minus26:[0;0]
表示初始的量化参数。实际的量化参数由该参数、slice header中的slice_qp_delta/slice_qs_delta计算得到。
(8) chroma_qp_index_offset:[0]
用于计算色度分量的量化参数,取值范围为[-12,12]。
(9) constrained_intra_pred_flag:[0]
若该标识为1,表示I宏块在进行帧内预测时只能使用来自I和SI类型宏块的信息;若该标识位0,表示I宏块可以使用来自Inter类型宏块的信息。
(10) redundant_pic_cnt_present_flag:[0]
标识位,用于表示Slice header中是否存在redundant_pic_cnt语法元素。当该标志位为1时,slice header中包含redundant_pic_cnt;当该标识位为0时,slice header中没有相应的信息。
五.GOP中帧数据的分析
1.每个图像帧的类型及所用的编码比特数、QP值;并以图像帧号为横坐标、每帧所用比特数为纵坐标画出曲线图;以图像帧号为横坐标、每帧所用QP为纵坐标画出曲线图。
用H.264Visa打开demo.264观察统计,该视频只有第一帧是I帧,其余都是P帧。
QP值对应量化步长的序号,对于亮度而言,此值范围为0~51 。值越小,量化步长越小,量化的精度就越高,意味着同样画质的情况下,产生的数据量可能会更大。
I帧:
挑选的一个P帧:
观察可以发现,I帧所有的块都是I类型;P帧少部分块是I类型,大多是P类型,但是I类型所用比特数比P类型大得多。
用ESEyE打开demo.264观察并输出数据保存为tyt.csv。
可以查看每一帧的QP值和总比特值但是无法批量获得统计数据,绘图困难!
2.以第一个I帧作为分析对象,基于该帧图像的空间特性,分析每个宏块所采用的编码类型及其比例。
由图可以看出有两种:点红和格子红。点红在图像平滑处,格子红在细节丰富处,格子越多,量化程度越高。
点红 | 格子红 | 比例 |
可以看出,该I帧没有使用到帧间编码,该帧完全使用了帧内编码,由于该帧没有前参考帧,没有使用到运动矢量。
3.以第一个P帧作为分析对象,基于该帧图像的空间和时间特性,分析每个宏块所采用的编码类型及其比例。
一共有四种,点蓝,点黄,点红,格子红。
点黄 | 点蓝 | 点红 | 格子红 | 比例 |
4.以某一个B帧作为分析对象,基于该帧图像的空间和时间特性,分析每个宏块所采用的编码类型及其比例。
找不到B帧的视频,主要是打不开,无法统计呜呜呜哭了。