音视频学习(三十二):VP8和VP9

发布于:2025-04-07 ⋅ 阅读:(24) ⋅ 点赞:(0)

VP8

简介

  • 全称:Video Processing 8
  • 发布者:原 On2 Technologies(2010 被 Google 收购)
  • 定位开源视频压缩标准,主要竞争对手是 H.264
  • 应用
    • WebRTC 视频通信
    • HTML5 <video> 标签(WebM)
    • FFmpeg、SRS 支持
    • YouTube(早期使用)

VP8编解码

VP8 是一种基于块的混合视频编码标准,整体思路类似 H.264。

编码核心步骤:

  1. 帧划分为宏块(Macroblock):16x16 像素
  2. 帧间预测(Inter)或帧内预测(Intra)
  3. 残差计算 + 变换编码(DCT)
  4. 量化
  5. 熵编码(基于概率模型的熵编码)
  6. 输出比特流

帧结构

组成结构

VP8 的每一帧(Frame)主要包括两个部分:

  • 帧头(Frame Header)
  • 帧数据(Frame Data)

帧头

帧头是帧的起始部分,包含了控制解码器如何处理该帧的各种信息。VP8 的帧头长度可变,分为 关键帧非关键帧 两种情况:

共通帧头结构(Frame Tag, 3字节)

0 1 2 3 4 5 6 7   8 9 10 11 12 13 14 15   16 17 18 19 20 21 22 23
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|I|P|  Version  |     Partition 0 Length (19 bits)   |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
  • I (1 bit): 关键帧标志。0 表示关键帧,1 表示非关键帧。

  • P (1 bit): 是否允许显示该帧(rarely used)。

  • Version (3 bits): 编码器版本号。

  • Partition Length (19 bits): 第一个分区(Partition 0)的长度。

关键帧附加信息(关键帧特有,附加 7 字节)

如果是关键帧(I=0),在上述 3 字节后再跟随:

0x9D 0x01 0x2A  // Start code (固定标志)
2 字节: 宽度 + flags
2 字节: 高度 + flags

这部分包含图像的尺寸信息(宽度和高度)以及颜色空间标志(通常忽略)。

帧数据(Frame Partitions)

VP8 的帧数据采用分区结构(Partitioning),通常为:

  • Partition 0:包含帧的控制信息、宏块模式等。必须完整接收后才能解码其余分区。
  • Partition 1+:包含实际的宏块残差编码数据(Residual)。

分区结构如下:

[ Partition 0 ] [ Partition 1 ] [ Partition 2 ] ... [ Partition N ]

注意:

  • Partition 0 通常为熵编码的语法元素,像预测模式、分区信息。
  • Partition 1-N 是变换系数等实际视频数据。

简化结构图

VP8 Frame:
├── Frame Tag (3 bytes)
│   ├── Key Frame? (I bit)
│   └── Partition 0 Length
├── [If Key Frame]
│   ├── Start Code (0x9D012A)
│   ├── Width, Height
├── Partition 0 (mode, control info)
├── Partition 1+ (macroblock residuals)

VP8编码技术细节

宏块划分(Macroblock)

  • 每帧划分为 16x16 的宏块
  • 每个宏块支持:
    • 4x4、8x8、16x16 子块
    • 1-4 个运动矢量
    • 多种预测模式

帧内预测(Intra Prediction)

  • 预测方向(如 H.264 中的 Intra_4x4、Intra_16x16):
    • 水平预测、垂直预测、DC 预测等
  • 使用邻近像素估计当前块的值

帧间预测(Inter Prediction)

  • 使用参考帧(last/golden/altref)中对应块
  • 具备子像素运动估计(1/4 像素精度)

变换和量化

  • 使用 DCT(Discrete Cosine Transform)
    • 4x4 DCT、16x16 DCT
  • 残差值 DCT 后进行量化

熵编码(Entropy Coding)

  • Bool编码器(Boolean Arithmetic Coding)
    • 类似算术编码,但更轻量
  • 使用上下文建模(Context Modeling)

比特流结构

+----------------------------------+
| 帧标志位 (Frame Tag)             |
|  - 帧类型(1bit)                  |
|  - 版本号(3bit)                  |
|  - 显示标志(1bit)                |
|  - 帧长度(19bit)                 |
+----------------------------------+
| 帧头部数据                       |
|  - 分辨率                        |
|  - 色彩空间                      |
|  - 参考帧标识                    |
|  - Loop Filter 参数              |
|  - Segment 参数                  |
+----------------------------------+
| 编码宏块数据                     |
+----------------------------------+

特点

特性 说明
宏块大小 固定 16x16
支持子块划分 支持
多参考帧 支持(Last, Golden, AltRef)
子像素精度 支持 1/4 像素
去块滤波器 内置
分辨率 无限制(理论上)
CABAC / CAVLC 无,使用 Bool 编码
B 帧 不支持
可扩展性 不支持 SVC(VP9 才支持)

源码结构(libvpx)

libvpx 项目概览

GitHub 地址:https://github.com/webmproject/libvpx

libvpx/
├── build/                  # 构建系统(make, cmake等)
├── configure               # 自动配置脚本
├── examples/               # 示例代码(编码、解码、实时编码等)
├── test/                   # 单元测试
├── tools/                  # 工具,如 vpxdec/vpxenc 封装
├── vpx/                    # 公共接口
├── vpxdec/                 # 解码器主程序
├── vpxenc/                 # 编码器主程序
├── vp8/                    # ✅ VP8 专用核心代码目录
│   ├── common/             # 公共结构、宏块、DCT、IDCT等
│   ├── decoder/            # 解码器实现
│   ├── encoder/            # 编码器实现
│   └── wrapper/            # 封装接口(如 vpx_codec_iface_t)
└── vp9/                    # VP9 对应目录

核心目录说明

vp8/common/ — 编解码公用模块

包含 VP8 的核心数据结构、算法实现:

文件/目录 功能
blockd.h 宏块(Macroblock)结构定义
entropymode.c/h 熵编码模式表
entropy.c/h Bool-based 熵编码实现
idctllm.c 快速 IDCT 实现
loopfilter.c/h 去块滤波器实现
mv.h 运动矢量定义
modecont.h 预测模式上下文表
treecoder.c Huffman 树解码辅助模块
onyxc_int.h 解码器内部数据结构
alloccommon.c 内存分配器
postproc.c 解码后处理滤波器
vp8/encoder/ — 编码器实现
文件/目录 功能
onyx_if.c 编码器主入口,初始化、编码过程
encodeframe.c 帧编码主流程(帧内/帧间)
ratectrl.c 码率控制模块
rdopt.c RDO(率失真优化)核心
modecosts.c 模式代价估算
picklpf.c 去块滤波器参数选择
quantize.c 残差量化
motion_search.c 运动估计核心(Diamond、Hexagon)
variance.c SAD/SSD 误差计算
firstpass.c 首遍编码(为双遍做统计)
temporal_filter.c 临时帧过滤(可用于 AltRef)
vp8/decoder/ — 解码器实现
文件/目录 功能
onyxd_if.c 解码器主流程
decodeframe.c 解码单帧流程
detokenize.c 熵编码解码器(Token -> 残差)
idct_blk.c IDCT 模块
reconinter.c 帧间重建
reconintra.c 帧内重建

vp8/wrapper/

对外接口(API)封装,实现 vpx_codec_iface_t 接口,供上层使用,比如 FFmpeg、WebRTC:

文件 功能
vp8_cx_iface.c VP8 编码器接口封装
vp8_dx_iface.c VP8 解码器接口封装

这些接口被 vpx_codec_*() 系列函数调用,如:

c复制编辑vpx_codec_enc_config_default(&vpx_codec_vp8_cx_algo, &cfg, 0);
vpx_codec_enc_init(&codec, &vpx_codec_vp8_cx_algo, &cfg, 0);

编码流程简析(VP8)

文件核心在 vp8/encoder/encodeframe.conyx_if.c

编码主流程:

// onyx_if.c
vp8_get_compressed_data():
    -> encode_frame_to_data_rate()         // 编码主流程
       -> vp8cx_encode_frame()
          -> vp8_first_pass()              // 如果是两遍编码
          -> motion_estimation()
          -> mode decision / RDO
          -> DCT -> Quant -> Entropy encode
          -> loopfilter()

解码流程简析(VP8)

c// onyxd_if.c
vp8_decode_frame():
    -> read frame header
    -> token parsing (detokenize.c)
    -> decode macroblocks (decodeframe.c)
    -> IDCT, loop filter, reconstruction

实用命令:libvpx 编码示例

# 编码 VP8 视频(libvpx 提供的工具)
vpxenc input.y4m --codec=vp8 --output=output.webm --threads=4 --target-bitrate=1000

# 解码 VP8 文件
vpxdec output.webm

VP9

简介

  • 发布者:Google(2013年开源)
  • 定位开放的高效视频压缩标准(H.265 竞争者)
  • 应用
    • YouTube(主流编码格式)
    • Chrome、Firefox、Edge、Android 原生支持
    • WebRTC(与 VP8 并存)
    • 支持 WebM 封装格式(.webm)

核心特性(相较于 VP8)

特性 VP8 VP9
参考帧 最多 3 帧 最多 8 帧
分区结构 固定 16x16 宏块 可递归划分至 4x4 子块
压缩效率 普通 提升约 30%-50%
运动估计精度 1/4 像素 支持至 1/8 像素
并行解码 支持有限 支持 Tile 并行解码
色彩空间 8-bit YUV 支持 10/12-bit、BT.2020 等
可扩展编码(SVC) 不支持 支持

帧结构

组成结构

一个 VP9 Frame(画面)主要由以下部分组成:

[ Uncompressed Header ]
[ Compressed Header ]
[ Tile Data ]

Uncompressed Header(非压缩帧头)

非压缩帧头是 VP9 帧最前面的部分,不使用熵编码,含有控制信息。

主要字段说明:

字段名 位数(大致) 含义
Frame Marker 2 bits 固定值 0b10,用于标记 VP9 帧
Profile 2 bits 取值 0~3,表示 VP9 Profile(色深/采样)
Show Existing Frame 1 bit 是否显示已有帧
Frame Type 1 bit 关键帧(0)/非关键帧(1)
Show Frame 1 bit 是否显示该帧
Error Resilient 1 bit 错误恢复模式(影响帧间预测)
Intra Only 1 bit 仅帧内预测(只对非关键帧)
Reset Frame Context 2 bits 控制解码上下文的重置策略
Color Space 3 bits 颜色空间,如 BT.601/BT.709 等
Bit Depth 1 bit 8 / 10 / 12 bits
Frame Width / Height 32 bits 图像宽高信息
Render Width / Height 32 bits 显示宽高(可能不同于编码宽高)
Loop Filter Params 可变 循环滤波配置
Quantization Params 可变 量化参数
Segmentation Params 可变 分块质量控制参数
Tile Info 可变 瓦片划分信息(行数列数)

注意:关键帧会包含完整帧宽高和颜色信息;非关键帧可以引用之前帧的元数据。

Compressed Header(压缩帧头)

使用熵编码(基于上下文概率表)压缩,包含预测模式、运动矢量、块划分模式等控制信息。

包含内容:

  • 预测模式(帧内/帧间)
  • 块划分方式
  • 运动矢量参考索引(ref_frame_idx)
  • skip_flags
  • transform size 选择
  • DCT/ADST 类型选择
  • Token 语法表

这个部分决定了解码时如何还原帧。

Tile Data(图像数据部分)

VP9 使用 Tile(瓦片)结构进行并行解码,Tile 是按列分割的一块图像区域,具有独立编码结构。

Tile 特点:

  • 每个 Tile 可独立解码(适合多线程)
  • Tile 数据包括:
    • 残差系数(变换块)
    • skip 模式信息
    • CDF 熵码表更新
  • Tile 是 VP9 实际图像内容的主要组成部分

你可以认为一帧由多个 tile 按顺序拼成图像。

VP9 帧结构图示

┌──────────────────────────┐
│ Frame Marker (2 bits)    │
│ Profile (2 bits)         │
│ Show Frame / Keyframe    │
├──────────────────────────┤
│ Uncompressed Header      │
│  ├── Width / Height      │
│  ├── Color Space         │
│  ├── Loop Filter         │
│  └── Quant Params        │
├──────────────────────────┤
│ Compressed Header (熵编码)│
│  ├── Mode Info           │
│  ├── Motion Vectors      │
│  └── Token Partition     │
├──────────────────────────┤
│ Tile Data                │
│  ├── Tile 0              │
│  ├── Tile 1              │
│  └── Tile N              │
└──────────────────────────┘

关键帧 vs 非关键帧 区别

关键帧(Keyframe) 非关键帧(Inter Frame)
是否独立 否,需要参考前帧
包含信息 完整图像元信息 可复用前帧元信息
参考帧使用 多参考帧/双向预测
用途 场景切换、起始帧 正常视频传输帧

VP9编码技术细节

Raw Frame (YUV)
   ↓
Block Partitioning(分块)
   ↓
Prediction(预测:Intra / Inter)
   ↓
Transform(变换:DCT / ADST)
   ↓
Quantization(量化)
   ↓
Entropy Coding(上下文熵编码)
   ↓
Bitstream(VP9 Frame)

Superblock & Block Partitioning(超级块与分块)

Superblock
  • 固定大小 64x64 像素
  • VP9 使用 superblock 替代 VP8 的宏块(16x16)
分块模式
  • 每个 superblock 可递归划分为更小块(最大到 4x4)
  • 块划分支持 10 种模式,如:
64x64 → 32x32 → 16x16 → 8x8 → 4x4
+ 一些非对称划分(e.g. 32x16, 16x32)
优点
  • 允许更细粒度的局部控制(纹理复杂区域划小块,背景区域保大块)
  • 类似 HEVC 中 CU/PU/TU 的结构

Prediction(帧预测)

帧内预测(Intra Prediction)
  • 预测当前块的数据来自当前帧已解码区域
  • 支持 10 种帧内预测模式(DC、水平、垂直、方向模式等)
帧间预测(Inter Prediction)
  • 预测当前块来自于 参考帧
  • 参考帧支持:Last, Golden, AltRef(最多 3 个)
  • 支持:
    • 单向预测(Single Reference)
    • 双向预测(Compound Prediction)
    • 亚像素精度运动矢量(1/8 像素)
    • 运动矢量预测与补偿(MVP)
    • 全局运动建模(Global Motion)
优点
  • 相较 VP8,大幅提升运动估计准确度和压缩效率

Transform(变换)

VP9 使用 DCT/ADST 进行频域转换,压缩空间冗余。

Block Size 支持变换类型
4x4 DCT / ADST
8x8 DCT / ADST
16x16 DCT only
32x32 DCT only
64x64 DCT only

水平/垂直可用不同变换类型,混合变换提升适应性。

Quantization(量化)

  • 各个块的变换系数会进行量化压缩
  • 使用 自适应量化参数,支持 segmentation(每块不同 QP)
  • QP(量化参数)影响压缩率和质量:数值越大,压缩越强,画质越差

Loop Filters(环路滤波)

用于消除块边界伪影(blocking artifacts):

包括:

  • Deblocking Filter(去块效应)
  • Adaptive Loop Filter
  • Clamping Filter
  • CDEF / Sgrproj(在 AV1 中)

VP9 提供灵活的滤波控制参数,按块启用或关闭滤波器。

Segmentation(分段控制)

将图像划分为多个 segment,每个 segment 可设置不同参数:

  • QP(量化参数)
  • Loop filter 强度
  • 预测模式约束
  • 可用于 ROI 编码(重点区域更高质量)

Tile-based 并行处理

Tile 是图像按列划分的子区域(不是 macroblock)

  • 每个 tile 可独立解码
  • 支持并行编码(多线程)
  • Tile Size 可调节(编码效率 vs 并行效率)

好处:

  • 实时编码优化(尤其适用于 WebRTC)
  • 硬件友好,可并行硬解

Entropy Coding(熵编码)

使用 上下文自适应二进制算术编码(Context Adaptive Binary Arithmetic Coding, CABAC) 类似结构:

  • 每个语法元素使用概率模型编码
  • 多个上下文模型(如 skip_flag、mv、transform_size 等)
  • 兼容多参考帧、多块模式预测

VP9 Profiles & 色深支持

Profile Bit Depth 色度格式 用途
0 8-bit 4:2:0 WebM, YouTube 默认
1 8-bit 4:2:2 / 4:4:4 高质量视频
2 10/12-bit 4:2:0 HDR 视频
3 10/12-bit 4:2:2 / 4:4:4 专业场景

比特流结构

[ Frame Header ]
[ Compressed Frame Data ]
    └── [ Tile Data ]
    └── [ Residual Data (Transformed & Quantized) ]
    └── [ Motion Vectors (for Inter Frames) ]
    └── [ Entropy-coded Data (Tokens) ]

特点

特性 VP9 H.264 H.265
压缩效率 很高
编码复杂度 很高
硬件支持 中(新设备支持) 普遍
授权成本 免费 付费 付费
多线程支持 好(Tile) 一般 优秀

源码结构(libvpx)

libvpx/
├── vp8/                    # VP8 编解码器实现
├── vp9/                    # VP9 编解码器实现 ✅ ← 重点
│   ├── encoder/            # VP9 编码器实现
│   ├── decoder/            # VP9 解码器实现
│   ├── common/             # 编码器 & 解码器共有的数据结构、工具函数
│   └── ...
├── build/                  # 构建脚本
├── test/                   # 单元测试代码
├── tools/                  # 命令行工具(vpxenc/vpxdec)
├── examples/               # 示例代码(使用 libvpx 编解码)
├── vpx/                    # 通用接口层
│   ├── vpx_encoder.h       # 编码器 API 入口
│   ├── vpx_decoder.h       # 解码器 API 入口
│   └── vpx_codec.h         # 通用编解码框架
└──

编码器核心结构(vp9/encoder/

文件名 功能
vp9_encoder.h/c 编码器核心控制接口
vp9_encodeframe.c 每帧编码主逻辑(块划分、预测、残差、熵编码)
vp9_ratectrl.c 码率控制模块(CBR/VBR)
vp9_pickmode.c 预测模式选择(Intra / Inter)
vp9_rdopt.c 率失真优化(Rate-Distortion)关键模块 ✅
vp9_quantize.c DCT 系数量化
vp9_tokenize.c 熵编码预处理(生成 token)
vp9_mcomp.c 运动估计(Motion Estimation)
vp9_segmentation.c 分段编码(不同 QP 区域)
vp9_skin_detection.c 人脸/肤色检测(可用于提升视觉质量)
vp9_aq_variance.c 自适应质量 AQ 策略
... 其他模块如 tile、滤波器、变换 等

解码器核心结构(vp9/decoder/

文件名 功能
vp9_decoder.h/c 解码器核心控制逻辑
vp9_decodeframe.c 解码主逻辑(解析帧头、构建帧)
vp9_dboolhuff.c 帧头解析 + 熵解码器
vp9_decodemv.c 解码运动矢量相关信息
vp9_detokenize.c 熵解码后的系数还原
vp9_reader.h 比特流解析器(BitReader)
... 变换、滤波、预测、Tile 重建等

通用模块(vp9/common/

文件名 功能
vp9_common.h 公共定义(帧类型、块大小等)
vp9_blockd.c/.h 块级数据结构、预测模式枚举
vp9_mv.h 运动矢量数据结构
vp9_entropy.c 熵编码上下文模型(上下文概率表)
vp9_loopfilter.c 去块滤波器(Deblocking Filter)
vp9_seg_common.c 分段结构解析
vp9_tile_common.c Tile 分区结构解析
vp9_pred_common.c 预测函数(帧内帧间)
vp9_inv_txfm.c 反变换(IDCT / IADST)

编码流程简析(VP9)

入口在 examples/vpxenc.c

  1. main() 中设置参数后调用:

    vpx_codec_enc_init()
    
  2. 使用 vpx_codec_encode() 编码一帧 YUV,会调用:

    vp9_get_cx_interface()vp9_encode()vp9_encode_frame()
    
  3. 编码流程深入到:

    • vp9_rd_pick_inter_mode_sb():选最优 Inter 模式
    • vp9_transform_block():DCT/ADST
    • vp9_quantize():量化
    • vp9_entropy_coding():熵编码 token
    • vp9_write_bitstream():写入 VP9 比特流

解码流程简析(VP9)

入口在 examples/vpxdec.c

  1. 初始化解码器 vpx_codec_dec_init()

  2. 调用 vpx_codec_decode() 解码 VP9 比特流

  3. 实际进入 VP9 解码器:

    vp9_get_dx_interface()vp9_decode()vp9_decode_frame()
    
  4. 解码流程:

    • vp9_read_frame_header()
    • vp9_decode_tiles():Tile 并行重建
    • vp9_loopfilter_frame():去块滤波
    • 输出 YUV 图像帧

FFMPEG推拉VP8/VP9

推流

推送 VP8 流(RTMP 协议)

ffmpeg -re -i input.mp4 -c:v libvpx -b:v 1M -c:a libopus -f flv rtmp://your-server-ip/live/stream_key
  • -re:以实时速度读取输入文件
  • -i input.mp4:输入文件(可以是 .mp4.y4m.avi 等)
  • -c:v libvpx:使用 VP8 编码器(libvpx)
  • -b:v 1M:设置视频比特率为 1Mbps
  • -c:a libopus:使用 Opus 编码器进行音频编码
  • -f flv:指定输出格式为 FLV(适用于 RTMP 流媒体)
  • rtmp://your-server-ip/live/stream_key:RTMP 流媒体服务器地址和流密钥

推送 VP9 流(RTMP 协议)

ffmpeg -re -i input.mp4 -c:v libvpx-vp9 -b:v 1M -c:a libopus -f flv rtmp://your-server-ip/live/stream_key
  • -c:v libvpx-vp9:使用 VP9 编码器(libvpx-vp9)
  • 其他参数与推送 VP8 流类似

拉流

拉取 VP8 流(RTMP 协议)

ffmpeg -i rtmp://your-server-ip/live/stream_key -c:v libvpx -c:a libopus output.webm
  • -i rtmp://your-server-ip/live/stream_key:指定 RTMP 流媒体源地址
  • -c:v libvpx:解码 VP8 视频流
  • -c:a libopus:解码音频流为 Opus 格式
  • output.webm:指定输出文件格式为 WebM

拉取 VP9 流(RTMP 协议)

ffmpeg -i rtmp://your-server-ip/live/stream_key -c:v libvpx-vp9 -c:a libopus output.webm
  • -c:v libvpx-vp9:解码 VP9 视频流
  • 其他参数与拉取 VP8 流类似

其他协议

推流至 RTSP 服务器(VP8/VP9)

ffmpeg -re -i input.mp4 -c:v libvpx -b:v 1M -c:a libopus -f rtsp rtsp://your-server-ip:port/stream
  • -f rtsp:设置 RTSP 协议输出
  • rtsp://your-server-ip:port/stream:指定 RTSP 流地址

拉取 RTSP 流(VP8/VP9)

ffmpeg -i rtsp://your-server-ip:port/stream -c:v libvpx -c:a libopus output.webm

拉取 HLS 流(VP8/VP9)

如果流源为 HLS(HTTP Live Streaming),可以使用以下命令来拉取 VP8 或 VP9 流:

ffmpeg -i http://your-server-ip/hls/stream.m3u8 -c:v libvpx -c:a libopus output.webm
  • -i http://your-server-ip/hls/stream.m3u8:指定 HLS 流的 M3U8 播放列表
  • output.webm:输出为 WebM 格式