所谓的码流结构就是指:视频经过编码之后所得到的数据是怎样排列的,换句话说,就是编码后的码流我们该如何将一帧一帧的数据分离开来,哪一块数据是一帧图像,哪一块是另外一帧图像,只要了解了这个,后面的事情就好办了。
1.H264帧的定义
在H264编码中有I帧,p帧和B帧。每一帧都相当于一张静止的图像。在实际的码流传输中会利用编码器来压缩图像以减少视频的体积,其中I帧、P帧、B帧最为常见。
I 帧: I 帧通常又称之为内部画面,它通常是视频编码的第一帧。它的最大特点是自带一个完整的图像信息,在解码的过程中只需要解码本帧就可以完整地提取出一个完整的画面。假设一个视频中丢失了I 帧,则整个视频则会处于黑屏状态,后面的视频则无法正常播出。由此可见, I 帧在视频编码中扮演着相当重要的角色。但是它也有自身的缺点,那就是I 帧的体积比较大,假设在传输视频中全部采用 I 帧去传输,那整个网络链路都承受着巨大的压力。 所以,I 帧就要配合 P 帧、 B 帧等进行数据的传输。P 帧: P 帧又称之为前向参考帧,此帧的特点是需要参考前一帧的图像信息才可以正确把图像解码出来。 P 帧指的是这一帧和前一帧的差别,并通过将图像序列中已经编码后的冗余信息充分去除来压缩传输数据量的编码图像。B 帧: B 帧也称之为双向参考帧, B 帧的特点是以前面的帧 (I 帧或者 P 帧 ) 或者后面的帧 ( 也是 I 帧、 P 帧 ) 作为参考帧找出 B 帧的预测值,并且取预测差值和预测矢量进行传送。所以在拉流端解码B 帧的时候不仅需要获得前面的缓存视频,还需要获得后面的缓存视频才能够正常解码 B 帧。所以, B 帧虽然压缩率更高,但是更消耗CPU 资源
2.GOP(I帧间隔)
GOP 指的是两个 I 帧之间的距离,在一个 GOP 包含了一组连续的图片。在一个 GOP 中包含了 I 帧、 P 帧、 B 帧,直到下一个 I 帧的出现,一个 GOP 才算结束。通常来说,I 帧所占用的字节和体积大于 P 帧、而 P 帧所占用的字节大于 B 帧。所以在码率不变的情况下,可以调整 GOP 的长度去改善画质, GOP的长度越长,所得到的P 帧和 B 帧更多,画面的质量和细节就会更好。
3.Slice片
一帧图像可以分成若干片,每一片又可分成若干个宏块。宏块是视频的最小单位。
为了并行编码,所以引入了片的概念,所谓的并行编码就是把一帧图像分成几个片,每个片相互独立进行编码。
4.H264的码流格式
H264的码流格式可以分为两类,不同的码流格式,对于编码器的处理方式不同
1.Annexb格式:也叫字节流格式,字节流格式使用起始码来表示一个编码数据的开始,起始码不是图像数据的一部分,只起到一个分隔图像的作用。大部分的编码器默认输出的是字节流格式的,字节流格式也叫做H264裸流。字节流格式的基本数据单元是NAL(也叫NALU)单元,为了从字节流中提取出NAL单元,协议规定,每个NAL前必须加上一个开始码
0x00000001(4字节)或0x000001(三字节)
这里要注意一下,NAL中的h264数据也有可能包含0x00000001或0x000001,一般对NAL的这种情况会做出如下的处理:
(1)00 00 00 修改成00 00 00 03 00(2)00 00 01 修改成00 00 00 03 01
(3)00 00 02 修改成00 00 00 03 02
(4)00 00 03 修改成00 00 00 03 03
即只能出现连续两个字节的 00 00
所以基于字节流的H264裸流=起始单元+NALU + 起始单元 + NALU +............(其实这个起始单元也是NALU的一部分,为了理解所以写成这样)
2.MP4格式,MP4格式没有起始单元,但是MP4编码中使用了4字节长度作为标识,用来表示编码数据的长度。每次读取4字节数据计算出编码数据的长度,然后取出编码数据,再读取4字节,计算出数据长度,然后取出数据,这样一直计算下去就可以取出编码数据了。
5.基于字节流的H264裸流的组成结构
H264裸流分层可以分成两层,一层是VCL层(视频编码的底层),另外一层是NAL层(网络提取层也叫NALU),VCL层包含的是H264的视频内容,NALU负责的是把网络视频进行打包和传输,对于开发人员我们一般是不关心CVL层的,我们关注的重点一般都是NALU
结构如图所示:
SODB(数据比特串):就是VCL层,是最基本的编码数据,没有包含任何的附加信息。
RBSP(原始字节序列载荷):在SODB的后面加上了结尾比特,一个bit1和若干个bit0,用来字节对齐
EBSP(扩展字节序列载荷):在RBSP的基础上添加了仿效字节(0x03).这样做的原因上面也介绍过,因为NALU的起始数据是 00 00 00 00 01或 00 00 01,防止NALU中的其他数据也出现 00 00 00 01 或 00 00 01,所以NALU会把连续的两个 00 00后插入一个0x03,在解码时,会自动的把加入的0x03丢掉,这个操作叫做脱壳操作。
如图所以,画框的都是一帧NALU单元,我们介绍一下NALU数据的特殊意义:
00 00 00 01 06 05这是SEI数据,是视频的附加信息,包含了用户的自定义信息,比如时间戳,字幕和弹幕信息等。SEI信息一般放在编码图像之前,很多时候SEI是可以忽略的
00 00 00 01 67这是SPS数据,这指的是序列参数集,它保存了一组编码视频序列的全局参数。编码视频序列指的是原始数据经过编码后组成的一系列序列集。
00 00 00 01 68PPS数据,这指的是图像的参数集,主要用于保存图像序列集中一个或多个独立的图像,一般情况下,配合SPS和PPS都是H264开头的两个NALU头。
00 00 00 01 65IDR数据,IDR指的是H264一帧完整的图像数据,也就是常说的关键帧。
所以一个标准的H264码流结构一般是:SEI+SPS+PPS+IDR
6.I帧和IDR帧的区别和联系
IDR属于I帧,但是I帧不一定是IDR帧。只有IDR帧,才有SPS和PPS。解码器收到IDR帧时,将缓存清空;而收到I帧不会清空缓存。也就是说,对某个IDR帧之后的帧,解码器不会参考这个IDR帧之前的任何帧做解码。对某个I帧之后的帧,解码器可能会参考这个I帧之前的帧做解码。