4. 编码流程总览
H.264 编码器并不是“一步压缩”,而是一条由多个阶段串联而成的流水线。输入是一帧帧原始 YUV 像素,输出是符合 H.264 语法规范的 NAL 单元组成的码流。理解这条流水线,是后续学习帧内预测、运动估计、变换量化、熵编码等模块的前提。本节从整体视角梳理从 YUV 到 H.264 码流的完整路径,并说明各阶段之间的数据依赖关系。
一、核心原理:编码器是一条“预测—残差—变换—量化—熵编码”的闭环流水线
H.264 编码的核心思想是去除冗余:空间冗余靠帧内预测消除,时间冗余靠帧间预测消除,剩余残差再通过变换、量化去除频域与视觉冗余,最后用熵编码去除统计冗余。整个流程可以概括为“预测产生残差,残差变换量化,量化系数熵编码”,同时重建帧会回写参考帧缓冲区,形成闭环。
需要特别注意的是,编码器内部存在一条重建回路:量化后的系数经过反量化、反变换得到重建残差,再与预测值相加得到重建块,经去块滤波后写入参考帧。这条回路保证编码端与解码端使用相同的参考图像,否则后续帧的预测会持续漂移。
一句话记忆:输入 YUV → 分块与预测 → 残差 → 变换 → 量化 → 熵编码 → 输出码流;同时量化系数 → 反量化 → 反变换 → 重建 → 滤波 → 参考帧。
二、方法流程:从 YUV 到 H.264 码流的八个阶段
- 输入与预处理:读取 YUV 4:2:0 帧,按宏块(16×16)或编码树单元划分。YUV 三个分量分别处理,亮度与色度使用不同量化参数。
- 帧类型决策:确定 I 帧、P 帧或 B 帧。I 帧只做帧内预测,P/B 帧可做帧间预测。GOP 结构决定参考关系。
- 预测:帧内预测利用当前帧已重建的相邻像素生成预测块;帧间预测通过运动估计在参考帧中寻找匹配块,得到运动矢量与预测块。
- 残差计算:原始块减去预测块,得到残差块。残差通常比原始像素更“平坦”,更利于压缩。
- 变换:对残差做整数 DCT 变换(4×4 或 8×8),将能量集中到低频系数,便于后续压缩。
- 量化:用量化参数 QP 对变换系数进行除法取整,这是有损压缩的关键步骤,QP 越大压缩率越高但画质越差。
- 熵编码:对量化系数、运动矢量、预测模式等语法元素进行 CABAC 或 CAVLC 编码,输出二进制码流。
- 封装输出:将编码数据打包为 NAL 单元,加上 SPS、PPS 等参数集,形成可传输或存储的 H.264 码流。
下面这张图展示了从输入 YUV 到输出 H.264 码流的完整数据流,以及重建回路与参考帧的关系:
三、案例/场景:一帧 P 帧的编码过程
假设输入一帧 1920×1080 的 YUV420 P 帧,编码器按以下顺序处理:
- 将帧划分为 16×16 宏块,对每个宏块在参考帧中做运动估计,找到最佳匹配块,得到运动矢量 MV 和预测块。
- 原始宏块减去预测块得到残差,残差做 4×4 整数 DCT,得到 16 个变换系数。
- 用当前 QP 对系数量化,高频系数大量变为 0,实现压缩。
- 量化系数一路送熵编码,另一路送反量化、反变换,重建残差加上预测块得到重建宏块。
- 重建宏块经去块滤波后写入 DPB,供后续帧参考。
- 熵编码将量化系数、MV、预测模式等写入切片数据,最终封装为 NAL 单元输出。
如果 QP 设置过小,码率会急剧上升;如果 QP 过大,高频细节丢失,出现块效应和模糊。实际编码中常通过码率控制算法动态调整 QP。
四、关键参数与阶段对照
| 阶段 | 输入 | 输出 | 关键参数 | 是否可逆 |
|---|---|---|---|---|
| 预测 | 原始块 + 参考帧 | 预测块 + MV/模式 | 预测模式、搜索范围 | 可逆 |
| 变换 | 残差块 | 变换系数 | 变换尺寸 4×4/8×8 | 近似可逆 |
| 量化 | 变换系数 | 量化系数 | QP | 不可逆 |
| 熵编码 | 量化系数 + 语法元素 | 二进制码流 | CABAC/CAVLC | 可逆 |
| 重建回路 | 量化系数 | 重建帧 | QP、滤波强度 | 与解码端一致 |
五、常见误区与复盘
误区一:认为编码器只做一次前向处理。实际上编码器内部有重建回路,否则参考帧会与解码端不一致,导致画面持续漂移。
误区二:把量化当成普通除法。量化是有损压缩的核心,QP 直接决定码率与画质平衡,且色度与亮度可使用不同 QP。
误区三:忽略熵编码前的语法组织。运动矢量、预测模式、块类型等都要按 H.264 语法写入码流,并非只编码像素残差。
误区二:把量化当成普通除法。量化是有损压缩的核心,QP 直接决定码率与画质平衡,且色度与亮度可使用不同 QP。
误区三:忽略熵编码前的语法组织。运动矢量、预测模式、块类型等都要按 H.264 语法写入码流,并非只编码像素残差。
复盘建议:学习后续章节时,始终把每个模块放回这条流水线中定位:它处理的是原始像素、残差、变换系数还是语法元素?它的输出流向熵编码还是重建回路?这样能避免“只见树木不见森林”。
掌握这条完整流程后,再深入帧内预测、运动估计、变换量化、CABAC 等模块时,就能清楚每个模块在整体中的位置与作用。编码器的性能优化,本质上也是在这条流水线上寻找计算瓶颈与率失真平衡点。