其他分享
首页 > 其他分享> > 【H264】码流结构详解

【H264】码流结构详解

作者:互联网

无论是解析视频文件或者通过网络传输,其实都是一串字节序列。H264 码流就是按照一定的规则组织排列的字节串。


一、码流的组织形式

在 H264 中完全没有 I 帧、P 帧、B 帧、IDR 帧的概念,之所以沿用这些说法是为了表明数据的编码模式。H264 码流的组织形式从大到小排序是:视频序列(video sequence)、图像(frame/field-picture)、片组(slice group)、片(slice)、宏块(macroblock)、子块(sub-block)、像素(pixel)。
H264_Struct_A.png


二、码流功能的角度

从码流功能的角度可以分为两层:视频编码层(VCL)和网络提取层(NAL)


VCL 是管理 H264 的视频数据层,是为了实现更高的视频压缩比,那 VCL 究竟是怎么管理 H264 视频数据的呢?抛开 H264 压缩算法细节来看就 3 步:

  1. 压缩:预测(帧内预测和帧间预测)-> DCT 变化和量化 -> 比特流编码;
  2. 切分数据,主要为了第三步。这里一点,网上看到的“切片(slice)”、“宏块(macroblock)”是在VCL 中的概念,一方面提高编码效率和降低误码率、另一方面提高网络传输的灵活性。
  3. 压缩切分后的 VCL 数据会包装成为 NAL 中的一部分。

下面要重点讲解下 NAL。


三、网络提取层(NAL)

NAL,英文全称为 Network Abstraction Layer,这块和 H264 压缩算法无关,涉设计出 NAL 的目的就是为了获得 “network-friendly”,即为了实现良好的网络亲和性,即可适用于各种传输网络。

终于要讲 NAL 了,但是,我们需要先看 NAL的组成单元 - NALU


3.1 NAL单元 - NALU

NALU 的格式如下图(引用H264 PDF)所示:

FFmpeg_H264_NALU.png


很明显,NALU 由身体两个部分组成:


3.2 NALU Header

首先,NALU Header只占 1 个字节,即 8 位,其组成如下图所示:

H264_Struct_B.jpg


H264_Struct_C.png


其中比较注意的应该是以下几个:

特殊的 NALU 类型:SPS和PPS

SPSPPS 存储了编解码需要一些图像参数,SPS,PPS 需要在 I 帧前出现,不然解码器没法解码。而 SPS,PPS 出现的频率也跟不同应用场景有关,对于一个本地 h264 流,可能只要在第一个 I 帧前面出现一次就可以,但对于直播流,每个 I 帧前面都应该插入 sps 或 pps,因为直播时客户端进入的时间是不确定的。


3.3 NALU Payload

很少有资料会称身体部分为 Payload,绝大部分资料对 NALU 组成的定义是这样子的:

NALU = NALU Header + SODB // 定义1
NALU = NALU Header + RBSP // 定义2
NALU = NALU Header + EBSP // 定义3

于是新的问题来了:SODB,RBSP和EBSP都是什么东西呢?这块概念,在博客NALU详解二(EBSP、RBSP与SODB)中介绍得非常清楚,总结来说就是:

SODB

英文全称 String Of Data Bits,称原始数据比特流,就是最原始的编码/压缩得到的数据。

RBSP

英文全称 Raw Byte Sequence Payload,又称原始字节序列载荷。和 SODB 关系如下:

RBSP = SODB + RBSP Trailing Bits(RBSP尾部补齐字节)

引入 RBSP Trailing Bits 做 8 位字节补齐。

EBSP

英文全称 Encapsulated Byte Sequence Payload,称为扩展字节序列载荷。和 RBSP 关系如下:

EBSP :RBSP插入防竞争字节(`0x03`)

这里说明下防止竞争字节(0x03):读者可以先认为 H264 会插入一个叫做 StartCode 的字节串来分割 NALU,于是问题来了,如果 RBSP 中也包括了 StartCode(0x000001 或 0x00000001)怎么办呢?所以,就有了防止竞争字节(0x03)

编码时,扫描 RBSP,如果遇到连续两个 0x00 字节,就在后面添加 防止竞争字节(0x03);解码时,同样扫描 EBSP,进行逆向操作即可。

最后,以一幅图总结 NALU 这段内容:

H264_Struct_D.jpg


四、码流解析的角度

H264 码流实际可以理解为由一个一个的 NALU 单元组成。(下图中的 RBSP 类似 NALU Payload)

H264_Struct_E.png


前面提到的一帧图像(I 帧, P 帧, B 帧)就是一个 NALU 单元,NALU 单元除了代表图像外还能包含其他类型的数据,如 PPS 和 SPS。


五、H264更详细的分层结构

H264_Struct_F.png


片的目的:

为了限制误码的扩散和传输,使编码片相互间保持独立。片共有 5 种类型: I 片(只包含 I 宏块)、P 片(P 和 I 宏块)、B 片(B 和 I 宏块)、SP 片(用于不同编码流之 间的切换)和 SI 片(特殊类型的编码宏块)。


六、扩展:怎么区分 NALU 的边界?

了解了 NALU 之后,关于 H264 格式,还有一个问题:解码器怎么知道一个 NALU 要结束了?或者说它怎么区分 NALU 的边界?

要回答这个问题,就必须了解 H264 的打包方式,通俗来说是H264 如何组织一连串的 NALU 为完整的 H264 码流。目前 H264 主流的两种格式:

至于为什么要有这两类格式,还需要查阅更多的资料。不过 StackOverflow 上关于Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream的回答可以帮助深入了解这两种格式,推荐阅读。


下面是一个 H264 码流,可以看到每个 NALU 前有一个 StartCode(0x000001 或 0x00000001),作为 NALU 的分割符:

H264_Struct_G.jpg


分析其中比较有代表性的3帧:


参考:

H264系列--码流组成和分层结构

知乎 - 视频和视频帧:H264编码格式整理

(十三) h264标准解析


标签:码流,字节,H264,NAL,详解,NALU,RBSP
来源: https://www.cnblogs.com/linuxAndMcu/p/14533228.html