视频编解码原理介绍

原始未压缩视频数据量极大,无法直接存储 / 传输: 举个例子:1080P 60fps、8bit 420 视频 单帧像素:1920×1080 = 2073600 YUV420:每像素平均 1.5 字节 每秒数据量:1920×1080×1.5×60 ≈ 178MB/s 一部 90 分钟电影:≈945GB。

这么大的数据,一方面网络传输困难,另外一方面存储也相当麻烦,所以为了解决视频数据的传输和存储问题, 需要对视频数据进行压缩处理。

一、编码压缩策略

编码核心目标:去除冗余信息,压缩体积;解码还原画面 视频存在三类可压缩冗余:

  1. 空间冗余:同一帧内相邻像素颜色亮度接近(比如蓝天墙面)
  2. 时间冗余:相邻帧画面大部分几乎不变(背景不动,只有人物动)
  3. 视觉冗余:人眼对亮度敏感、对色彩细节不敏感,可丢失部分色彩信息

二、编码核心模块

2.1 帧类型划分,消除时间冗余(帧间预测)

编码器把画面分成 3 种帧,核心是用已编码画面预测当前帧,只存差值

  • I 帧(关键帧,Intra) 独立完整画面,不依赖任何其他帧,只做帧内压缩; 缺点:体积最大;场景切换、视频 seek 跳转必须靠 I 帧。
  • P 帧(前向预测帧,Predictive) 参考前面 I/P 帧,只记录画面移动变化(运动矢量 + 残差差值); 体积远小于 I 帧。
  • B 帧(双向预测帧,Bi-predictive) 同时参考前一帧、后一帧画面,预测精度最高,压缩率最好; 缺点:需要缓存前后帧,增加编码 / 解码延迟,直播低延时场景常禁用 B 帧。

2.2 分块(宏块 / CTU):处理空间冗余

整张画面太大,编码器会把图像切分成固定大小块处理:

  • H.264:宏块 MB,基础 16×16 像素
  • H.265 (HEVC):CTU 编码树单元,最大 64×64,可四叉树拆分成 32/16/8×8 小块 对每个块二选一压缩方式: 1)帧内预测(Intra) :只用当前帧内周边像素预测(I 帧全部用这个) 2)帧间预测(Inter) :参考其他帧,计算运动矢量 MV,记录块往哪移动,只存预测误差(残差)

2.3 变换(DCT 离散余弦变换):把空间信息转频率信息

人眼对低频(大面积明暗)敏感,高频(细微纹理、噪点)不敏感。 将像素块从「像素空间」转换为「频率系数」:

  • 低频系数:画面主体明暗,保留完整
  • 高频系数:细小纹理,后续可大幅压缩丢弃

2.4 量化 Quantization:有损压缩核心

对 DCT 变换后的频率系数除以量化步长,缩小数值、大量高频系数直接归零。

  • QP 量化参数:QP 越大,压缩越强、画质越糊;QP 越小,画质清晰、文件更大。 这一步是有损压缩根源,丢失的细节无法复原。

2.5 熵编码:无损二次压缩(无信息丢失)

图像数据分布不均匀:0 大量出现、大数值极少。 高频出现的符号用短比特码字 ,低频符号用长比特码字,整体总比特减少。 熵解码本质:查表,把变长比特串还原成原始符号。

量化后剩下的系数、运动矢量、帧类型等参数是零散数字,做无损压缩打包:

  • H.264:CAVLC、CABAC
  • H.265/AV1:CABAC(效率更高) 作用:把重复出现的短符号用更短二进制编码,进一步缩小码流体积,不损失画质。

三、解码核心模块

与编码相对应

3.1、熵解码(还原系数、MV、参数)

3.1.1. CAVLC 解码(上下文自适应变长编码)

编码侧逻辑

对残差块量化后的系数分组,给不同组合分配固定变长码表,按顺序写入比特流。

解码步骤
  1. 从码流依次读取比特;
  2. 逐位匹配预设码表,识别出:非零系数个数、拖尾 1 数量、系数幅值、符号;
  3. 还原出一整块 DCT 量化系数数组;
  4. 切换下一个块,重复读取匹配。

缺点:只使用静态码表,不根据前后数据自适应调整,压缩效率一般。

3.1.2. CABAC 解码(上下文自适应二进制算术解码,现代标准核心)

CABAC 解码分 4 大核心步骤,是工业主流:

步骤 1:二值化 Binarization

编码器会把所有待编码数字(MV 差值、QP、预测模式、残差幅值)转换成只有 0/1 的二进制串(bin)。 解码第一步:拿到算术解码器输出的一串 0/1 二进制位。

步骤 2:上下文模型选择 Context Model

CABAC 的 "自适应" 来源: 不同位置、不同类型的数据,0 和 1 出现概率完全不同。 解码器维护一组概率模型(每个模型存:0 的概率、1 的概率),读取当前数据前,根据相邻已解码块信息选择对应上下文模型。 例:平坦区域残差多为 0 → 该上下文模型 0 概率接近 90%。

步骤 3:算术解码 Arithmetic Decoding(核心运算)

算术编码不逐个符号存码字,而是把一整段数据映射到 0~1 之间的小数区间; 解码时根据输入比特流,不断缩小区间,解出单个 bin(0 或 1):

  1. 读取码流比特更新区间上下限;
  2. 用当前上下文的 0/1 概率分割区间;
  3. 判断当前码流数值落在 0 区间还是 1 区间,输出 bin;
  4. 更新上下文概率(自适应更新:如果这次解出 0,就略微提高该模型下一次 0 的概率)。
步骤 4:逆二值化 Debinarization

把连续解出的一串 0/1 二进制 bin,还原成原始十进制数值:运动矢量差值、量化残差、块划分模式等。

3.2 反量化(恢复频率系数)

解码链路中,熵解码输出量化系数 ,反量化是它的逆运算: 把缩小后的整数系数,还原回接近原始 DCT 系数的幅度,供给后续反 DCT(IDCT)

基础公式:

= 反量化后的重建频率系数

关键特性

  1. 无法完全复原原始 DCT 系数 量化时向下取整丢失小数,反量化只能得到近似值,差值就是量化误差
  2. 纯线性运算,计算极简单,硬件加速友好;
  3. 只改变系数幅值,不改变矩阵内 0 / 非 0 的分布。

真实编码不只用单一 Qstep,会搭配量化缩放矩阵(Quant Matrix),区分亮度 / 色度、帧内 / 帧间块,适配人眼视觉特性:

完整标准公式

:位置 (x,y) 对应的缩放矩阵系数

  • 低频位置 M 偏小,压缩力度轻,保留细节;
  • 高频位置 M 偏大,压缩力度重,更容易被量化成 0。

QP 越大 → Qstep 越大 → 反量化后系数幅值偏差越大 → 画面越糊、压缩率越高。

量化存在的问题

  • 块效应(分块马赛克) 不同块量化误差不一致,块边缘亮度色差断层;
  • 模糊、细节丢失 高频纹理(发丝、文字、边缘)量化后归零,反量化后依然是 0,细节永久消失;
  • 色彩失真 色度通道量化步长更大,反量化误差更高,画面容易发灰、偏色。

3.3 反 DCT 变换(转回像素残差块)

一维主要是音频使用;视频主要用二维反 DCT(分两步:先行逆变换,再列逆变换)

二维可分离,计算简化:

  1. 对系数矩阵每一行做一维反 DCT
  2. 对结果矩阵每一列做一维反 DCT 输出:像素残差块矩阵

3.4 帧内 / 帧间补偿(根据运动矢量取出参考帧块 + 叠加残差)

反 DCT 输出的是残差块(差值),不是完整像素; 补偿的核心公式统一:

重建像素 = 预测像素块 + 残差块

补偿就是拿到预测像素,和残差相加,还原当前图像块。 根据预测像素来源,分成两种:帧内补偿、帧间补偿

3.4.1 帧内补充

只使用同一帧内部、已经解码完成的相邻像素(上方、左方、左上)做参考,不依赖其他帧。 一张图里每个块的预测值,由旁边已重建好的像素插值推算。

特点
  1. 独立解码,不需要缓存其他帧,视频拖拽、卡顿恢复靠 I 帧;
  2. 只消除空间冗余,没有时间冗余压缩,同等画质下码率最大;
  3. 画面细节复杂区域切换场景时强制用 I 帧 + 帧内预测;
  4. 缺陷:平坦区域还行,大面积静态背景压缩效率远不如帧间。

3.4.2 帧间补偿

参考之前 / 之后已经完整解码并缓存的参考帧 ,不是当前帧。 通过运动矢量 MV 描述当前图像块在上 / 下参考帧的偏移位置,取出对应区域作为预测块。

关键细分:P 帧补偿 / B 帧补偿
  1. P 帧前向帧间补偿 只取过去已解码帧作为参考,单个运动矢量; 延迟低,直播低延时场景常用。

  2. B 帧双向帧间补偿 同时取前向参考帧 + 后向参考帧,生成两个预测块再加权平均; 预测精度更高,残差更小,压缩率更好; 缺点:解码需要缓存未来帧,引入额外延时,实时直播一般关闭 B 帧。

亚像素插值

真实物体运动不会刚好偏移整数像素(比如人物缓慢移动,偏移 0.5 像素)。 参考帧只有整数像素点,编码器 / 解码器通过滤波插值生成半像素、1/4 像素预测值,大幅减小残差。 H.264/H.265 使用 6 抽头滤波做亚像素插值。

优势

消除时间冗余:相邻帧背景几乎不变,只记录物体移动偏移 + 微小残差,码率远低于 I 帧。

典型缺陷
  • 快速运动物体:MV 预测不准,残差变大,出现模糊、拖影;
  • 遮挡区域:参考帧找不到对应像素,预测失效,残差巨大,产生马赛克;
  • 全局镜头移动(摇镜头):全图 MV 偏移,依然比 I 帧省码率。
对比项 帧内补偿(Intra) 帧间补偿(Inter)
参考来源 同帧已解码相邻像素 其他缓存参考帧
适用帧 I 帧 P、B 帧
压缩效率 差,码率高 优秀,码率大幅降低
解码延迟 无额外帧缓存延迟 B 帧需缓存后帧,延迟更高
依赖关系 仅块间依赖,可独立刷新 依赖其他帧,丢参考帧会大面积花屏
核心作用 消除空间冗余 消除时间冗余

3.5 拼接所有图像块

所有操作都是对独立小块(4×4/8×8/16×16/32×32 CTU)单独运算,每一步输出只是一小块像素;拼接就是把分散的小块按坐标放回原图对应位置,拼成一整帧完整画面。

3.5.1 亮度 Y、色度 U/V 分开拼接

YUV420 格式三层分辨率不同,拼接是三层独立分开执行

  1. 亮度平面 Y 分辨率等于原始画面,每个 CTU 对应一块完整 Y 像素,直接按坐标写入 Y 缓存。
  2. 色度平面 U、V 宽高均为 Y 的 1/2,色度块尺寸对应缩小一半; 一块 64×64 Y CTU,只对应一块 32×32 U、一块 32×32 V 色度块。

解码补偿完成后,分别填充 Y、U、V 三块独立内存缓冲区,互不干扰。

3.5.2 边界特殊处理

画面宽 / 高无法被 CTU 尺寸整除时,边缘会出现不完整小块(如画面宽 1923,CTU64,最后一列只剩 3 像素宽度):

  1. 编码器只编码有效画面内像素;
  2. 解码补偿后仅拷贝有效像素,超出画面范围的像素丢弃,不写入缓存;
  3. 硬件解码器会做边界填充对齐内存,方便 GPU 读取渲染。