H264帧格式解析

文章目录

  • H264帧基础知识
    • 一组图像 GOP
    • IDR帧与I帧
    • P帧
    • B帧
    • H264 profile level
    • H264码率控制
  • H264 Annexb byte-stream格式
    • NALU header
    • NALU start-code
  • H264 AVCC格式
    • extradata结构详解
  • H264 Annexb与AVCC格式转换
    • H264 Annexb转为AVCC
    • AVCC 转为 H264 Annexb

进入音视频领域也快一年了,空闲时间思考一下,觉得有必要把一些关于H264与流媒体的知识总结一下,对自己在音视频领域内的知识进行梳理,不至于随着时间的流逝而遗忘。

H264帧基础知识

一组图像 GOP

H264帧格式解析_第1张图片

  • 所谓GOP就是1组图像Group of Picture,在这一组图像中有且只有1个I帧,多个P帧或B帧,两个I帧之间的帧数,就是一个GOP。
  • GOP一般设置为编码器每秒输出的帧数,即每秒帧率,一般为25或30,当然也可设置为其他值。
  • 在一个GOP中,P、B帧是由I帧预测得到的,当I帧的图像质量比较差时,会影响到一个GOP中后续P、B帧的图像质量,直到下一个GOP 开始才有可能得以恢复,所以GOP值也不宜设置过大。
  • 由于P、B帧的复杂度大于I帧,所以过多的P、B帧会影响编码效率,使编码效率降低。另外,过长的GOP还会影响Seek操作的响应速度,由于P、B帧是由前面的I或P帧预测得到的,所以Seek操作需要直接定位,解码某一个P或B帧时,需要先解码得到本GOP内的I帧及之前的N个预测帧才可以,GOP值越长,需要解码的预测帧就越多,seek响应的时间也越长。

IDR帧与I帧

在I帧中,所有宏块都采用帧内预测的方式,因此解码时仅用I帧的数据就可重构完整图像,不需要参考其他画面而生成。

H.264中规定了两种类型的I帧:普通I帧(normal Iframes)和IDR帧(InstantaneousDecoding Refresh, 即时解码刷新)。 IDR帧实质也是I帧,使用帧内预测。IDR帧的作用是立即刷新,会导致DPB(Decoded Picture Buffer参考帧列表)清空,而I帧不会。所以IDR帧承担了随机访问功能,一个新的IDR帧开始,可以重新算一个新的Gop开始编码,播放器永远可以从一个IDR帧播放,因为在它之后没有任何帧引用之前的帧。如果一个视频中没有IDR帧,这个视频是不能随机访问的。所有位于IDR帧后的B帧和P帧都不能参考IDR帧以前的帧,而普通I帧后的B帧和P帧仍然可以参考I帧之前的其他帧。IDR帧阻断了误差的积累,而I帧并没有阻断误差的积累。

一个GOP序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像,但I帧不一定都是IDR帧,只有GOP序列的第1个I帧是IDR帧。

  • I帧:帧内编码帧 ,I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)
  • 它是一个帧内压缩编码帧,压缩比约为7。它将全帧图像信息进行JPEG压缩编码及传输;
  • 解码时仅用I帧的数据就可重构完整图像;
  • I帧描述了图像背景和运动主体的详情;
  • I帧不需要参考其他画面而生成;
  • I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量);
  • 帧是帧组GOP的基础帧(第一帧),在一组中只有一个I帧;
  • I帧不需要考虑运动矢量;
  • I帧所占数据的信息量比较大。

疑问:按照GOP、IDR帧、I帧的解释,如果一个GOP出现除去第一个IDR帧之外的I帧,是不存在的,那这样的话,就不存在非IDR的I帧了,可是为什么还要说明非IDR的I帧呢。

解答:H264编码存在多种编码方式CBR、VBR、CVBR、ABR等等,VBR编码模式下图像内容变化差异很大时,会动态调整I帧的数量,因此GOP的概念需要修正:两个IDR帧之间的间隔为一组GOP,一组GOP中可以出现非IDR的I帧。

P帧

P帧:前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面,P帧没有完整画面数据,只有与前一帧的画面差异的数据。P帧的压缩率20

  • P帧是I帧后面相隔1~2帧的编码帧;
  • P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差);
  • 解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像;
  • P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧;
  • P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧;
  • 由于P帧是参考帧,它可能造成解码错误的扩散;
  • 由于是差值传送,P帧的压缩比较高。

B帧

B帧:双向预测内插编码帧。B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,约为50,但是解码时CPU会比较累。

  • B帧是由前面的I或P帧和后面的P帧来进行预测的
  • B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量
  • B帧是双向预测编码帧
  • B帧压缩比最高,因为它只反映丙参考帧间运动主体的变化情况,预测比较准确;
  • B帧不是参考帧,不会造成解码错误的扩散。

H264 profile level

  • BP-Baseline Profile:基本画质。支持I/P 帧,只支持无交错(Progressive)和CAVLC;
  • EP-Extended profile:进阶画质。支持I/P/B/SP/SI 帧,只支持无交错(Progressive)和CAVLC;
  • MP-Main profile:主流画质。提供I/P/B 帧,支持无交错(Progressive)和交错(Interlaced),也支持CAVLC 和CABAC 的支持;
  • HP-High profile:高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、无损视频编码和更多的YUV 格式。

一般可以输出H264帧的USB摄像头,使用的是BP-Baseline Profile,只有I帧与P帧。

H264码率控制

  • VBR:Variable BitRate,动态比特率,其码率可以随着图像的复杂程度的不同而变化,因此其编码效率比较高,Motion发生时,马赛克很少。码率控制算法根据图像内容确定使用的比特率,图像内容比较简单则分配较少的码率(似乎码字更合适),图像内容复杂则分配较多的码字,这样既保证了质量,又兼顾带宽限制。这种算法优先考虑图像质量。

  • ABR:Average BitRate,平均比特率 是VBR的一种插值参数。ABR在指定的文件大小内,以每50帧 (30帧约1秒)为一段,低频和不敏感频率使用相对低的流量,高频和大动态表现时使用高流量,可以做为VBR和CBR的一种折衷选择。

  • CBR:Constant BitRate,是以恒定比特率方式进行编码,有Motion发生时,由于码率恒定,只能通过增大QP来减少码字大小,图像质量变差,当场景静止时,图像质量又变好,因此图像质量不稳定。优点是压缩速度快,缺点是每秒流量都相同容易导致空间浪费。

  • CVBR:Constrained Variable it Rate,VBR的一种改进,兼顾了CBR和VBR的优点:在图像内容静止时,节省带宽,有Motion发生时,利用前期节省的带宽来尽可能的提高图像质量,达到同时兼顾带宽和图像质量的目的。这种方法通常会让用户输入最大码率和最小码率,静止时,码率稳定在最小码率,运动时,码率大于最小码率,但是又不超过最大码率。

H264 Annexb byte-stream格式

在这里插入图片描述

  • SODB:String of Data Bits,数据 bit 流,最原始的编码数据
  • RBSP:Raw Byte Sequence Payload,原始字节序列载荷,在SODB的后面填加了结尾比特,RBSP trailing bits 一个bit“1”,若干比特“0”,以便字节对齐
  • EBSP:Encapsulated Byte Sequence Payload,扩展字节序列载荷,在RBSP基础上填加了仿校验字节(0x03)。
  • Start-code:在NALU加到Annexb即byte-stream格式时,需要在每组NALU之前添加开始码StartCode,如果该NALU对应的slice为1个GOP开始则用4位字节表示,0x00000001,否则用3位字节表示0x000001(也不一定)。

NALU header

H264帧格式解析_第2张图片

bit位 描述
F 禁止位,0表示正常,1表示错误,一般都是0
NRI 重要级别,11表示非常重要,一般取值为11、10、01
Type:nal_unit_type 表示该NALU的类型是什么,类型的具体取值可见下表
nal_unit_type NAL类型
0 未使用
1 非IDR的片
2 片数据A分区
3 片数据B分区
4 片数据C分区
5 一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧
6 补充增强信息单元(SEI)
7 序列参数集(SPS)
8 图像参数集(PPS)
9 分界符
10 序列结束
11 码流结束
12 填充
13…23 保留
24…31 未使用

NALU Header常见的取值:0x67 0x68 0x65 0x61,0x47 0x48 0x45 0x41,0x27 0x28 0x25 0x21

NALU header 描述
0x67,0x47,0x27 SPS, 序列参数集,重要级别分别为11、10、01
0x68,0x48,0x28 PPS,图像参数集,重要级别分别为11、10、01
0x65,0x45,0x25 IDR帧,重要级别分别为11、10、01
0x61,0x41,0x21 非IDR帧,重要级别分别为11、10、01

NALU start-code

一个NALU包中的数据并不包含它的大小(长度)信息,因此不能简单的连接NALU包来建立一个流,因为你不知道一个包从哪里结束,另一个包从哪里开始。
Annex B格式用起始码来解决这个问题,即给每个NALU加上前缀码:2个或者3个0x00,后面再加一个0x01, 如:0x000001或者0x00000001。
4字节类型的开始码通常只用于标识流中的随机访问点,如SPS PPS AUD和IDR,然后其他地方都用3字节类型的开始码以减少数据量。

  • 如果该NALU对应的slice为1个GOP开始则用4位字节表示,0x00000001,否则用3位字节表示0x000001(不一定)
  • 防竞争字节:为了使NALU主体中不包括与开始码相冲突的,在编码时,就插入一个字节的0x03;解码时将0x03去掉。也称为脱壳操作。
  • 编码器将每个NAL各自独立、完整地放入一个分组,因为分组都有头部,解码器可以方便地检测出NAL的分界,并依次取出NAL进行解码。每个NAL前有一个起始码 0x00 00 01(或者0x00 00 00 01),解码器检测每个起始码,作为一个NAL的起始标识,当检测到下一个起始码时,当前NAL结束。同时H.264规定,当检测到0x000000时,也可以表征当前NAL的结束。那么NAL中数据出现0x000001或0x000000时怎么办?H.264引入了防止竞争机制,如果编码器检测到NAL数据存在0x000001或0x000000时,编码器会在最后个字节前插入一个新的字节0x03,这样:
    0x000000->0x00000300
    0x000001->0x00000301
    0x000002->0x00000302
    0x000003->0x00000303
    解码器检测到0x000003时,把03抛弃,恢复原始数据。解码器在解码时,首先逐个字节读取NAL的数据,统计NAL的长度,然后再开始解码。

H264 AVCC格式

  • Annex-B:没有NALU长度字节,使用start code分隔NALU,start code为三字节或四字节,0x000001或0x00000001,一般是四字节;SPS和PPS按流的方式写在一组GOP之前。
  • AVCC:使用NALU长度,固定字节,通常为4字节,分隔NALU;一般在每个NALU头部为4字节大端格式的长度字节,在一组GOP的头部包含extradata结构,用于存储sequence-header、SPS、PPS数据。
  • 虽然AVCC格式不使用起始码,防竞争字节仍然存在

extradata结构详解

在这里插入图片描述

name length, unit: bit value
version 8bit 0x01
avc profile 8bit 0x64
avc compatibility 8bit 0x00
avc level 8bit 0x0A
NALULengthSizeMinusOne 8bit 0xFF,高6位保留,默认为1,低2位为11,表示NALU长度用3+1=4字节表示
number of SPS NALUs 8bit 0xE1,高3位保留,低5位表示有几个SPS,通常只有1个SPS
SPS size 16bit 大端格式的SPS长度,0x0019,表示25字节SPS
SPS NALU data SPS size × 8bit 0x67到0x80,表示SPS数据
number of PPS NALUs 8bit 0x01PPS个数,通常只有1个PPS
PPS size 8bit 0x07,表示PPS的数据长度
PPS NALU data PPS size × 8bit 0x68到0x30,表示PPS数据

NALULengthSizeMinusOne解释:这个变量告诉我们用几个字节来存储NALU的长度,如果NALULengthSizeMinusOne是0,那么每个NALU使用一个字节的前缀来指定长度,那么每个NALU包的最大长度是255字节,这个明显太小了,使用2个字节的前缀来指定长度,那么每个NALU包的最大长度是64K字节,也不一定够,一般分辨率达到1280*720 的图像编码出的I帧,可能大于64K;3字节是比较完美的,但是因为一些原因(例如对齐)没有被广泛支持;因此4字节长度的前缀是目前使用最多的方式,

H264 Annexb与AVCC格式转换

H264 Annexb转为AVCC

  • 1、对于一个GOP的开始,根据start-code,分离出SPS、PPS帧,并分别计算出长度
  • 2、根据SPS, PPS创建出extradata,附加到GOP的头部
  • 3、从IDR帧开始,搜索start-code,分离出每一个NALU,计算长度,然后将start-code转为4字节的NALU长度

AVCC 转为 H264 Annexb

  • 1、对于一个GOP的开始,首先检索出extradata部分的数据
  • 2、根据extradata数据创建出SPS帧,并用4字节的start-code:0x00000001附加在SPS数据的头部
  • 3、根据extradata数据创建出PPS帧,并用4字节的start-code:0x00000001附加在PPS数据的头部,并将PPS数据连接到SPS后面
  • 4、根据NALU的长度字段,分离出每一个NALU,然后用用4字节的start-code:0x00000001替换长度字段
  • 5、在以上过程中需要计算每一个NALU的长度,尤其是GOP的IDR帧,一般在IDR帧前还有SPS与PPS帧,其长度需要一起计算。

本文对多篇博客进行了参考,进行了总结,着重确定了我想理解的部分,谢谢广大博友的贡献。

你可能感兴趣的:(嵌入式,音视频)