视频编码原理讲解一:VCL层和NAL层的讲解
一.视频为什么要编码?
在我们做音视频产品的时候,经常会把音视频数据进行网络传输,而此时音视频数据就需要进行编码(所谓编码就是指压缩)。因为在网络传输的时候,网络带宽有限,若此时网络传输的时候还用原始数据进行传输的时候,则会对网络带宽造成极大的负担。比方说一个分辨率为1280 * 720 帧率为30帧的视频,按照YUV420格式的计算,它每秒传输的数据量就是1280* 720* 30 * 3/2 ~= 39.5M,这个数据量是极其惊人的。所以此时,我们就要引入视频编码技术来压缩视频,让其体积大小能够大幅度缩小,这样在网络传输的时候就会大大降低网络负担。在音视频开发中,一般分为H264、H265这两种最常见的编码格式。H264的压缩比能够达到1:100,H265的压缩比能够达到1:200,但是目前业内由于HEVC技术还没完全成熟,所以绝大部分设备都是用H264进行压缩处理。
二.VCL层和NAL层的讲解
H264编码格式是目前业内最流行的视频编码格式,它是MPEG-4的第十个部分。H264具有高压缩率、高图像质量、网络适应性很强等特点。在同等的图像质量下,H264的压缩比远超绝大部分编码格式(HEVC除外)。在H264(HEVC)编码框架中分为两大层,一层是VCL(Video Coding Layer)、另外一层是NAL层(Network Abstraction Layer)。VCL层主要负责内容的表示(如下图),NAL层主要负责对H264数据进行打包和传输,下面我们来重点介绍这两层里面包含的知识点。
2.1. VCL层的知识点:
VCL层包含了四个比较重要的知识点,分别是帧内预测压缩、帧间预测压缩、变换量化、熵编码。
帧内压缩:也称之为空间压缩,当压缩一张图片的时候,若仅仅考虑帧的数据也不考虑相邻帧和帧之间的冗余数据,这样的方式就叫帧内压缩。在H264中I帧生成的原理就是帧内压缩,帧内压缩可以独立解码出一帧完整的图像而不需要参考任何帧,帧内压缩表现出来的数据是最大16 x 16的宏块(它也包括:4x4,8x8)。帧内压缩本质上是在空间的XY轴上进行压缩,它的压缩率比较小。下面9张图是9种不同的预测方式
帧间压缩:帧间压缩是通过对比相邻两帧之间的数据进行压缩,并且进一步提高压缩量。用这种方法可以先编码出一个完整的图像1帧,紧接着2帧的数据就不编码出一张完整的图像,而是只写入和1帧的不同的数据,这样2帧数据的大小则会大大降低。以此类推,3帧的数据参考2帧数据,并且也只写入2帧不同的数据,帧间压缩表现出来的数据同样也是最大16 x 16的宏块(它也包括:4x4,8x8)。按照这样的方法,不停地循环下去。这样的方式,就是P帧、B帧的实现方法。
变换量化:
为了要让压缩的H264图像在网络传输中节省更多的码率,需要采用变换编码以及量化技术来消除图像信号中的相关性以及减小图像编码中的动态变换范围。变换编码的原理就是把图像时域信号变换成频域信号,在频域范围内,绝大部分信号集中在低频区域,相对时域信号,码率能够大幅度下降。而在H264中通常用下面方法进行处理:H264数据经过帧内压缩(16 * 16亮度、4 * 4亮度、8 * 8亮度)、帧间预测(4 * 4 ~ 16 * 16亮度)得到了残差值。这些残差值需要经过冗余的统计,并进行数据的变换和量化操作。
对于H264的16 x 16的亮度块,通过4x4的前向DCT变换,然后对16个DC系数再进行4 x4 的Hadmard变换,最后把这16个DC系数进行量化。
而对于8x8的色度块,则进行4x4的DCT变换后,得到4个DC和60个AC系数并对DC系数进行2x2的Hadmard变换。变换后则对DC系数和AC系数进行量化操作。
要值得注意的是,由于变换块越大,编码的效率越高,图像的还原度越好。所以为了让其编码质量得到更好的效果,在H264的高清(HD)档次,它支持8x8的DCT变换,并且不需要对DC系数进行Hadmard变换,DCT变换后则对DC和AC系数进行统一处理。
熵编码:
熵编码压缩是一种无损压缩模式,其实现原理是使用的编码模式来表示输入的数据,并达到压缩效果。常用的熵编码方式分别是变长编码(CAVLC)和算术编码(CABAC)。
变长编码(CAVLC):对出现概率大的符号,对出现概率小的符号提供长字节二进制码。
算术编码(CABAC):采用一个浮点数代替一串输入符号,范围是[0,1)
具体的参考CAVLC和CABAC简介_c avlc-CSDN博客
CABAC的编码压缩率远远大于CAVLC,CABAC具有更高的编码效果。
2.2. NAL层(Network Abstraction Layer):负责以网络所要求的方式进行打包和传送,下图是一个NAL层的组成部分(这里主要介绍H264码流的NAL,H265的后面会讲)
NAL层一般由三部分组成,分别是
StartCode(起始码:必须是00 00 00 01)
NALU Header(NALU头部)
NALU Payload(具体传输的视频内容)。
比方说一串H264码流的NALU:00 00 00 01 68 43 A0 25 56 2E....
其中:[00 00 00 01]是StartCode也就是起始码
[68] 是NAL头部
[43 A0 25 56 2E...]就是NALU Payload的数据
2.2.1. StartCode起始码:
起始码是所有H264码流开始时候的分隔符,一般分为0x000 001和0x000 000 01两种
2.2.2. NAL头部:
NALU头部长度为一个字节,它的语法是禁止位(1bit)、重要性位(2bit)、NALU类型(5bit)
NALU类型的图表:
比方说:NALU头部是0x68,转换为二进制数据就是0110 1000.
这表示,禁止位为0,NAL重要性为11(最重要),01000换算成HEX格式就是8也就是PPS。