标签:param 二进制 模式 怎么 种类型 提高 home 进入 包含
无论是解析视频文件或者通过网络传输,其实都是一串字节序列。H264 码流就是按照一定的规则组织排列的字节串。
在 H264 中完全没有 I 帧、P 帧、B 帧、IDR 帧的概念,之所以沿用这些说法是为了表明数据的编码模式。H264 码流的组织形式从大到小排序是:视频序列(video sequence)、图像(frame/field-picture)、片组(slice group)、片(slice)、宏块(macroblock)、子块(sub-block)、像素(pixel)。
从码流功能的角度可以分为两层:视频编码层(VCL)和网络提取层(NAL)
VCL 是管理 H264 的视频数据层,是为了实现更高的视频压缩比,那 VCL 究竟是怎么管理 H264 视频数据的呢?抛开 H264 压缩算法细节来看就 3 步:
下面要重点讲解下 NAL。
NAL,英文全称为 Network Abstraction Layer,这块和 H264 压缩算法无关,涉设计出 NAL 的目的就是为了获得 “network-friendly”,即为了实现良好的网络亲和性,即可适用于各种传输网络。
终于要讲 NAL 了,但是,我们需要先看 NAL的组成单元 - NALU。
NALU 的格式如下图(引用H264 PDF)所示:
很明显,NALU 由头和身体两个部分组成:
首先,NALU Header只占 1 个字节,即 8 位,其组成如下图所示:
forbidden_zero_bit
在网络传输中发生错误时,会被置为 1,告诉接收方丢掉该单元;否则为 0。
nal_ref_idc
用于表示当前NALU的重要性,值越大,越重要。
解码器在解码处理不过来的时候,可以丢掉重要性为 0 的 NALU。
nal_unit_type
表示 NALU 数据的类型,有以下几种:
其中比较注意的应该是以下几个:
特殊的 NALU 类型:SPS和PPS
SPS 和 PPS 存储了编解码需要一些图像参数,SPS,PPS 需要在 I 帧前出现,不然解码器没法解码。而 SPS,PPS 出现的频率也跟不同应用场景有关,对于一个本地 h264 流,可能只要在第一个 I 帧前面出现一次就可以,但对于直播流,每个 I 帧前面都应该插入 sps 或 pps,因为直播时客户端进入的时间是不确定的。
很少有资料会称身体部分为 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 码流实际可以理解为由一个一个的 NALU 单元组成。(下图中的 RBSP 类似 NALU Payload)
前面提到的一帧图像(I 帧, P 帧, B 帧)就是一个 NALU 单元,NALU 单元除了代表图像外还能包含其他类型的数据,如 PPS 和 SPS。
片的目的:
为了限制误码的扩散和传输,使编码片相互间保持独立。片共有 5 种类型: I 片(只包含 I 宏块)、P 片(P 和 I 宏块)、B 片(B 和 I 宏块)、SP 片(用于不同编码流之 间的切换)和 SI 片(特殊类型的编码宏块)。
了解了 NALU 之后,关于 H264 格式,还有一个问题:解码器怎么知道一个 NALU 要结束了?或者说它怎么区分 NALU 的边界?
要回答这个问题,就必须了解 H264 的打包方式,通俗来说是H264 如何组织一连串的 NALU 为完整的 H264 码流。目前 H264 主流的两种格式:
Annex-B:本文关于 NALU 的很多细节介绍都是 Annex-B,它依靠前文提到的 Start Code 来分隔 NALU,打包方式如下:
[start code]--[NALU]--[start code]--[NALU]...
AVCC:笔者对这个格式了解的不多,从网上找到很多资料知道以下几点:
由 NALU 和 extradata/sequence header 组成,由于在 extradata/sequence header 中存储了 NALU 的长度,因此 NALU Payload 不需要做字节对齐,不过防竞争字节还是有的;
SPS 和 PPS 被放在了 extradata/sequence header。
打包方式如下:
[SIZE (4 bytes)]--[NAL]--[SIZE (4 bytes)]--[NAL]... // 请注意,SIZE一般为4字节,但是具体以实际为准
至于为什么要有这两类格式,还需要查阅更多的资料。不过 StackOverflow 上关于Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream的回答可以帮助深入了解这两种格式,推荐阅读。
下面是一个 H264 码流,可以看到每个 NALU 前有一个 StartCode(0x000001 或 0x00000001),作为 NALU 的分割符:
分析其中比较有代表性的3帧:
参考:
标签:param 二进制 模式 怎么 种类型 提高 home 进入 包含
原文地址:https://www.cnblogs.com/linuxAndMcu/p/14533228.html