从这篇开始,我们进入H264的学习实践,主要分三个阶段
- 学习H264基本结构;
- 了解具体编码压缩技术;
- 分析了解相关开源库 x264和h264bitstream。
这篇我们来一起学习H264的基本结构
目录
- H264/AVC的目标和方案
- H264分层结构- VCL和NAL
- NALU HEAD解析
- NALU payload
- I/P/B帧的特点
- 切片Slice和宏块
- 资料
- 收获
一、H264/AVC的目标和方案
音视频编码的标准由标准发展组织制定,主要两大组织:ISO(国际标准化组织和国际电工委员会)和ITU-T(国际电信联盟的电信标准化部门)
MPEG-1、MPEG-2、MPEG-4 part2、有ISO制定 H.261、H.262、H.263有ITU-T制定 H264/MPEG-4 part10 以及HEVC(h265) 有ISO和ITU-T等联合制定
H.264 的主要目标:
1)高的视频压缩比,当初提出的指标是比H.263,MPEG-4,约为它们的2 倍,现在都已基本实现; 2)良好的网络亲和性,即可适用于各种传输网络。 为此,H.264 的功能分为两层,即视频编码层(VCL)和网络提取层(NAL)。 VCL 数据即编码处理的输出,它表示被压缩编码后的视频数据序列。 在VCL 数据传输或存储之前,这些编码的VCL 数据,先被映射或封装进NAL 单元中。
h264编码的主要流程
压缩技术 H264/MPEG-4 part10 是运用比较广泛的编码标准协议 为了实现目标,H264在H263的基础上增加了如下压缩技术
代码语言:javascript复制1. 双向运动补偿
2. 以小块进行的可变块运动补偿
3. 四分之一像素运动补偿
4. 环路滤波器
5. 变长编码
6. 加权预测
7. 可伸缩视频编码
8. 多视点编码等
二、H264分层结构- VCL和NAL
上一小节,我们为了实现H264的两个目标,H264功能上进行了分层,视频编码层(VCL, Video Coding Layer)和网络提取层(NAL, Network Abstraction Layer)。 其中,VCL(Video Coding Layer)视频编码层,包括核心压缩引擎和块、宏块和片的语法级别定义,设计目标是尽可能地独立于网络进行高效的编码,负责有效表示视频数据的内容。 而 NAL(Network Abstraction Layer)网络提取层,负责将 VCL 产生的比特字符串适配到各种各样的网络和多元环境中,覆盖了所有片级以上的语法级别;一个NALU 单元常由 [NALU Header] [NALU Payload] 部分组成, NAL对VCL进行了封装包裹
图片来自:VCL & NAL (H.264/AVC)
三、NALU HEAD解析
为了分析H264,我们先通过如下命令 提取视频
代码语言:javascript复制保留编码格式:ffmpeg -i test.mp4 -vcodec copy -an test_copy.h264
强制格式:ffmpeg -i test.mp4 -vcodec libx264 -an test.h264
然后用010Editor打开提取的h264文件,如下所示:
H264 分成两种流格式,一种是 Annex-B 格式(上图看到的就是这种格式),一种是 RTP 包流的格式。 Annex-B 格式是默认的输出格式。数据单元的分割使用[StartCode] (0x000001或0x00000001 )作为起始码。
NALU 单元常由 [NALU Header] [NALU Payload] 组成,
NALU Header是紧随StartCode后的一个字节,按照位划分三块。
其中第1位,表示禁止位,为1禁止使用该NALU单元,为0可以使用。 第2-3位是参考级别(NRI,NAL ref idc)表示重要性,值越大说明越重要。比如在做丢帧处理时,就是通过这两位来判断该帧是否被依赖,进而决定是否可以被丢弃。 后面的5位表示NLAU的类型,其值的含义具体见下表
图片来自:https://zhuanlan.zhihu.com/p/71928833
我们可以看到NAL类型分为两类,VCL和非VCL。其中 包含图像数据的unit属于VCL NAL units; SPS、PPS、和SEI 属于Non-VCL NAL Units;
下面我们分别看下 我们的h264截图中的 06、67、68代表什么意思?
0x06 --二进制化--》00000110 --取后五位--》000 00110 值位6 ,查看上表,发现是SEI即补充增强信息单元,可以向视频码流中加入额外信息的方法
0x67 --二进制化--》01100111 --取后五位--》000 00111 值位7 ,查看上表,发现是SPS 即序列参数集,保存了一组编码视频序列(Coded Video Sequence)的全局参数
0x68 --二进制化--》01101000 --取后五位--》000 01000 值位8 ,查看上表,发现是PPS 即图像参数集,该类型保存了整体图像相关的参数。
除了上面这些,更多的是00 00 00 01 41,或者00 00 00 01 01那么41、01又是什么呐?
0x41 --二进制化--》01000001 --取后五位--》000 00001 值位1 ,查看上表,发现是非IDR帧,可以是 I/P/B帧
其中IDR是一种I帧,告诉解码器,之前依赖的解码参数集合可以被刷新了
0x01 --二进制化--》00000001 --取后五位--》000 00001 值位1 ,查看上表,发现是非IDR帧,可以是 I/P/B帧 ,相比41,这个帧的重要性很低,可以丢弃
四、NALU payload
NALU的主体涉及到三个重要的名词,分别为EBSP、RBSP和SODB。其中EBSP完全等价于NALU主体,而且它们三个的结构关系为: EBSP包含RBSP,RBSP包含SODB。
1. SODB: String Of Data Bits 原始数据比特流,就是最原始的编码/压缩得到的数据
2. RBSP: Raw Byte Sequence Payload,又称原始字节序列载荷。
和SODB关系如下:
RBSP = SODB RBSP Trailing Bits(RBSP尾部补齐字节) 引入RBSP Trailing Bits做8位字节补齐。
3. EBSP: Encapsulated Byte Sequence Payload: 扩展字节序列载荷
如果RBSP中也包括了StartCode(0x000001
或0x00000001
)怎么办呢?所以,就有了防止竞争字节(0x03
). 编码时,扫描RBSP,如果遇到连续两个0x00
字节,就在后面添加防止竞争字节(0x03
);解码时,同样扫描EBSP,进行逆向操作即可。
图片来自:视频和视频帧:H264编码格式整理
图片来自:H264/AVC 句法和语义详解(三):NALU详解二(EBSP、RBSP与SODB
五、I/P/B帧的特点
通过第三小节我们通过 NAL的Type表了解到。5代表IDR帧、1代表非IDR帧。这一小节,我们就来了解下视频的I/P/B帧。
I帧:帧内编码帧 intra picture,关键帧,I 帧通常是每个 GOP的第一个帧,进行帧内预测压缩,作为P/B帧的参考帧。 I帧的特点如下:
代码语言:javascript复制 它是一个全帧压缩编码帧。它将全帧图像信息进行JPEG压缩编码及传输;
解码时仅用I帧的数据就可重构完整图像;
I帧描述了图像背景和运动主体的详情;
I帧不需要参考其他画面而生成;
I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量);
I帧是帧组GOP的基础帧(第一帧),在一组中只有一个I帧;
I帧不需要考虑运动矢量;
I帧所占数据的信息量比较大。
P帧:前向预测编码帧 predictive-frame,主要进行帧间编码,参考前面的I/P帧,去除时间冗余信息,P帧没有完整画面数据,只有与前面I/P的画面差别的数据。解码时需要用之前缓存I/P帧叠加上本帧的差异数据,生成最终画面 P帧的特点如下:
代码语言:javascript复制 P帧是I帧后面相隔1~2帧的编码帧;
P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差);
解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像;
P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧;
P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧;
由于P帧是参考帧,它可能造成解码错误的扩散;
由于是差值传送,P帧的压缩比较高。
B帧:双向预测帧 bi-directional interpolated prediction frame,考虑前面的I/P帧以及后面的P帧之间的时间冗余信息来压缩传输数据量的编码图像;要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU消耗多些。 B帧特点如下:
代码语言:javascript复制 B帧是由前面的I或P帧和后面的P帧来进行预测的;
B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量;
B帧是双向预测编码帧;
B帧压缩比最高,因为它只反映参考帧间运动主体的变化情况,预测比较准确;
B帧不是参考帧,不会造成解码错误的扩散。
GOP:两个I帧之间是一个图像序列,主要用作形容一个 i 帧 到下一个 i 帧之间的间隔了多少个帧,一个序列的第一个图像是 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。 增大GOP图片组能有效的减少编码后的视频体积,但是也会降低视频质量。 GOP 越长,B 帧所占比例更高,编码性能要求也越高。
使用H.264 Video ES Viewer工具打开一个test.264文件 查看每个NAL的类型以及VCL数数据大小以及帧类型。
六、切片Slice和宏块
GOP、帧、片、宏之间的关系
片的主要作用是用作宏块(Macroblock)的载体, 目的是为限制误码的扩散和传输。 如何限制误码的扩散和传输? 每个片(slice)都应该是互相独立被传输的,某片的预测(片内预测和片间预测)不能以其它片中的宏块(Macroblock)为参考图像。
每个分片也包含头和数据两部分:
分片头中包含着分片类型、宏块类型、帧的数量、分片属于那个图像以及对应的帧的设置和参数等信息。 分片数据中则是宏块,这里就是我们要找的存储像素(YUV)数据的地方
什么是宏块
宏块是视频信息的主要承载者,因为它包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中的像素阵列。
组成部分:一个宏块由一个16×16亮度像素和附加的一个8×8 Cb和一个 8×8 Cr 彩色像素块组成。每个图象中,若干宏块被排列成片的形式。
常用的宏块类型 I宏块:采用帧内预测宏块,可能位于I/P/B帧(因为在P和B帧中也是可以进行帧内预测的) P宏块:采用单向帧间预测,只存在于P帧 B宏块:采用双向帧间预测,只存在于B帧
切片(slice)类型跟宏块类型的关系
I片:只包 I宏块,I 宏块利用从当前片中已解码的像素作为参考进行帧内预测(不能取其它片中的已解码像素作为参考进行帧内预测)。 P片:可包 P和I宏块,P 宏块利用前面已编码图像作为参考图象进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割:即 16×16、16×8、8×16 或 8×8 亮度像素块(以及附带的彩色像素);如果选了 8×8 的子宏块,则可再分成各种子宏块的分割,其尺寸为 8×8、8×4、4×8 或 4×4 亮度像素块(以及附带的彩色像素)。 B片:可包 B和I宏块,B 宏块则利用双向的参考图象(当前和 来的已编码图象帧)进行帧内预测。 SP片(切换P):用于不同编码流之间的切换,包含 P 和/或 I 宏块 SI片:扩展档次中必须具有的切换,它包 了一种特殊类型的编码宏块,叫做 SI 宏块,SI 也是扩展档次中的必备功能。
我们通过H264Visa工具来查看h264的 NALU、Slice、marcoblock、以及YUV数据如下:
七、资料
本篇的内容很多来下面的参考资料的学习,结合自己的理解进行整理和描述,以及通过码流分析工具进行查看分析。感谢如下作者的输出。
- 图书 《视频编码全角度详解》
- 图书 《新一代视频压缩编码标准 — H.264/AVC》
- 李超-H264基本原理(https://zhuanlan.zhihu.com/p/31056455)
- 深入浅出理解视频编码H264结构(https://philm.gitbook.io/philm-ios-wiki/mei-zhou-yue-du/shen-ru-qian-chu-li-jie-shi-pin-bian-ma-h264-jie-gou)
- 视频和视频帧:H264编码格式整理(https://zhuanlan.zhihu.com/p/71928833)
- H264编码总结(https://www.jianshu.com/p/0c296b05ef2a)
- VCL & NAL (H.264/AVC)(https://www.jianshu.com/p/eeecb0eb2c6e)
八、收获
通过本篇的学习
- 了解H264的结构 VCL和NAL的分层
- 了解NALU HEAD对应的一个字节代表的含义和类型,SPS、PPS、SEI、IDR、非IDR等,以及了解I/P/B帧的特性
- 了解NALU PayLoad的结构
- 了解对帧分片和宏块的定义和目的。
- 通过码流分析工具结合实践更好的理解。
感谢你的阅读 下一篇我们分析学习H264的编码技术之帧内预测,欢迎关注公众号“音视频开发之旅”,一起学习成长。 欢迎交流