VCL(Video Coding Layer,视频编码层)和 NAL(Network Abstraction Layer,网络提取层),是 H.264/AVC 视频编码标准中最重要的两个概念。简单来说,VCL 负责“高效压缩”,NAL 负责“友好传输”。
可以把 VCL 和 NAL 理解为视频编码的“内部”与“外部”。VCL 是视频的“内核”,专注于压缩算法本身;而 NAL 是“外壳”,负责将这个内核打包,以适应各种网络环境。
1. VCL:视频编码层
VCL 是 H.264 的核心压缩引擎。它的任务是尽可能地去除视频中的冗余信息,从而用最小的数据量表达出最好的画面质量。
VCL 的核心技术包括:
- 帧内预测:利用图像内部相邻像素之间的相似性进行压缩。
- 帧间预测:利用前后图像之间的相似性进行压缩,支持1/4像素精度的运动矢量,运动估计更精确。
- 变换与量化:对预测后的残差数据进行整数变换(如DCT),再通过量化丢弃人眼不敏感的高频细节。
- 熵编码:对量化后的数据进行无损压缩,如 CAVLC 或 CABAC。
VCL 编码后产生的原始数据,被称为 SODB(String Of Data Bits,数据比特串)。
2. NAL:网络提取层
VCL 产生的是纯粹的、无格式的压缩视频流,它并不知道自己将要被传输到互联网上,还是被存储成一个本地文件。这时,就需要 NAL 出场了。
NAL 的核心功能:
- 数据封装:将 VCL 产生的 SODB 进行字节对齐,添加头部信息,打包成一个一个的 NALU(NAL Unit,NAL单元)。
- 提供头部信息:每个 NALU 都有一个头部字节,告诉解码器这个单元的重要性或包含的数据类型。
3. 数据如何从 VCL 变成 NALU?
这是理解两者关系的关键,流程如下:
- VCL 编码:VCL 对原始视频进行编码,输出原始比特流 SODB。
- 形成 RBSP:为了使数据长度是8的倍数(字节对齐),在 SODB 末尾添加一个
1和若干个0,形成 RBSP(Raw Byte Sequence Payload,原始字节序列载荷)。 - 添加防竞争字节:为防止 RBSP 中的数据与 NALU 的起始码(
0x000001)冲突,在遇到连续两个0x00字节后添加一个0x03,形成 EBSP(Encapsulated Byte Sequence Payload,扩展字节序列载荷)。 - 加上 NALU 头:在 EBSP 前面加上一个字节的 NALU 头,一个完整的 NALU 就诞生了。
最终,多个 NALU 组合在一起,就形成了我们在文件中看到的 H.264 码流。
4. 深入 NALU:一个字节的头信息
NALU 头是整个分层结构中最关键的部分,它只有 1 个字节(8 个比特),结构如下:
| 位序号 | 名称 | 长度 (bits) | 功能说明 |
|---|---|---|---|
| 第1位 | F (forbidden_zero_bit) | 1 | 禁止位。正常为0,如果为1表示该NALU在传输中发生了错误,解码器可以丢弃。 |
| 第2-3位 | NRI (nal_ref_idc) | 2 | 重要性指示。值越大(0-3),该NALU越重要。值为0表示此单元不用于预测,可以被丢弃。 |
| 第4-8位 | Type (nal_unit_type) | 5 | 类型标识。用于指示NALU中承载的数据类型,这是最常用的字段。 |
Type 字段的部分关键值:
| nal_unit_type | 类型名称 | 说明 |
|---|---|---|
| 5 | IDR Slice | 关键帧。IDR 帧(即时解码刷新帧),解码器看到它时会清空参考帧缓冲区,重新开始解码。 |
| 1 | Non-IDR Slice | 非关键帧,可能是 I、P 或 B 帧。 |
| 7 | SPS (Sequence Parameter Set) | 序列参数集。存储视频的“全局”配置,如分辨率、帧率、编码档次等。 |
| 8 | PPS (Picture Parameter Set) | 图像参数集。存储一帧图像的“局部”配置,如熵编码模式、宏块初始化等。 |
| 6 | SEI (Supplemental Enhancement Information) | 补充增强信息。存储一些额外的自定义数据,如时间戳、字幕信息等。 |
5. 数据流与协议栈
整个流程可以直观地表示为: