参考帖子:https://blog.csdn.net/go_str/article/details/80340564?spm=1001.2014.3001.5502
0、序言:
首先要弄明白编码的目的。当你此刻显示器正在播放一个视频,分辨率为1280*720,帧率为25,那么一秒所产生正常的数据大小为:1280*720(像素)*25(张)/8(1字节8位)(结果:B)/1024(结果:KB)/1024(结果:MB)=2.75MB。显然一秒这么大的数据是无法接受的,因此需要对数据进行压缩。
1、前言:
H264在视频采集到传输中属于编解码层次的数据,是在采集数据后做编码压缩时通过编码标准编码之后呈现的数据。
2、H264相关概念:
2.1序列:
H264编码标准中所遵循的理论依据个人理解成:参照一段时间内相邻的图像中,像素、亮度、色温的差别很小,所以当面对一段时间内图像我们没必要对每一幅画面进行一次完整一帧的编码,而是选取这段时间内的第一帧图像作为完整编码,下一幅图像可以记录与第一帧完整编码图像像素、亮度、色温的差别即可,以此类推循环下去。
什么是序列呢,上述的这段时间内图像变化不大的图像集我们称之为一个序列。序列可以理解为有相同特点的一段数据。但是如果某个图像与之前的图像变换很大,很难参考之前的帧来生成新的帧,那么就结束删除一个序列,开始下一段序列。
2.2帧类型:
H264结构中,一个视频图像编码后的数据叫做一帧,一帧由一个片(slice)或多个片组成,一个片由一个或多个宏块(MB)组成,一个宏块由16*16的yuv数据组成。宏块作为H264编码的基本单元。
在H264协议内定义了三种帧,分别是I帧、B帧和P帧。I帧就是之前说的一个完整图像帧,而B帧和P帧所对应的就是之前说的不编码全部图像的帧。P帧与B帧的差别是P帧是参考之前的I帧和P帧生成的,而B帧是参考前后图像帧编码生成的。
2.3GOP(画面组):
GOP我理解为和序列差不多的意思,就是一段时间内变化不大的图像集。GOP结构一般有两个数字,如M=3,N=12。M指定I帧与P帧之间的距离,N指定两个I帧之间的距离。如上M=3,N=12,GOP的结构为:IBBPBBPBBPBBI。在一个GOP中I帧解码不依赖任何其它帧,P解码依赖前面的I帧或P帧,B帧解码依赖最近的I帧或P帧及其后最近的P帧。
2.4IDR帧:
在编码解码中为了方便,将GOP中首个I帧要和其它I帧区别开,把第一个I帧叫IDR,这样方便控制编码和解码流程,所以IDR帧一定是I帧,但I帧不一定是IDR帧。IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始算新的序列开始编码。I帧有别跨越参考的可能,IDR帧不会。
作用:H264引入IDR帧是为了解码的重同步,当解码器解码到IDR帧时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参考集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会,IDR帧之后的图像永远不会参考IDR帧之前图像的数据来解码。
3、H264压缩方式:
H264采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成I帧的算法,帧间压缩是生成P帧和B帧的算法。
帧内压缩也称空间压缩。当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧的冗余信息,这实际上与静态图像压缩类似,帧内一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像,所以可以独立的解码、显示。帧内压缩一般达不到很高的压缩,和编码jpeg差不多。
帧间压缩也称时间压缩。相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小。即连续的视频帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步增加压缩量,减少压缩比。
3.1压缩方式说明:
Step1:分组,将一系列变化不大的图像归为一个组,也就是一个序列,也可以叫GOP(画面组)
Step2:定义帧,将每组的图像帧规分为I帧、P帧和B帧三种类型
Step3:预测帧,以I帧作为基础帧,以I帧预测P帧,以I帧和P帧预测B帧
Step4:数据传输,最后将I帧数据和预测的差值信息进行存储和传输。
4、H264分层结构:
H264的主要目标是为了有较高的压缩比和良好的网络亲和性,为了达到这个目标,H264的解决方案是将系统框架分为两个层面,分别是视频编码层(VCL)和网络抽象层(NAL)。如图:
VCL层是对核心算法引擎、块、宏块及片的语法级别的定义,负责有效表示视频数据的内容,最终输出编码完的数据SODB
NAL层定义了片级以上的语法级别(如序列参数集参数和图像参数集),负责以网络所要求的恰当方式去格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。NAL层将SODB打包成RBSP然后加上NAL头组成一个NALU单元。
这里说一下SODB与RBSP的关联。SODB:数据比特串,是编码后的原始数据;RBSP:原始字节序列载荷,是在原始编码数据后面添加了结尾比特,一个bit"1"和若干bit"0",用于字节对齐。
5、H264码流结构:
经过编码之后的H264码流如图所示,H264码流是由一个个的NAL单元组成,其中SPS、PPS、IDR和SLICE是NAL单元某一类型的数据。
6、H264的单元:
6.1 H264的NAL结构
在实际的网络数据传输中,H264的数据结构是以NALU进行传输的,传输数据结构组成为【NALU Header】+【RBSP】。
从之前的分析我们知道,VCL层编码后的视频帧数据,帧有可能是I/B/P帧,这些帧也可能是属于不同的序列之中,同一序列也还有不同的序列参数集(SPS)和图像参数集(PPS)。综上所述,想要准确无误进行视频的解码,除了需要VCL层编码出来的视频帧数据,同时还需要传输序列及和图像参数集等等,所以RBSP不单纯只保存I/B/P帧的数据编码信息,还有其它信息也可能出现在里面。
上面知道NAL单元是作为实际视频数据传输的基本单元,NALU头是用来标识后面RBSP是什么类型的数据,同时记录RBSP数据是否会被其他帧参考以及网络传输是否有错误。
6.2 NAL头
一、NAL头的组成
NAL单元的头部是由forbidden_bit(1bit),nal_reference_bit(优先级)、nal_unit_type(5bit)(类型)三个部分组成的。
F:禁止位,占用NAL头的第一位,当禁止位值为1时表示语法错误
NRI:参考级别,占用NAL头的第二到第三位,值越大,该NAL越重要
Type:NAL单元的数据类型,也就是标识该NAL单元的数据类型是哪种,占用NAL头的第四到第八位
一、NAL单元数据类型:
NAL类型主要就是下面图中所有这些类型,每个类型都有其特殊的作用:
在具体介绍NAL数据类型之前,有必要知道NAL分为VCL和非VCL的NAL单元。其中SPS、PPS、SEI等非VCL的NAL参数集对解码和显示视频都是很有作用的。
另一个需要了解的概念就是参数集(Parameter sets),参数集是携带解码参数的NAL单元,参数集对于正确解码是非常重要的,在一个有损的传输场景中,传输过程中比特列或包有可能丢失或损坏,在这种网络环境下,参数集可以通过高质量的服务来发送,比如向前纠错机制和优先级机制。Parameter sets与其之外的语法元素之间的关系图如下:
每种类型都有代表一种数据类型,比较重要的以下几种做个简单介绍:
1、非VCL的NAL数据类型:
1)SPS(序列参数集):SPS对如标识符、帧数、参考帧数目、解码图像尺寸、帧场模式等解码参数进行标识记录。
2)PPS(图像参数集):PPS对如熵编码类型、有效参考图像的数目和初始化等解码参数进行标志记录。
3)SEI(补充增强信息):这部分参数可作为H264的比特流数据传输,每一个SEI信息被封装成一个NAL单元。SEI对于解码器来说可能是有用的,但是对于基本的解码过程来说,并不是必须的。
@:先标记一下,SPS、PPS内容是编码器给出的
2、VCL的NAL数据类型:
1)头信息块,包括宏块类型,量化参数,运动矢量。这些信息是最重要的,因此离开他们数据块中的码元都无法使用,该数据分块称为A类数据分块。
2)帧内编码信息数据块,称为B类数据分块。它包含帧内编码宏块类型,帧内编码系数。对应的slice来说,B类数据块的可用性依赖于A类数据分块,和帧间编码信息数据块不同的是,帧内编码信息能防止进一步的偏差,因此比帧间编码信息更重要。
3)帧间编码信息数据块,称为C类数据分块。它包含帧间编码宏块类型,帧间编码系数。它通常是slice中最大的一部分。帧间编码信息数据块是不重要的一部分,它所包含的信息并不提供编解码器之间的同步。C类数据分块的可用性也依赖于A类数据分块,但与B类数据分块无关。
6.3 H264的NAL单元与片、宏之间的联系
通过剖析这么多的小零件,是时候展示一个世界地图了。
其实H264的码流结构并没有大家想象的那么复杂,编码后视频的每一组图像(GOP、图像组)都给予了传输中的序列(SPS)和本身这个帧的图像参数(PPS)。所以我们的整体结构,应该如此: