音视频 (一) —— H.264 中的 NAL 技术
音视频 (二) —— H.264中相关参数理解
音视频 (三) —— 视频相关参数的理解
了解H.264
H.264,同时也是MPEG-4第十部分,是由ITU-T视频编码专家组(VCEG)和ISO/IEC动态图像专家组(MPEG)联合组成的联合视频组(JVT,Joint Video Team)提出的高度压缩数字视频编解码器标准。这个标准通常被称之为H.264/AVC(或者AVC/H.264或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC)而明确的说明它两方面的开发者。
H264标准各主要部分有Access Unit delimiter(访问单元分割符),SEI(附加增强信息),primary coded picture(基本图像编码),Redundant Coded Picture(冗余图像编码)。还有Instantaneous Decoding Refresh(IDR,即时解码刷新)、Hypothetical Reference Decoder(HRD,假想参考解码)、Hypothetical Stream Scheduler(HSS,假想码流调度器)。
H.264基本数据结构
H264码流可以分为两层,VCL层和NAL层,NAL的全称是Network abstraction layer,叫网络抽象层,它保存了H264相关的参数信息和图像信息,NAL层由多个单元NALU组成,NALU由了NALU头(00 00 00 01或者00 00 01)sps
(序列参数集 Sequence Parameter Set)、pps
(图像参数集合 picture parameter set)、SEI
(增强信息 set enhance information)。
H264编码出的NALU规律
第一个I帧 第一帧包括
SPS
【00 00 00 01 0x67】PPS
【00 00 00 01 0x68】SEI
【00 00 00 01 0x06】IDR
【00 00 00 01 0x65】P帧
P
【00 00 00 01 0x61】I帧包括
SPS
【00 00 00 01 0x67】PPS
【00 00 00 01 0x68】IDR
【00 00 00 01 0x65】
如何判断帧类型是图像参考帧还是I、B、P等?
NALU类型是我们判断帧类型的利器,从官方文档中得出如下图:
00 00 00 01
分割之后的下一个字节就是NALU类型,将其转为二进制数据后,解读顺序为从左往右算,如下:
(1)第1位禁止位,值为1表示语法出错
(2)第2~3位为参考级别
(3)第4~8为是nal单元类型
例如上面00 00 00 01
后有67、68以及65
其中
0x67
的二进制码为:
0110 0111
4-8为00111,转为十进制7,参考第二幅图:7对应序列参数集SPS,即0x67开始直到下个00000001之间的数据为SPS
其中
0x68
的二进制码为:
0110 1000
4-8为01000,转为十进制8,参考第二幅图:8对应图像参数集PPS,即0x68开始直到下个00000001之间的数据为PPS
其中0x65的二进制码为:
0110 0101
4-8为00101
,转为十进制5,参考第二幅图:5对应IDR图像中的片(I帧),即0x65
开始直到下个00 00 00 01
之间的数据为I
帧
所以判断是否为I帧的算法为: (NALU
类型 & 0001 1111
)= 5 即 NALU
类型 & 31 = 5
比如0x65
& 31 = 5
更多NALU
类型值参考下表
H.264编码原理
H.264是新一代的编码标准,以高压缩高质量和支持多种网络的流媒体传输著称,在编码方面,我理解的他的理论依据是:参照一段时间内图像的统计结果表明,在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内。所以对于一段变化不大图像画面,我们可以先编码出一个完整的图像帧A,随后的B帧就不编码全部图像,只写入与A帧的差别,这样B帧的大小就只有完整帧的1/10或更小!B帧之后的C帧如果变化不大,我们可以继续以参考B的方式编码C帧,这样循环下去。这段图像我们称为一个序列(序列就是有相同特点的一段数据),当某个图像与之前的图像变化很大,无法参考前面的帧来生成,那我们就结束上一个序列,开始下一段序列,也就是对这个图像生成一个完整帧A1,随后的图像就参考A1生成,只写入与A1的差别内容。
在H264协议里定义了三种帧,完整编码的帧叫I帧,参考之前的I帧生成的只包含差异部分编码的帧叫P帧,还有一种参考前后的帧编码的帧叫B帧。
H264采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成I帧的算法,帧间压缩是生成B帧和P帧的算法。
对序列的说明
在H.264中图像以序列为单位进行组织,一个序列是一段图像编码后的数据流,以I帧开始,到下一个I帧结束。
一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。
一个序列就是一段内容差异不太大的图像编码后生成的一串数据流。当运动变化比较少时,一个序列可以很长,因为运动变化少就代表图像画面的内容变动很小,所以就可以编一个I帧,然后一直P帧、B帧了。当运动变化多时,可能一个序列就比较短了,比如就包含一个I帧和3、4个P帧。
对三种帧的介绍
I帧
为了更好地理解I帧的概念,我罗列了两种解释:
- 帧内编码帧 ,I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)。
- 帧内编码帧 又称intra picture,I 帧通常是每个 GOP(MPEG 所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,做为随机访问的参考点,可以当成图象。I帧可以看成是一个图像经过压缩后的产物。
I帧的特点:
- 它是一个全帧压缩编码帧。它将全帧图像信息进行JPEG压缩编码及传输
- 解码时仅用I帧的数据就可重构完整图像
- I帧描述了图像背景和运动主体的详情
- I帧不需要参考其他画面而生成
- I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量)
- I帧是帧组GOP的基础帧(第一帧),在一组中只有一个I帧
- I帧不需要考虑运动矢量
- I帧所占数据的信息量比较大
P帧
为了更好地理解P帧的概念,我也罗列了两种解释:
- 前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)
- 前向预测编码帧 又称predictive-frame,通过充分将低于图像序列中前面已编码帧的时间冗余信息来压缩传输数据量的编码图像,也叫预测帧
P帧的预测与重构:P帧是以I帧为参考帧,在I帧中找出P帧“某点”的预测值和运动矢量,取预测差值和运动矢量一起传送。在接收端根据运动矢量从I帧中找出P帧“某点”的预测值并与差值相加以得到P帧“某点”样值,从而可得到完整的P帧。
P帧特点:
- P帧是I帧后面相隔1~2帧的编码帧
- P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差)
- 解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像
- P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧
- P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧
- 由于P帧是参考帧,它可能造成解码错误的扩散
- 由于是差值传送,P帧的压缩比较高
B帧
为了更好地理解P帧的概念,我依然罗列了两种解释:
- 双向预测内插编码帧。B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别(具体比较复杂,有4种情况,但我这样说简单些),换言之,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU会比较累。
- 双向预测内插编码帧 又称bi-directional interpolated prediction frame,既考虑与源图像序列前面已编码帧,也顾及源图像序列后面已编码帧之间的时间冗余信息来压缩传输数据量的编码图像,也叫双向预测帧;
B帧的预测与重构: B帧以前面的I或P帧和后面的P帧为参考帧,“找出”B帧“某点”的预测值和两个运动矢量,并取预测差值和运动矢量传送。接收端根据运动矢量在两个参考帧中“找出(算出)”预测值并与差值求和,得到B帧“某点”样值,从而可得到完整的B帧。
B帧的特点:
- B帧是由前面的I或P帧和后面的P帧来进行预测的
- B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量
- B帧是双向预测编码帧
- B帧压缩比最高,因为它只反映丙参考帧间运动主体的变化情况,预测比较准确
- B帧不是参考帧,不会造成解码错误的扩散
I、B、P各帧是根据压缩算法的需要,是人为定义的,它们都是实实在在的物理帧。一般来说,I帧的压缩率是7(跟JPG差不多),P帧是20,B帧可以达到50。可见使用B帧能节省大量空间,节省出来的空间可以用来保存多一些I帧,这样在相同码率下,可以提供更好的画质。
对压缩算法的说明
h264的压缩方法:
- 分组:把几帧图像分为一组(GOP,也就是一个序列),为防止运动变化,帧数不宜取多。
- 定义帧:将每组内各帧图像定义为三种类型,即I帧、B帧和P帧;
- 预测帧:以I帧做为基础帧,以I帧预测P帧,再由I帧和P帧预测B帧;
- 数据传输:最后将I帧数据与预测的差值信息进行存储和传输。
帧内(Intraframe)压缩也称为空间压缩(Spatial compression)。当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息,这实际上与静态图像压缩类似。帧内一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像,所以可以独立的解码、显示。帧内压缩一般达不到很高的压缩,跟编码jpeg差不多。
帧间(Interframe)压缩的原理是:相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点。也即连续的视频其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减小压缩比。帧间压缩也称为时间压缩(Temporal compression),它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩一般是无损的。帧差值(Frame differencing)算法是一种典型的时间压缩法,它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻帧的差值,这样可以大大减少数据量。
顺便说下有损(Lossy )压缩和无损(Lossy less)压缩。无损压缩也即压缩前和解压缩后的数据完全一致。多数的无损压缩都采用RLE行程编码算法。有损压缩意味着解压缩后的数据与压缩前的数据不一致。在压缩的过程中要丢失一些人眼和人耳所不敏感的图像或音频信息,而且丢失的信息不可恢复。几乎所有高压缩的算法都采用有损压缩,这样才能达到低数据率的目标。丢失的数据率与压缩比有关,压缩比越小,丢失的数据越多,解压缩后的效果一般越差。此外,某些有损压缩算法采用多次重复压缩的方式,这样还会引起额外的数据丢失。
三种帧的不同
- I frame:自身可以通过视频解压算法解压成一张单独的完整的图片。
- P frame:需要参考其前面的一个I frame 或者B frame来生成一张完整的图片。
- B frame:则要参考其前一个I或者P帧及其后面的一个P帧来生成一张完整的图片。
- 两个I frame之间形成一个GOP,在x264中同时可以通过参数来设定bf的大小,即:I 和p或者两个P之间B的数量。
- 通过上述基本可以说明如果有B frame 存在的情况下一个GOP的最后一个frame一定是P.
DTS和PTS的不同
概念
DTS(Decoding Time Stamp) : 即解码时间戳,是解码器进行解码时相对于SCR(系统参考时间)的时间戳。它主要标识读入内存的bit流在什么时候开始送入解码器中进行解码。
PTS(Presentation Time Stamp) : 即显示时间戳,是显示帧时相对于SCR的时间戳。它主要是度量解码后的视频什么时候被显示出来。
DTS主要用于视频的解码,在解码阶段使用.PTS主要用于视频的同步和输出.在display的时候使用.在没有B frame的情况下.DTS和PTS的输出顺序是一样的。
例子:
下面给出一个GOP为15的例子,其解码的参照frame及其解码的顺序都在里面:
如上图:I frame 的解码不依赖于任何的其它的帧.而p frame的解码则依赖于其前面的I frame或者P frame.B frame的解码则依赖于其前的最近的一个I frame或者P frame 及其后的最近的一个P frame.