3-1 视频技术知识
I帧:举个例子,假设有一个摄像头对着我们, 一秒钟大概会抓取几十帧的数据,比如我们的动画,就要保证每秒钟25帧以上,一般的视频文件都是在30帧左右,对于清晰度比较高的,每秒钟在60帧以上。 对于一组帧它的变化很小,为了压缩数据变小,我们会将第一帧完整的保存下来,这一帧就是我们的关键帧,称为I帧,如果没有第一帧,我们后面所以帧解码的时候都会出现问题。
P帧:除了第一帧,后面的所有帧都是向前依赖的,所有后面所有帧都只存它与前一帧的差异,这样可以将数据大大减小,从而达到一个高压缩率的效果。
B帧: 前后帧都参考,只存储前后帧都没有的差异,存储的信息更小。缺点是如果是实时互动的场景,想要压缩率更高,就要参考后面这一帧,想参考后面这一帧,在网络中,就要等待后面一帧到达,这就与网络息息相关,如果网络非常好的话,那解码更快一些,如果网络不好,还要丢包,丢包就要重传。这个时候解码器就要一直等待,等待的长短只能根据当时的网络情况。所以对于实时互动技术来说,一般不会使用B帧,因为时间控制不可控。
Group OF Frame 就是一组帧,如果在一秒钟之内有30帧,这30帧可以化为一组,如果说摄像机捕获这个镜头一分钟之内没有什么变化,那么我们也可以把这一分钟之内的所有帧称为一组帧,如上图所示,就是在两个I帧之间的所有帧,包括B帧和P帧,称为一组帧 GOF。
这两个都是存 GOF的参数,在一组帧之前,我们首先会收到SPS与PPS,如果没有这两个参数,我们是无法解码的。如果在编解码中收到一组帧,发现这组帧出现问题了,就要首先看看有没有SPS和PPS,如果没有,可能就是对端没发出来,或者是发送的时候丢失了。SPS和PPS我们也将其化为I帧,这两组数据是绝对不能丢的。
视频经常会卡顿,这是怎么产生的呢?这就与我们刚才讲的GOF息息相关了。为了避免花屏, 丢掉了P帧或者I帧丢失的GOP,就会出现卡顿。
x264/x265: x264是目前使用最广泛的H264的编解码器,性能特别优秀,如果是用软编的话,基本都是用X264,x265现在也逐渐成熟,但是在我们的直播系统里,因为它的压缩比更高,占用的CPU也非常高,所以这个目前基本上是不可用的。但在点播系统里还是可以尝试使用的。
openH264:相对于x265性能要低一些,但是它支持SVC视频技术,简单的说就是将视频分层传输,就是一帧数据分为小中大传输,如果网络差的情况下,只发最小的,内核的帧,如果网络稍微好点的话,就将中间的帧也发出去,如果网络非常好,那么三层全部发出去。发到对端之后将三层叠加在一起,就形成了原来的视频。如果只发送了内核层,就可以看到图像的一个大概信息,但是非常不清晰,每加一层就清晰一次。但是在移动端,由于SVC不是标准的,很多硬件都不支持,所以如果要使用SVC技术,那移动端就不能使用硬件编码,只能使用软件编码。使用软编对CPU还是有很大消耗的,就会发烫,耗电,带来一系列问题,所以这就是一个取舍。
vp8/vp9:谷歌推出的技术,对应于x264和x265
3-2 H264宏块的划分和帧分组
什么是空域数据呢,比如说一幅图,宽高形成了这幅图,在这里面有很多色彩,颜色,光亮是人眼不是很敏感的,对于这些数据,我们称其为冗余数据,是可以将其直接删除掉的,这些数据就是空域数据。
什么是时域数据呢,比如说一个摄像头捕获了很多视频,在一组视频之间,帧的相关性是非常高的,但由于它的数据基本上都是重复的,所以我们可以把这些重复的数据全部删除掉。这种数据就是时域冗余数据。
什么是DCT呢,这是和傅立叶变换有关系的。我们知道傅立叶变换可以将一个复杂的波形图变成许多的正弦波,就是它的频率不一样,它的振幅也不一样,一旦将他们变成频域上的数据,他们的数据没有相关性,那么我们就可以对这些数据做相关的处理。
什么是CABAC压缩呢?它也被称为无损压缩,和我们之前经常所说的哈夫曼编码思想是一致的,只不过比它压缩率更高。
左右两张图,对于左边这张图,我们拿出了它的左上角,红色框起来的部分就是一个宏块,它是一个8x8的宏块,也就是横着是8个像素,竖着也是8个像素,取出来就用右图来表示,比如有多少红色的,有多少紫色的等
将一张图片整个用宏块划分,划分万之后的结果就是上图所示,上边的部分就是用宏块划分后的数据,底下的部分就是原始的,这样划分之后整个图就做完了第一步的处理
并不是每个宏块都是8x8的,它还有子块,左边这张图是一个16x16的宏块,这个宏块里有3块黑色,为了更好的压缩,H.264会给它划分成更小的,为了减小编码,可以划分成4x8的,4x4的,划分完了之后就变成了右边这张图,H.264就变成了比较细的划分,而MPEG2 就比较粗,相对来说H.264效率要高很多。
在一组帧内,物体的变化是很小的,可以把这些帧划分为一组,通过预测,可以预测出下一帧
3-3 视频压缩技术详解
什么是组内宏块查找,按照上图所表达的意思, 一组帧,内容是一个台球从一个角滚到另一个角,将这组帧中相邻的两幅图做组内的宏块查找, 也就是说,我们要逐行扫描,扫描每一个宏块,扫到第三行的时候就会发现一个台球,当它发现台球之后,就会在周围找是否有类似的宏块
如果找到了,就变成了上图左边所示,将他们放到了一张图里,这张台球刚开始有一个初始位置,到了第二幅图里面,实际上有一个运动矢量,即他们之间有一个运动的方向和运动的距离。将所有这些图都进行两两比较,最后就形成了右边这张图,就是红色的部分,每个红色的部分都是一个运动矢量,很多帧就形成了一个连续的运动估算
形成运动估算之后,我们要达到的结果就是上图所示,我们要对其进行压缩,因为所有帧的背景都是一样的,
变化的就是运动矢量和台球的数据,压缩之后,就留下了运动矢量的数据加上残差的数据,经过这样的计算,在帧间压缩数据的时候,实际上我们只需要存一点点数据,而不是像以前,要将几十帧的数据全部存下来,这就达到了压缩的效果。 这就叫做帧间压缩技术。
帧内的压缩是针对域I帧的,因为它解决掉是空间的数据冗余,而帧间的压缩是解决的时间都数据冗余。
首先看这张图的左边部分,意思是说,它要先经过一个计算,然后选用我想要用那种模式,对每个宏块进行不同的模式计算,选择一个帧内预测的模式,当选择了之后,就形成了右边的部分。
抽检出模式之后,右图所示,通过每个宏块使用的块预测模式,预测结果就是右图右边所示,而右图左边就是
它的原图,预测出的图和原图之间是有差别的,看一看到,原图比较华润,而预测的图则比较粗糙。现在两幅图都有了,我们就要做两幅图之间的差计算。上图左边只是原图的一个角,而右图地下的部分就是原图,
通过预测得到的差几十右图中上面黑色的框,这就叫残差值。
拿到残差值之后,进行压缩, 压缩的时候只存残差数据和选择的每个模式信息的数据,有了这两个数据,当我们解码的时候,首先根据预测模式信息,恢复出一副我们预测出的图出来,然后再将预测的图与我们的残差值进行累加,就能还原出我们原始的图像了。
DCT也就是整数余弦变换,它是怎么压缩的呢?上图左边我们有一个宏块,这个宏块我们可以将其进行量化,量化之后,量化之后,右图通过DCT的数学方法进行压缩。
压缩完了之后,就变成了只有我们左上角这边有数据,而右下角就都是空了。这样就达到了一个数据减小的目的。
VLC压缩实际上就和我们之前讲的哈夫曼编码是相同的道理,她把A频率高的定位短码11,把Z频率低的定为一个长码,通过我们VLC压缩之后,就变成了一个无损压缩的数据,右图所示,VLC实际上是MPEG2使用的技术,而对于H.264 使用的是VABAC,也就是上下文适应的压缩技术,除了类似哈夫曼编码的高频使用短码低频使用长码的常规压缩之外,还有一个上下文适用,根据上下文就可以进行压缩比更强的压缩。
上图所示,对于一副图,如果是MPEG2的规范,我们压缩出是一块一块的数据,但如果是H.264的CABAC压缩技术,我们会开始是一个一个大块,随着压缩时间产生上下文之后,就可以进行进一步的压缩,将以前的一个个大块,压缩成一个个小块。
3-4 H264结构与码流
上一节我们对H264的压缩技术做了详细的分析,通过压缩技术之后我们形成了一帧帧的H264的视频帧,每一个视频帧是一个结构化的东西
上图所示是压缩后的一帧帧的视频帧序列,我们取其中的一帧,可以看到每一帧都是有多个片,而每个片又是由一个个宏块组成,每一个宏块又可以分成多个子块,它的结构就是一副压缩后的H264的视频帧是由多个片组成,每个片是由多个宏块组成。
H264在编码的时候分成了两层,第一层为网络抽象层,我们的H264最终还是要在网络上进行传输,在传输的时候,由于我们每个包的最大传输单元以太网是1500字节,而一个H264的帧往往是大于1500字节的,所以需要将一个帧拆成多个包进行传输,而所有的拆包组成等这些处理都是通过HAL层来处理的。第二层为 视频编码层,它的作用是对视频的原始数据进行压缩。
接下来看码流,首先是SODB,即原始数据比特流,由VCL层产生,由于不是8点整数倍,所以处理起来比较麻烦。所以有了RBSP,因为生成的SODB是一个流,我们并不知道它结束在哪,为了知道它的结束,所以我们在末尾给它补一个1,如果补了1之后它不是8个字节的,我们再给它补多个0,使之成为8位对齐的,这就是RBSP。
EBSP就是我们在生成压缩流之后,还要在每一个帧的开头加一个起始位,这个起始位一般是16进制的00000001,根据H264的规范,如果在压缩的数据里遇到了连续的两个0,那么就要在后面插入一个03,这样就防止了与起始标记产生冲突,改变完了之后,数据称做EBSP。
NALU即在EBSP的基础上,加上一个字节的网络头。
接下来我们看看NALU单元,一个NALU单元是又一个NALU的头部加上一个H264的切片组成,切片又可以细分成切片头和切片数据,一个H264的帧最少要有一个切片,在网络传输的时候,需要将H264的帧拆开进行传输,就按照切片来切,如果H264点帧里面有多个切片,每个切片组成一个NALU单元。
接下来看切片与宏块的关系,我们刚才看到了,每一个切片都包括了切片头和切片数据,每个切片数据里面又包括了很多宏块,每个宏块里面又包括了宏块的类型、宏块的预测和编码残差数据,这就是切片与宏块的关系。
正如我们刚才讲到的,在一幅压缩的H264帧里面,可以有多个切片,至少有一个切片
上图是一个总体的码流分层,一个ANNESB格式的数据,是由起始码,加上后面的一个NAL单元,而一个NAL单元包括了一个NALU header和一个 NALU body,在body里有一个切片,这个切片包括了切片头和切片的数据,在切片的数据里又包括了多个宏块,在宏块里存的数据包括宏块类型,宏块数据还有宏块的参数等等残差数据,可以有两种类型,在残差数据里又分为了残差块。另外从网络上来说,是一个RTP包,RTP包里包括了NLU header和 NLU body,下面都一样。这就是H264的码流结构图。
3-5 NAL单元详解
大家知道,NAL是由NAL header和NAL body组成的其中Header占一个字节,这一个字节正如上图所示,0-7一共8位,由3部分组成。
F为禁止位,无法改变
NRI:指示NAL的重要性,00是最不重要的,11是最重要的。实际上没啥用
Type:类型
这里列举几个类型:
5是IDR图像的片,是关键帧的一部分,7序列参数集就是我们的IPS,指导一个序列的图像,也就是GOF,8图像参数集就是PDS,帧内预测。28 ,29 叫做分片的单元,就是一个H264的帧,通过网络传输,一个网络包最大1500字节,存不下就要将其切成多个片,每一个包就是 一个片,最后将其组装。
单一类型:一个RTP包里包含一个完整的帧,叫做单一类型。像P帧,B帧很多都是单一类型。
下面重点介绍一下FU的header,它也是一个字节,由4部分组成,
例如S位,我们在网络传输的时候,一个网络包,我知道它是一个分片了,但是怎么知道它是中间的包还是末尾的包还是开头的包呢?就是通过S位,如果为1,就是说明它是一个分片的开始。
3-6 YUV讲解