音视频压缩:H264码流层次结构和NALU详解

问题背景:

前面在讲封装格式过程中,都有一个章节讲解如何将H.264的NALU单元如何打包到TS、FLV、RTP中,解装刚好相反,怎么从这些封装格式里面解析出一个个NALU单元。NALU即是编码器的输出数据又是解码器的输入数据,所以在封装和传输时,我们一般处理对象就是NALU,至于NALU内部到底是什么则很少关心。甚至我们在编解码时,我们只需要初始化好x264编码库,然后输入YUV数据,它就会给你经过一系列压缩算法后输出NALU,或者将NALU输入到x264解码库就会输出YUV数据。

这篇文章就初步带你看下NALU能传输那些数据,NALU的类型和结构以及H264码流的层次,最后通过分析工具分析下裸码流记性验证,你可以选择感兴趣章节阅读。 


NALU结构:

H.264的基本流(elementary stream)就是一系列NALU的集合,如下图所示:

NALU结构分为两层,包含了视频编码层(VCL)和网络适配层(NAL):

视频编码层(VCL即Video Coding Layer):负责高效的视频内容表示,这是核心算法引擎,其中对宏块、片的处理都包含在这个层级上,它输出的数据是SODB;

网络适配层(NAL即Network Abstraction Layer):以网络所要求的恰当方式对数据进行打包和发送,比较简单,先报VCL吐出来的数据SODB进行字节对齐,形成RBSP,最后再RBSP数据前面加上NAL头则组成一个NALU单元。

分层目的:

这样做的目的:VCL只负责视频的信号处理,包含压缩,量化等处理,NAL解决编码后数据的网络传输,这样可以将VCL和NAL的处理放到不同平台来处理,可以减少因为网络环境不同对VCL的比特流进行重构和重编码;

NLAU结构:

其实NALU的承载数据真实并不是RBSP而是EBSP即(Extent Byte Sequence Payload),EBSP和RBSP的区别就是在 RBSP里面加入防伪起始码字节(0x03),因为H.264规范规定,编码器吐出来的数据需要在每个NALU添加起始码:0x00 00 01或者0x00 00 00 01,用来指示一个NALU的起始和终止位置,那么RBSP数据内部是有可能含有这种字节序列的,为了防止解析错误,所以在RBSP数据流里面碰到0x 00 00 00 01的0x01前面就会加上0x03,解码时将NALU的EBSP中的0x03去掉成为RBSP,称为脱壳操作。

原始数据字节流RBSP即Raw Byte Sequence Playload,因为VCL输出的原始数据比特流SODB即String Of Data Bits,其长度不一定是8bit的整数倍,为了凑成整数个字节,往往需要对SODB最后一个字节进行填充形成RBSP,所以从SODB到RBSP的示意图如下:

具体填充方式就是对VCL的输出数据进行8bit进行切分,最后一个不满8bit的字节第一bit位置1,然后后面缺省的bit置0即可,示意图已经非常明确。

其中H.264规范规定,编码器吐出来的数据需要在每个NALU添加起始码:0x00 00 01或者0x00 00 00 01,用来指示一个NALU的起始和终止位置。

所以H.264编码器输出的码流中每个帧开头3-4字节的start code起始码为0x00 00 01或者0x00 00 00 01。

上面我们分析了NALU的结构以及每层输出数据的处理方法,但是对于NALU的RBSP数据二进制表示的什么含义并不清楚,下面分析下NALU的类型。

NALU类型:

NALU的类型即RBSP可以承载的数据类型如下所示:

Nalu_Type

NALU内容

备注

0

未指定

 

1

非IDR图像编码的slice

比如普通I、P、B帧

2

编码slice数据划分A

2类型时,只传递片中最重要的信息,如片头,片中宏块的预测模式等;一般不会用到;

3

编码slice数据划分B

3类型是只传输残差;一般不会用到;

4

编码slice数据划分C

4时则只可以传输残差中的AC系数;一般不会用到;

5

IDR图像中的编码slice

IDR帧,IDR一定是I帧但是I帧不一定是IDR帧。

6

SEI补充增强信息单元

可以存一些私有数据等;

7

SPS 序列参数集

SPS对如标识符、帧数以及参考帧数目、解码图像尺寸和帧场模式等解码参数进行标识记录

8

PPS 图像参数集

PPS对如熵编码类型、有效参考图像的数目和初始化等解码参数进行标志记录。

9

单元定界符

视频图像的边界

10

序列结束

表明下一图像为IDR图像

11

码流结束

表示该码流中已经没有图像

12

填充数据

哑元数据,用于填充字节

13-23

保留

 

24-31

未使用

 

举例说明:

这其中NALU的RBSP除了能承载真实的视频压缩数据,还能传输编码器的配置信息,其中能传输视频压缩数据的为slice。

那么如果NLAU传输视频压缩数据时,编码器没有开启DP(数据分割)机制,则一个片就是一个NALU,一个 NALU 也就是一个片。否则,一个片由三个 NALU 组成,即DPA、DPB和DPC,对应的nal_unit_type 类型为 2、3和4。 

通常情况我们看到的NLAU类型就是SPS、PPS、SEI、IDR的slice、非IDR这几种。

上面站在NALU的角度看了NALU的类型、结构、数据来源、分层处理的原因等,其中NLAU最主要的目的就是传输视频数据压缩结果。那么站在对数据本身的理解上,我们看下H.264码流的层次结构。


H.264层次结构:

其实为了理解H.264是如何看待视频数据,先要了解下视频的形成过程。其实你把多副连续的有关联图像连续播就可以形成视频,这主要利用了人视觉系统的暂留效应,当把连续的图片以每秒25张的速度播放,人眼基本就感觉是连续的视频了。为了说明这个概念,我下面准备了一个视频大家点开看下,加深对视频的理解。

从上面的视频播放过程中,我们大概能看出视频有下面几个特点:

一张图像里面相邻的区域或者一段时间内连续图像的相同位置,像素、亮度、色温差别比较小,所以视频压缩本质就是利于这种空间冗余和时间上冗余进行编码,我们可以选取一段时间第一幅图像的YUV值,后面的只需要记录和这个的完整图像的差别即可,同时即使记录一副图像的YUV值,当有镜头完全切换时,我们又选取切换后的第一张作为基本图像,后面有一篇文章回讲述下目前视频压缩的基本原理。

所以从这里面就可以引申以下几个概念:

GOP:一段时间内图像变化不大的图像集我们就可以称之为一个序列,这里的图像又在H264里面称为帧,所以就是一组视频帧,其中第一个我们称为是IDR帧。

帧:一副图像编码后的视频数据也叫做一帧,其中有I帧、B帧、P帧,前文多次提到,不再赘述;

片:一帧图像又可以划分为很多片,由一个片或者多个片组成;

宏块:视频编码的最小处理单元,承载了视频的具体YUV信息,一片由一个或者多个宏块组成;

所以视频流分析的对象可以用下面的图片描述:

如果站在数据的角度分析NALU的层次关系,如下图:

这里视频帧被划分为一个片或者多个片,其中slice数据主要就是通过NLAU进行传输,其中slice数据又是由:

一个Slice = Silce + Slice Data

Slice片类型:

片类型

含义

I片

只包含I宏块

P片

包含P和I宏块

B片

包含B和I宏块

SP片

包含P 和/或 I宏块,用于不同码流之间的切换

SI片

一种特殊类型的编码宏块

设置片的目的是限制误码的扩散和传输,也就是一帧图像中它们的编码片是互相独立的,这样假设其中一张图像的某一个片有问题导致解码花屏,但是这个影响范围就控制在这个片中,这就是我们平时看视频发现只有局部花屏和绿屏的原因。

Slice Data里面传输的是一个个宏块,宏块中的数据承载各个像素点YUV的压缩数据。一个图像通常被我们划分成宏块来研究,通常有16*16、16*8等格式。我们解码的过程也就是恢复这些像素阵列的过程,如果知道了每个像素点的亮度和色度,就能渲染出一张完整的图像,图像的快速播放即是视频。

其中宏块MB的类型:

宏块分类

意义

I宏块

利用从当前片中已解码的像素作为参考进行帧内预测

P宏块

利用前面已编码图像作为参考进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割:即16×16.16×8.8×16.8×8亮度像素块。如果选了8×8的子宏块,则可再分成各种子宏块的分割,其尺寸为8×8,8×4,4×8,4×4

B宏块

利用双向的参考图像(当前和未来的已编码图像帧)进行帧内预测

宏块的结构:


H.264码流示例分析:

这里我们分析一下H.264的NLAU数据,其中包括了非VCL的NALU数据和VCL的NALU。

H.264码流的NLAU单元:

1. 我们发现视频流数据就是由一系列SPS、PPS、I、P、B帧序列组成,这些数据都是通过NALU进行承载的;

2. 我们看到了NALU不仅仅可以承载SPS、PPS、SEI等非VCL数据,也可以传输I、B、P帧的切片VCL数据。

3. 我们同时看到了NALU的Data部分,如果是VCL数据,则就是slice header+silce data这种结构,其中对VCL的SODB做了bit填充的字节对齐处理;

4. 这里由于没有数据分割机制,所以一个NALU承载一个片,同时一个片就是一个视频帧;

4.至于NALU的非VCL数据SPS、PPS、SEI各个字段的含义具体解析放到下篇文章,这个信息对于解码器进行播放视频很重要,很多播放问题都是这个数据有问题导致的;

上面看了视频的GOP序列,视频帧信息和片的组成,下面分析片中的宏块信息;

H.264的层次结构:

1. 我们能看到整个视频的视频序列,显示看了该视频有I、B、P帧信息,这和上面的分析结果是一致的;

2. 中间图片部分用不同颜色的点显示了宏块和子宏块信息,右上角是对宏块内容的具体说明;

3. 其中不同的帧类型上面的宏块类型也是不一样的;


总结:

本文主要讲述了平时研究和分析视频流对象的层次,然后这些视频数据通过NALU传输时,NALU的类型和层次关系,以及NALU数据在不同层次的输出。最后用视频分析工具分析了H.264裸码流验证了上述层次关系。

所以对H.264数据分析时,一定要了解你现在分析的层次和框架,因为每个层次我们关心的数据处理对象是不一样的,这个非常重要。

一般H.264的分析工具都是收费的,也有一些免费和裁剪版本供大家学习和使用。推荐几个:Elecard StreamEye、CodecVisa、VideoEye、H264Analyzer、H264Visa等,有时需要交叉使用才能完成对你关心信息的分析,这些都放到我的Git上了,大家获取使用即可。

发布了207 篇原创文章 · 获赞 196 · 访问量 65万+

猜你喜欢

转载自blog.csdn.net/tanningzhong/article/details/102546827