H.264详解之一

前面有一篇文章,利用H264举例,讲解了编码的基础知识。这篇文章,就详细的剖析H264。H264究竟是什么结构?为什么性能好?带着这些问题来讲解,来完成这篇文章。
1.H264元素的分层结构

H264每个基本元素由视频序列(Group of Picture),图像、片(slice)、宏块(block)、子宏块等组成。H264的元素对每个slice中的公共数据做了提取,减少了冗余度。
H.264详解之一
H264的分层结构之所以效率高,在错误识别,误码率降低,重建图形的性能提高。目前市面上很多协议都是"头部+数据"组合,比如TCP/IP网络协议,这样多重头部,势必会造成冗余信息增多,当这些冗余信息增加到码流中,也会造成一种浪费,这样的结构,一旦数据信息丢失,就会导致码流不可能被解码,造成极差的体验。
H.264详解之一H.264详解之一
在H264是将原本的公共头数据及冗余信息抽取,放到SPS(序列参数集)和PPS(图像参数集)里面,剩下的部分放到slice里面,其中SPS和PPS,一般是放在IDR帧或I帧,SPS或PPS,可以被多个图像参数集或图像使用,这种复用参数集的功能,参数集是独立,支持重发。
H.264详解之一
在H264详细结构的图中,可以看到,一般第一个序列的图像叫做IDR(立即刷新图像)帧,IDR帧在流媒体的播放器中很重要,比如要实现"秒开"画面的功能,就需要快速找到IDR帧,并清空已解码缓冲数据,开始一个新的序列,IDR图像是不会使用之前的图像进行解码,立即显示,达到极好的用户体验,关于播放器优化,以后会有专栏讲解。这里说明下,前面提到I帧和IDR帧,它们的区别是什么?IDR帧是I帧,但I帧不一定是IDR帧,比如在编码的时候设置GOP为30,那么就是说30帧图像有一个I帧,这样60帧就有2个I帧,但是60帧只有1个IDR帧。

2.H264元素编程字段说明

在各类流媒体的编解码库,实现H264编码,都会遵循以下的语法去实现,下面会一一列举出这些元素的编程顺序,以后用FFmpeg和X264源码来分析源码结合下面的图来分析。H264在网络传输时以NAL作为每个单元,多个NAL组成一个组,如何分辨NAL单元的边界呢?这就需要起始码。一般如果NAL是0x000001 或是 0x000000这样标记,就说明是一组NAL的开始,但是如果一组内部有其它NAL单元这样标记,那么就会发生错误,怎样避免这种错误呢?H264提供了一种"防止竞争机制",当检查到一组NAL还有其它是这样标记,就插入一个0x03,解码是做逆操作就行。

表格下面nal_unit_type由一组nal的属性,如0表示未使用,1表示不分区,5表示IDR图像的片,6表示SEI(补充增强信息单元)、7表示序列参数集、8表示图像参数集等。rbsp_byte[]表示NAL单元的封装格式,编码后的原始数据,封装后,放入NAL的数据部分。emulation_prevention_three_byte就是上面提到的"防止竞争机制"引入的填充字节。
H.264详解之一
SPS和PPS在编码之前,就需要将这些字段信息,写入到IDR帧头部,在解码中如果找不到SPS和PPS,一般是无法解码出图像,所以这两个结构是非常重要。profile_idc、 level_idc表示profile、level,这两个参数之前已经描述过了,可以参考前文。seq_parameter_set_id表示参数集的id号,这里需要说明,如果编码器要有新序列参数集,应该使用新的seq_parameter_set_id,而不是改变原来参数集的内容。pic_order_cnt_type表示编码方法,如果使用B帧预测,图像播放和解码的顺序有可能不一致,这就需要在编码方法中说明。offset_for_non_ref_pic表示非参考帧和场的编码方法,offset_for_top_to_bottom_field表示底场的编码方法。编码时,如果传输信道被堵塞死掉,那么可以设置gaps_in_frame_num_value_allowed_flag为1,就可以丢弃若干帧,为了在解码端防止发生错误,就必须在解码端启动错误隐藏机制去重构这些图像。
H.264详解之一
H.264详解之一
H.264详解之一
H.264详解之一
pic_parameter_set_id表示参数集序号,在各片的片头被使用,seq_parameter_set_id表示图像参数集所引用的序列参数集的序号。entropy_coding_mode_flag表示熵编码使用CAVLC还是CABAC。num_ref_idx_l0_active_minus1表示这个图像序列,有多少个参考帧。weighted_pred_flag表示是否预先p和SP片的加权预测(为了降低误差),weighted_bipred_flag是否允许B片加权预测。
H.264详解之一
H.264详解之一
H.264详解之一
Slice层一般会在编码图像后写入,语法如下面所示:
H.264详解之一
H.264详解之一
H.264详解之一
H.264详解之一
完成Slice输出后,就需要写入尾部。
H.264详解之一
slice头部输出,first_mb_in_slice表示第一个宏块的地址,如果在帧场自适应模式,宏块是成对出现,那真实地址是2*first_mb_in_slice。frame_num表示了各图像解码顺序,当gaps_in_frame_num_value_allowed_flag不为1,表示frame_num值是前一个参考帧的值加1,这个值也可以被当做图像是否丢弃的标志。field_pic_flag标志编码模式是帧编码,场编码,还是帧场自适应编码。idr_pic_id表示IDR图像的标识。direct_spatial_mv_pred_flag表示B帧是时间预测还是用户空间预测。
H.264详解之一
H.264详解之一
H.264详解之一
H.264详解之一
参考帧重排序,图像排序
H.264详解之一
H.264详解之一
宏块加权预测语法,如下图所示:
H.264详解之一
H.264详解之一
参考帧队列标记,如下图所示:
H.264详解之一
slice 数据语法,cabac_alignment_one_bit表示当为熵编码时,此时要求对齐,如果没有对齐,则会用若干个cabac_alignment_one_bit填充。mb_skip_run表示采用帧间预测编码,在图像平坦区域允许使用"跳跃"块,用来重新构建。
H.264详解之一
H.264详解之一
H.264详解之一
宏块层语法,mb_type表明不同宏块类型,I宏块、P宏块、B宏块、SI宏块、SP宏块。

H.264详解之一
宏块层预测语法
H.264详解之一
H.264详解之一
子宏块预测语法
H.264详解之一
H.264详解之一
残差语法,一般残差编码,包括所有残差(AC、DC)都编码、只有DC系数编码、所有残差(DC、AC)都不编码。
H.264详解之一
3.总结

H264给元素相互依赖和独立,既减少了冗余信息,提高编码效率,又增强鲁棒性,减少误码率。针对H264各个字段说明到此 结束,以上列举了重要的字段进行说明解释,后面会结合X264的开源代码来分析,进入真正的场景,敬请关注。另外,微信公众号和头条号也会同步更新,扫描下面的公众号即可查看。
H.264详解之一
H.264详解之一
头条号地址:https://mp.toutiao.com/profile_v3/index
微信公众号地址:https://mp.weixin.qq.com/cgi-bin/home?t=home/index&token=526916555&lang=zh_CN