3、H265 编码结构:CTU、CU、PU、TU、Slice 与 Tile

这一章咱们聊聊 H.265 的核心骨架。说白了,编码器怎么把一帧画面拆成小块,再一块块地处理。我刚开始接触 HEVC 时,也被 CTU、CU、PU、TU 这些缩写搞得头晕。后来在项目里调过几次码率,才真正理解它们各自扮演的角色。

你想想看,一帧 4K 画面,几百万个像素。编码器不可能一口气全处理完。它得先切块,再分层,最后才是预测和变换。这套切分逻辑,就是 H.265 的编码结构。

一句话总结:CTU 是根,CU 是枝,PU 是预测的视角,TU 是变换的粒度。Slice 和 Tile 则是并行与容错的工具。

3.1 编码树单元(CTU)—— 一切的起点

CTU 是 H.265 引入的第一个大概念。它取代了 H.264 里的宏块(Macroblock)。

H.264 的宏块固定 16x16。H.265 的 CTU 可以大到 64x64,甚至 128x128。为什么?因为高分辨率视频里,大块平坦区域(比如蓝天、白墙)很多。用大块编码,能省下大量比特。

我个人习惯把 CTU 理解成「地皮」。你拿到一块地皮,怎么盖房子,是后面 CU 的事。CTU 只负责划定边界。

参数 H.264 宏块 H.265 CTU
最大尺寸 16x16 64x64(可配置为 128x128)
亮度块尺寸 16x16 64x64 / 32x32 / 16x16
色度块尺寸 8x8 32x32 / 16x16 / 8x8
划分方式 固定 四叉树递归划分

CTU 的尺寸在编码器配置里设定。我建议在低延迟场景用 64x64,在离线转码场景可以试试 128x128。曾经有个项目,我们把 CTU 从 64 改成 128,码率直接降了 8%,但编码时间翻了一倍。嗯,这里要注意,不是越大越好。

3.2 编码单元(CU)—— 真正的决策者

CTU 划好地皮后,CU 开始决定怎么盖。CU 是编码决策的基本单位。每个 CU 要么帧内预测,要么帧间预测,二选一。

CU 的划分是递归的。一个 64x64 的 CTU,可以分成 4 个 32x32 的 CU。每个 32x32 的 CU,又可以继续分成 4 个 16x16 的 CU。一直分到最小 8x8。

这就像你拿着一把刀,不断把蛋糕切小。切到哪一层停下来?看率失真代价(RDO)。如果 32x32 的 CU 编码质量够好,就不往下切了。如果细节多,继续切。

避坑指南:我曾经在移动端芯片上踩过坑。CU 划分深度设得太深(比如允许 8x8),编码器会疯狂递归,功耗飙升。后来我限制最大划分深度为 2(即最小 16x16),画质损失很小,但功耗降了 30%。

3.3 预测单元(PU)—— 预测的视角

CU 决定用帧内还是帧间后,PU 负责具体怎么预测。PU 是预测操作的基本单位。

帧内预测的 PU 形状比较规矩。帧间预测就灵活多了。H.265 支持非对称划分(AMP)。比如一个 32x32 的 CU,PU 可以切成 32x8 加 32x24。这在处理运动物体边缘时特别有用。

我记得有一次做监控视频编码,画面里有个行人从右往左走。用对称 PU 编码,边缘总有锯齿。换成非对称 PU 后,锯齿消失了,码率还降了 5%。

预测模式 PU 划分方式 典型场景
帧内(Intra) 2Nx2N, NxN I 帧、场景切换
帧间(Inter) 2Nx2N, 2NxN, Nx2N, NxN, 2NxnU, 2NxnD, nLx2N, nRx2N P 帧、B 帧

PU 的划分信息需要写到码流里。划分越细,码流开销越大。所以编码器会在「预测精度」和「划分开销」之间做权衡。

3.4 变换单元(TU)—— 频域的转换

预测做完后,残差数据需要变换到频域。TU 就是变换的基本单位。

TU 的尺寸可以是 4x4、8x8、16x32、32x32。注意,TU 可以比 CU 大,也可以比 CU 小。TU 的划分独立于 CU 的划分。这叫残差四叉树(RQT)。

为什么 TU 要独立?因为预测和变换的「最佳粒度」不一样。预测希望块大一点,减少开销。变换希望块小一点,捕捉局部纹理。两者分开,各自优化。

注意:TU 的最大尺寸受限于变换块的最大尺寸。H.265 标准规定,变换块最大 32x32。所以即使 CTU 是 128x128,TU 也不能超过 32x32。

在实际编码中,TU 的划分深度会影响高频细节的保留。我建议在低码率场景下,限制 TU 最大为 16x16。因为 32x32 的变换在低码率下容易产生块效应。

3.5 条带(Slice)—— 独立解码的单元

Slice 是一帧图像中若干个 CTU 的集合。每个 Slice 可以独立解码。这意味着,解码器拿到一个 Slice 的数据,不需要依赖同一帧的其他 Slice。

Slice 的引入主要是为了容错。如果网络丢包了,只影响丢包所在的 Slice,其他 Slice 还能正常解码。

H.265 支持三种 Slice 类型:

  • I Slice:只使用帧内预测。不依赖任何参考帧。
  • P Slice:可以使用帧内预测和单向帧间预测。依赖前面的 I 或 P 帧。
  • B Slice:可以使用帧内预测、单向和双向帧间预测。依赖前后的帧。

我见过不少开发者把 Slice 当成并行工具。其实 Slice 的并行能力有限。因为 Slice 之间虽然独立解码,但编码时是串行的。真正适合并行的,是下面要讲的 Tile。

3.6 片(Tile)—— 真正的并行利器

Tile 是 H.265 新增的概念。它把一帧图像分成矩形区域。每个 Tile 包含整数个 CTU。Tile 之间完全独立,可以并行编码和解码。

Tile 和 Slice 的区别在于:

  • Slice 是逻辑上的独立单元,形状可以是任意 CTU 集合。
  • Tile 是物理上的矩形区域,形状规则。
  • Slice 主要用于容错,Tile 主要用于并行。

我曾经在一个 8K 实时编码项目里,把一帧分成 4 个 Tile,每个 Tile 分配给一个编码核心。编码速度直接提升了 3.5 倍。嗯,这里要注意,Tile 边界会有轻微的质量损失。因为跨 Tile 的预测被禁止了。

特性 Slice Tile
形状 任意 CTU 集合 矩形区域
并行编码 有限(依赖熵编码上下文) 完全独立
并行解码 完全独立 完全独立
主要用途 容错、低延迟 并行加速、大分辨率
码流开销 每个 Slice 有独立头 Tile 边界信息在 PPS 中

3.7 整体结构关系图

下面这张图展示了 CTU、CU、PU、TU 之间的层级关系。从一帧图像开始,先切成 CTU,CTU 递归划分成 CU,CU 再决定 PU 和 TU。

H.265 编码结构层级关系 一帧图像(Frame) 编码树单元(CTU)64x64 / 128x128 编码单元(CU)递归四叉树划分 64x64 → 32x32 → 16x16 → 8x8 预测单元(PU) 帧内:2Nx2N, NxN 帧间:对称 + 非对称(AMP) 变换单元(TU) 残差四叉树(RQT) 4x4 / 8x8 / 16x16 / 32x32 条带(Slice)独立解码 片(Tile)并行编码/解码 CTU 是根 → CU 是决策节点 → PU 和 TU 是执行单元 → Slice/Tile 是组织方式

3.8 实际编码中的选择策略

在实际编码器里,CTU、CU、PU、TU 的选择不是孤立的。编码器会遍历所有可能的划分方式,计算每种方式的率失真代价,然后选最优的。

这个过程叫率失真优化(RDO)。计算量非常大。所以编码器会做一些快速决策:

  • 如果当前 CU 的残差很小,就不继续划分了。
  • 如果当前 CU 是平坦区域,优先用大 TU。
  • 如果当前 CU 是纹理区域,优先用小 TU。

我的经验:在 x265 编码器里,可以通过 --ctu 参数控制 CTU 大小,--rect 和 --amp 控制 PU 划分,--tu-intra-depth 和 --tu-inter-depth 控制 TU 深度。调参时,先调 CTU,再调 TU,最后调 PU。这个顺序能帮你快速找到性能瓶颈。