H264编码原理

4 阅读12分钟

一、为什么需要 H.264?

原始视频数据量巨大

1080P@30fps NV12 格式:
1920 × 1080 × 1.5 字节/像素 × 30 帧/秒 = 93.3 MB/s
= 5.6 GB/分钟 = 336 GB/小时

H.264 的三大目标

目标说明
高压缩率压缩到原来的 1/100 ~ 1/200
高画质人眼几乎看不出画质损失
高兼容性支持从低端手机到4K蓝光播放器

四种冗余(编码的理论基础)

H.264 的压缩思路是针对四种冗余分别处理:

冗余类型含义H.264 的处理方式
空间冗余同一帧内相邻像素相似(如蓝天)帧内预测(9种方向预测模式)
时间冗余相邻帧内容大部分相同(如静止背景)帧间预测(运动估计+补偿)
编码冗余符号出现概率不同熵编码(CABAC/CAVLC)
视觉冗余人眼对色度、高频细节不敏感色度子采样(4:2:0)、量化(丢弃高频)

二、H.264 编码器完整流程(核心)

H.264 编码器完整流程

【预处理阶段】

原始帧 (RGB 或 YUV 4:4:4)
        ↓
色度子采样 (YUV 4:4:4 → YUV 4:2:0)
        ↓

【编码器内部】

YUV 4:2:0 帧输入
        ↓
① 帧类型决策 (I/P/B)
        ↓
② 分块
   基本单元:宏块 (16×16 像素)
   可细分为:16×1616×88×168×88×44×84×4
        ↓
   ┌─────────────────────┴─────────────────────┐
   ↓                                           ↓
【I帧:帧内预测】                      【P/B帧:帧间预测】
• 用相邻已编码像素预测                 • 运动估计(搜索最佳匹配位置)
• 4×49种方向预测模式                 • 1/4 像素精度运动矢量
• 16×164种预测模式                   • 多参考帧(最多 16 帧)
• 色度:4种预测模式                    • 色度复用亮度运动矢量
• 残差 = 原始块 - 预测块               • 残差 = 原始块 - 补偿块
   ↓                                           ↓
   └─────────────────────┬─────────────────────┘
                        ↓
③ 残差计算:原始块 - 预测块
                        ↓
④ DCT 变换(整数近似变换)
   空间域(像素值)→ 频率域(频率系数)
   能量集中到左上角(低频),右下角(高频)数值小
   完全可逆,不丢失任何信息
                        ↓
⑤ 量化(Quantization)
   ★ 唯一的有损压缩环节 ★
   系数 ÷ 量化步长(QP 控制),高频系数因数值小被归零
   QP 每增加 6,码率约减半
                        ↓
                量化后的系数
                │         │
                ↓         ↓
      【前向编码路径】    【重建路径】
       (对外输出)       (模拟解码器,内部闭环)
                │         │
                ↓         ↓
        ⑥ 熵编码       ⑦ 反量化
        CAVLC(低复杂度)  恢复 DCT 系数(有损版本)
        CABAC(高压缩率)  ↓
                ↓       ⑧ 反 DCTNAL 打包       频率域 → 空间域
        + SPS            ↓
        + PPS           ⑨ 加回预测值
                ↓        得到重建帧(有损版本)
                ↓         ↓
                ↓        ⑩ ★ 环路滤波(去块滤波)★
                ↓         消除块边界不连续
                ↓          ↓
                ↓        ⑪ 存入参考帧列表
                ↓               ↓
                ↓       供后续 P/B 帧参考
                ↓        (形成编码闭环)
                ↓
     ★ H.264 裸流输出 ★
      (传输给解码器/存储)


【关键设计总结】

前向编码路径:原始帧 → 预测 → 残差 → DCT → 量化 → 熵编码 → NAL打包 → 码流输出
重建路径:量化结果 → 反量化 → 反DCT → 加预测值 → 环路滤波 → 参考帧列表
两者关系:并行执行,共享量化结果;前向路径对外,重建路径对内

环路滤波位置:重建路径的最后一步(反DCT → 加预测值 → ★环路滤波★ → 参考帧)
色度子采样位置:编码器外部预处理,不参与编码环路

三、前向编码路径(生成码流)

3.1 帧类型与 GOP

I/P/B 帧对比

帧类型编码方式体积依赖延迟
I帧仅用自身帧内信息编码最大(基准)
P帧前向参考(参考前面已编码帧)I帧的 1/3~1/5依赖前面的I/P帧
B帧双向参考(参考前后帧)I帧的 1/5~1/10依赖前后帧

IDR帧 vs 普通I帧(面试高频):

类型行为作用
IDR帧I帧 + 清空参考帧列表GOP的起点,随机访问点,错误恢复点
普通I帧I帧但不清空参考帧列表不中断参考链,压缩率更高

GOP 大小权衡

GOP 大小优点缺点适用场景
小(如 15-30)随机访问快、抗丢包好码率高视频会议、直播
大(如 120-300)码率低随机访问慢、丢包恢复慢监控存储、点播

DTS vs PTS(必考):

B帧时,编码顺序 ≠ 显示顺序:

示例(IBBPBB):
显示顺序: I(0) B(1) B(2) P(3) B(4) B(5)
编码顺序: I(0) P(3) B(1) B(2) B(4) B(5)
               ↑
         P帧必须先编码,因为B帧要参考它

DTS = 解码时间戳(解码器输入顺序)
PTS = 显示时间戳(播放器输出顺序)

3.2 帧内预测(去空间冗余)

利用同一帧内已编码的相邻像素预测当前块。

块大小预测模式数说明
4×49种8种方向 + DC(平均值)
8×89种同4×4
16×164种垂直、水平、DC、平面

3.3 帧间预测(去时间冗余)

利用相邻帧之间的相似性,只记录运动矢量和残差。

参数说明
运动矢量精度1/4像素(子像素插值)
参考帧数量最多16帧
分块模式16×16, 16×8, 8×16, 8×8, 8×4, 4×8, 4×4

3.4 DCT变换与量化

DCT变换:将像素从空间域转换到频率域,实现能量集中。自然图像的能量集中在低频区域(左上角),高频区域系数值很小。DCT本身是完全可逆的,不丢失任何信息。

量化(唯一的有损环节) :用量化步长除以DCT系数并取整。高频系数因为数值小,量化后被归零,从而实现了有损压缩

  • 用 QP(量化参数)控制量化步长
  • QP 每增加 6,码率约减半
  • QP 小 → 保留更多细节 → 画质好 → 码率高
  • QP 大 → 丢弃更多高频 → 画质差 → 码率低

3.5 熵编码

编码方式复杂度压缩率适用场景
CAVLC一般Baseline Profile(低功耗设备)
CABAC高(比CAVLC高10-15%)Main/High Profile(追求画质)

3.6 NAL Unit 结构(码流的最小单元)

H.264码流由一系列NAL Unit组成:
  [NALU] [NALU] [NALU] [NALU] ...

每个NAL Unit = NAL Header + NAL Payload

NAL Header(1字节):(MSB->LSB顺序)
  +---+---+---+---+---+---+---+---+
  | 0 | NRI |    Type               |
  +---+---+---+---+---+---+---+---+
    F   1-2位    5位

F (1 bit)    :禁止位,必须为 0
NRI (2 bits) :NAL 参考优先级(值越大越重要)
Type (5 bits):NAL 单元类型(0-31)

  Type值(常考):
  ┌──────┬─────────────────────────────────────────┐
  │ 1    │ 非IDR图像的Slice(普通P/B帧)             │
  │ 5    │ IDR图像的Slice(关键帧)                  │
  │ 6    │ SEI(补充增强信息:时间戳、字幕等)         │
  │ 7    │ SPS(序列参数集)★极其重要                │
  │ 8    │ PPS(图像参数集)★极其重要                │
  │ 9    │ Access Unit Delimiter(访问单元分隔符)   │
  └──────┴─────────────────────────────────────────┘

SPS(Sequence Parameter Set)包含:
  - Profile/Level(基线/主/高级)
  - 分辨率(宽高)
  - 帧率
  - 参考帧数量
  - 比特深度

PPS(Picture Parameter Set)包含:
  - 熵编码模式(CABAC/CAVLC)
  - 切片组结构
  - 量化参数初始值
  - 去块滤波参数

重要性:
  没有SPS/PPS → 解码器无法初始化 → 什么都解不出来
  SPS/PPS通常在连接建立时发送一次,或附在每个IDR帧前面

3.7 Annex-B vs AVCC(两种封装格式)

Annex-B格式(流式传输用,RTSP/RTMP常见):
  用Start Code分隔NAL Unit

  [Start Code] [NALU1] [Start Code] [NALU2] [Start Code] [NALU3] ...

  Start Code有两种:
    3字节:0x00 0x00 0x01     (短起始码)
    4字节:0x00 0x00 0x00 0x01 (长起始码,更常见)

  实际码流举例(xxd看到的):
    00 00 00 01 67  ← Start Code + SPS(type=7)
    00 00 00 01 68  ← Start Code + PPS(type=8)
    00 00 00 01 65  ← Start Code + IDR(type=5)
    00 00 00 01 61  ← Start Code + Non-IDR(type=1)

AVCC格式(文件存储用,MP4容器内):
  用长度前缀分隔NAL Unit

  [4字节长度] [NALU1] [4字节长度] [NALU2] ...

  实际数据:
    00 00 00 12  [NALU_18字节]  ← 长度=0x12=18
    00 00 00 0A  [NALU_10字节]  ← 长度=0x0A=10

区别总结:
  ┌─────────┬────────────────┬────────────────┐
  │          │ Annex-B         │ AVCC            │
  ├─────────┼────────────────┼────────────────┤
  │ 分隔方式 │ Start Code      │ 长度前缀         │
  │ 用途     │ TS/RTSP/RTMP   │ MP4/FLV容器     │
  │ SPS/PPS │ 嵌在码流中       │ 存在extradata中 │
  │ 转换     │ FFmpeg的         │ FFmpeg的         │
  │          │ h264_mp4toannexb│ 自动处理         │
  └─────────┴────────────────┴────────────────┘

面试常问:RTSP流和MP4文件里H.264的封装有什么区别?
答:RTSP用Annex-B,Start Code分隔;MP4用AVCC,长度前缀分隔。

四、重建路径(模拟解码器)⭐ 核心

4.1 为什么需要重建路径?

核心原因:编码器和解码器必须同步。

【错误做法:用原始帧做参考】
编码器:原始I帧 → 参考 → 编码P帧 → 质量高
解码器:有损I帧 → 参考 → 解码P帧 → 质量差
结果:编解码不同步,画面错误累积!

【正确做法:用重建路径模拟解码器】
编码器:原始I帧 → 模拟解码 → 有损I帧 → 参考 → 编码P帧
解码器:有损I帧 → 参考 → 解码P帧
结果:编解码完全同步,无误差累积!

4.2 重建路径的完整流程

┌─────────────────────────────────────────────────────────────────────┐
│                        重建路径(编码器内部)                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  量化后的残差系数(来自前向路径)                                     │
│      ↓                                                              │
│  反量化(恢复DCT系数,但已是有损的)                                  │
│      ↓                                                              │
│  反DCT(频率域→空间域,得到"重建残差")                              │
│      ↓                                                              │
│  加回预测值(得到"重建帧")                                          │
│      ↓                                                              │
│  ★环路滤波(去块滤波)★                                              │
│      ↓                                                              │
│  存入参考帧列表(供后续P/B帧参考)                                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.3 环路滤波(去块效应)

为什么需要:基于块的编码(DCT+量化)会导致块边界不连续,产生"块效应"。

H.264 的处理:在重建路径的最后一步做去块滤波

特性说明
位置重建帧之后、存入参考帧列表之前
作用消除块边界不连续,提升画质
为什么在环路内滤波后的帧作为后续帧参考 → 预测更准 → 压缩率更高

H.265 的增强:在去块滤波基础上,新增 SAO(样点自适应偏移)  和 ALF(自适应环路滤波)


五、为什么这叫"闭环"?

                    ┌──────────────────────────────────────────────────┐
                    │             编码器内部                           │
                    │                                                  │
原始帧 → 前向路径 → 码流输出                                         │
                    │       ↓                                         │
                    │   重建路径(模拟解码器)                         │
                    │       ↓                                         │
                    │   重建帧(有损,但和解码器一致)                 │
                    │       ↓                                         │
                    │   环路滤波                                      │
                    │       ↓                                         │
                    └───────┼──────────────────────────────────────────┘
                            ↓
                      参考帧列表
                            ↓
              ┌─────────────┴─────────────┐
              ↓                           ↓
        编码下一个P帧               解码器解码时
        参考这个重建帧              参考的是同一个帧
              ↓                           ↓
           残差更小                   画面完全同步
              ↓                           ↓
         压缩率更高                   无误差累积

这就是"闭环":编码器模拟解码器 → 生成参考帧 → 反馈给编码器

六、码率控制

CBR(Constant Bit Rate,恒定码率):
  ┌──────────────────────────────────┐
  │  码率                              │
  │  ───────────────────────  2Mbps   │
  │                                    │
  └──────────────────────────────────┘
  适用场景:带宽固定(监控、直播)
  特点:码率稳定,但复杂场景画质下降

VBR(Variable Bit Rate,可变码率):
  ┌──────────────────────────────────┐
  │  码率                              │
  │     /\          /\                 │
  │    /  \   /\   /  \               │
  │   /    _/  _/    __            │
  │                                    │
  └──────────────────────────────────┘
  适用场景:本地录像、离线存储
  特点:复杂场景分配更多码率,画质更均匀

CRF(Constant Rate Factor,恒定质量因子):
  不设目标码率,设目标质量(0~51,越小越好)
  18=视觉无损  23=默认  28=可接受
  适用场景:离线转码

关键参数:
  码率:2Mbps(1080P监控常用)  4Mbps(高清)
  GOP:30~601~2秒)
  Profile:Baseline(无B帧,延迟低)
           Main(有B帧,压缩率好)
           High(压缩率最好,但复杂度最高)
  Level:决定最大分辨率/帧率/码率上限

七、Profile 与 Level

Profile特性适用场景
Baseline无B帧、无CABAC视频会议、低功耗设备
Main有B帧、有CABAC流媒体、广播
High所有工具 + 8×8变换蓝光、高清存储 ★最常用
Level最大分辨率最大码率
4.01080P@30fps20 Mbps
4.11080P@60fps50 Mbps
5.14K@30fps240 Mbps

八、面试标准回答(精简版)

H.264 的核心是什么?

H.264 采用混合编码框架,通过四种手段去除视频冗余:

  1. 帧内预测(去空间冗余):用相邻像素预测当前块
  2. 帧间预测(去时间冗余):用运动估计找匹配块
  3. DCT+量化(去视觉冗余):空间转频域,丢弃高频细节
  4. 熵编码(去编码冗余):CABAC/CAVLC压缩码流

关键设计:编码器内部有重建路径(反量化→反DCT→加预测值→环路滤波→参考帧列表),模拟解码器行为,保证编解码同步,同时滤波后的高质量参考帧反馈给编码器,形成闭环,提升压缩效率。

面试附加问题

Q1:色度子采样为什么用 4:2:0 而不是 4:4:4?

:人眼对亮度(Y)的敏感度远高于色度(U/V)。4:2:0 在水平和垂直方向对色度减半采样,数据量减少 50%,但人眼几乎察觉不到画质差异。它平衡了画质与压缩率,是目前最广泛使用的采样格式。

Q2:I帧和IDR帧有什么区别?

:IDR 帧是一种特殊的 I 帧。普通 I 帧只进行帧内编码,不刷新参考帧列表;IDR 帧在 I 帧的基础上,会清空参考帧列表,使之前的帧不再被后续帧参考,起到“断点”作用。

Q3:CAVLC 和 CABAC 如何选择?

  • CAVLC:复杂度低,适合实时通信、低功耗设备(Baseline Profile)
  • CABAC:压缩率高 10-15%,适合流媒体、高清存储(Main/High Profile)
  • 在 RK3588 等嵌入式平台上,硬件编码器通常同时支持两者,可以画质优先时用 CABAC,性能优先时用 CAVLC。

Q4:GOP 大小如何选择?

场景推荐 GOP理由
视频会议15-30低延迟,抗丢包
直播30-60平衡延迟与压缩率
监控录像120-300强调压缩率,容忍高延迟
点播视频60-120兼顾随机访问与压缩率

Q5:SPS 和 PPS 是每个 NAL Unit 都有吗?

不是。  SPS 和 PPS 只在码流开头、每个 GOP 开头、或编码参数变化时出现一次。解码器收到后会缓存起来,后续帧直接使用。如果每帧都带 SPS/PPS,会浪费码率,因为分辨率、帧率等参数在视频流中通常不变。

Q6:如何判断当前设备是否支持 H.264 硬件编码?

# 在 Linux 上查看硬件编码支持
vainfo           # VA-API 支持
v4l2-ctl --list-formats-ext  # V4L2 支持
# RK3588 上使用 MPP 硬件编码
ffmpeg -encoders | grep h264 | grep rkmpp