Slice(切片)详解
Slice(切片)详解
在视频编码(H.264/AVC, H.265/HEVC)中,Slice(切片)是帧(Frame/Picture)的一个独立编码单元。
简单来说:一个帧(Frame)可以被分割成一个或多个 Slice。
📖一、核心概念
1. 定义
Slice是一组连续的CTU(编码树单元)或宏块(Macroblock)的集合。
- 它是熵编码(CABAC/CAVLC)的独立单元。
- 每个 Slice 可以独立解码,不依赖同一帧中的其他 Slice。
2. 层级关系
视频序列 (Sequence) └─> GOP (图像组) └─> Frame/Picture (帧) └─> Slice (切片) <-- 独立解码单元 └─> CTU / Macroblock (编码块) └─> CU / Sub-block🏗️二、为什么要使用 Slice?
Slice 的存在主要为了解决以下三个关键问题:
1. 并行处理(Parallel Processing)
- 多线程编码/解码:不同的 Slice 可以由不同的 CPU 核心同时处理。
- Wavefront Parallel Processing (WPP):虽然 WPP 是基于 CTU 行的,但 Slice 提供了更粗粒度的并行边界。
- Tile:H.265 引入了 Tile(瓦片),与 Slice 类似但更灵活,两者常结合使用。
2. 错误恢复(Error Resilience)
- 隔离错误:如果网络传输中某个 Slice 的数据包丢失或损坏,解码器只会丢弃该 Slice,而不会影响同一帧中其他 Slice 的解码。
- 重传机制:在网络视频(如视频会议)中,可以只重传出错的 Slice,而不是整帧。
3. 随机访问与延迟控制
- 低延迟编码:可以将一帧分成多个小 Slice,编码完一个 Slice 就发送一个,无需等待整帧编码完成。
- 灵活更新:支持部分区域更新(如屏幕共享中只有鼠标周围变化)。
🧩三、Slice 的类型
Slice 的类型通常与其所在的帧类型相关,但也有限制:
| Slice 类型 | 说明 | 参考关系 |
|---|---|---|
| I-Slice | 仅使用帧内预测 | 不参考其他帧,也不参考同帧其他 Slice |
| P-Slice | 使用帧内或单向帧间预测 | 可参考之前的 I/P Slice |
| B-Slice | 使用帧内或双向帧间预测 | 可参考前后 I/P/B Slice |
⚠️注意:一个 P 帧中可以包含 I-Slice、P-Slice,但不能包含 B-Slice。一个 B 帧中可以包含 I、P、B 三种 Slice。
🔧四、Slice 的结构与语法
每个 Slice 由两部分组成:
1. Slice Header(切片头)
包含解码该 Slice 所需的所有控制信息:
- Slice Type:I, P, or B
- Pic Parameter Set (PPS) ID:指向使用的参数集
- Frame Num / POC:帧编号
- Reference Picture List:参考帧列表
- Quantization Parameter (QP):初始量化参数
- Deblocking Filter Control:去块滤波参数
- Entry Points:如果是 WPP,包含每行的起始位置
2. Slice Data(切片数据)
- 包含该 Slice 内所有 CTU/Macroblock 的编码数据。
- 经过熵编码(CABAC/CAVLC)的比特流。
📐五、Slice 的分割模式
在 H.265/HEVC 中,Slice 的划分非常灵活:
1. Raster Scan Order(光栅扫描顺序)
默认情况下,Slice 按照从左到右、从上到下的顺序包含连续的 CTU。
Frame (4x4 CTUs) ┌────┬────┬────┬────┐ │ S1 │ S1 │ S2 │ S2 │ <-- Slice 1: 前两个 CTU ├────┼────┼────┼────┤ │ S1 │ S1 │ S2 │ S2 │ <-- Slice 2: 后两个 CTU ├────┼────┼────┼────┤ │ S3 │ S3 │ S4 │ S4 │ ├────┼────┼────┼────┤ │ S3 │ S3 │ S4 │ S4 │ └────┴────┴────┴────┘2. Rectangular Slice(矩形切片)
H.265 允许定义矩形区域的 Slice,便于与 Tile 对齐。
3. Single Slice per Frame
最简单的情况,整个帧就是一个 Slice。大多数高清电影编码采用这种方式以最大化压缩效率(因为 Slice 边界会限制预测和熵编码的上下文)。
⚡六、Slice 与并行编码的关系
1. Slice-based Parallelism
- 独立性:Slice A 的编码完全不依赖 Slice B。
- 线程分配:Thread 1 编码 Slice 1,Thread 2 编码 Slice 2。
- 缺点:Slice 边界处的像素不能相互预测,导致压缩效率略微下降(约 1-5%)。
2. Wavefront Parallel Processing (WPP)
- H.265 的特性。
- 允许在同一 Slice 内,不同的 CTU 行并行处理。
- 依赖:第 N 行的 CTU 需要等待第 N-1 行对应位置的 CTU 编码完成。
- 优势:比 Slice 并行更细粒度,压缩损失更小。
3. Tile
- H.265 引入。
- 将帧划分为独立的矩形区域(Tile)。
- Tile 之间完全独立(无预测依赖,无熵编码依赖)。
- Slice 可以跨越 Tile,也可以限制在 Tile 内部。
💻七、X265 中的 Slice 实现
在 X265 源码中,Slice 相关的逻辑主要分布在encoder/slice.cpp和encoder/encoder.cpp。
1. Slice 结构体定义
// source/common/slice.hclassSlice{public:intm_sliceType;// SLICE_I, SLICE_P, SLICE_Bintm_sliceIdx;// Slice 索引intm_sliceCurStartCUAddr;// 起始 CTU 地址intm_sliceCurEndCUAddr;// 结束 CTU 地址Frame*m_frame;// 所属帧PPS*m_pps;// 图片参数集SPS*m_sps;// 序列参数集// 参考帧列表RefPicList m_refPicList[2];intm_sliceQp;// Slice 级别的 QPboolm_deblockingFilterDisable;// 是否禁用去块滤波};2. Slice 的创建与划分
在Encoder::encode()过程中:
// encoder/encoder.cpp (简化逻辑)voidEncoder::compressFrame(Frame*curFrame){// 1. 确定 Slice 划分策略// 根据参数 --slice-max-size, --slice-max-cpus 等intnumSlices=calculateNumSlices(curFrame);// 2. 初始化每个 Slicefor(inti=0;i<numSlices;i++){Slice*slice=curFrame->getSlice(i);slice->initSliceData();// 设置 Slice 覆盖的 CTU 范围slice->m_sliceCurStartCUAddr=startCTU[i];slice->m_sliceCurEndCUAddr=endCTU[i];}// 3. 并行编码 Sliceif(numSlices>1){// 启动多个线程,每个线程处理一个或多个 SlicethreadPool->enqueue(compressSliceTask,slice1);threadPool->enqueue(compressSliceTask,slice2);}else{// 单 Slice,直接处理compressSlice(curFrame->getSlice(0));}}3. Slice 级别的熵编码初始化
每个 Slice 开始时,CABAC 上下文会被重置或初始化:
// encoder/entropy.cppvoidEntropy::startSlice(Slice*slice){// 加载 PPS 中定义的 CABAC 初始化表loadContextModels(slice->m_pps->cabacInitIdc);// 重置概率模型resetProbabilities();}📊八、Slice 对压缩效率的影响
| 配置 | 压缩效率 | 并行度 | 错误恢复能力 | 延迟 |
|---|---|---|---|---|
| 1 Slice/Frame | 最高 | 低 (依赖 WPP/Tile) | 差 (丢包损整帧) | 高 (需整帧编完) |
| Multiple Slices | 略低 (-1~5%) | 高(线程级并行) | 好(丢包损局部) | 低(可流式传输) |
最佳实践:
- 存储/蓝光:通常使用 1 个 Slice,追求极致压缩。
- 直播/会议:使用多个 Slice(或 Tile),追求低延迟和抗丢包。
🆚九、Slice vs Tile vs WPP
这是 H.265 中三个容易混淆的并行/分割概念:
| 特性 | Slice | Tile | WPP (Wavefront) |
|---|---|---|---|
| 基本单位 | CTU 序列 | 矩形区域 | CTU 行 |
| 预测依赖 | 无(Slice 间独立) | 无(Tile 间独立) | 有(依赖上一行) |
| 熵编码依赖 | 无(独立 CABAC) | 无(独立 CABAC) | 有(共享 CABAC 状态) |
| 并行粒度 | 粗 (帧级/区域级) | 中 (区域级) | 细 (行级) |
| 压缩损失 | 中等 | 小 | 极小 |
| 主要用途 | 错误恢复、旧兼容 | 并行编码、随机访问 | 高分辨率并行 |
💡十、总结
Slice 是什么?
- 帧内部的独立编码子单元。
- 熵编码和预测的边界。
Slice 的作用:
- 并行化:允许多线程同时编码/解码不同区域。
- 容错性:限制传输错误的影响范围。
- 灵活性:支持低延迟流式传输。
在 X265 中:
- 通过
--slice-max-size或--slice-max-cpus控制 Slice 数量。 - Slice 越多,并行度越高,但压缩率略微下降。
- 对于大多数高质量归档场景,建议保持单 Slice 或少数 Slice,配合 WPP 使用。
