3. 数字视频表示与预处理
编码器真正“吃”进去的并不是屏幕上的彩色画面,而是一串按特定顺序排列的字节。理解数字视频的表示方式,是读懂 H.264/H.265 码流结构、预测方式和码率来源的前提。本章围绕 RGB 与 YUV 的转换、色度二次采样、帧类型、时间戳、帧序列组织,以及原始视频数据的格式与读取方法展开,帮助你把“看得见的画面”与“编码器眼中的数据”对应起来。
一、从 RGB 到 YUV:颜色空间转换
显示器以 RGB 三原色加色混合呈现颜色,每个像素由红、绿、蓝三个分量构成。但人眼对亮度(Luma)远比对色度(Chroma)敏感,因此视频编码普遍采用 YUV(YCbCr)颜色空间,把亮度与色度分离,从而对色度“少花点笔墨”。
以最常见的 BT.601 标清系数为例,RGB 转 YUV 的公式为:
Y = 0.299R + 0.587G + 0.114B
Cb = -0.169R - 0.331G + 0.500B + 128
Cr = 0.500R - 0.419G - 0.081B + 128
其中 Y 取值范围为 16~235(有限范围),Cb、Cr 为 16~240,这是“TV Range”;而计算机图形常用 0~255 的“Full Range”。范围不一致会导致画面发灰或过曝,是工程中常见的坑。BT.709 用于高清,BT.2020 用于 4K/HDR,系数不同,转换结果也不同。
二、色度二次采样:4:4:4、4:2:2、4:2:0
色度二次采样(Chroma Subsampling)利用人眼对色度不敏感的特性,降低色度分量的采样率。记法 J:a:b 中,J 为亮度采样宽度,a 为第一行色度采样数,b 为第二行色度采样数。
| 格式 | 亮度采样 | 色度采样 | 数据量(相对4:4:4) | 典型场景 |
|---|---|---|---|---|
| 4:4:4 | 每像素1个Y | 每像素1个Cb、Cr | 100% | 影视后期、无损 |
| 4:2:2 | 每像素1个Y | 水平减半 | 约66.7% | 专业采集、广播 |
| 4:2:0 | 每像素1个Y | 水平垂直各减半 | 50% | H.264/H.265主流 |
4:2:0 意味着每 2×2 的四个亮度像素共享一组 Cb、Cr。以 1920×1080 为例,Y 有 2073600 个样本,Cb、Cr 各只有 518400 个,总样本数约为 3110400,相比 4:4:4 的 6220800 减少一半。这正是 H.264/H.265 默认采用 4:2:0 的原因。
三、视频帧类型:I、P、B 帧
编码器不会把每一帧都独立压缩,而是利用时间冗余。帧类型决定了帧之间的依赖关系:
- I 帧(Intra):帧内编码,不依赖其他帧,可独立解码,是随机访问的入口,但体积最大。
- P 帧(Predicted):前向预测,参考前面的 I/P 帧,体积较小。
- B 帧(Bi-predicted):双向预测,可参考前后帧,压缩率最高,但引入解码顺序与显示顺序的差异。
由于 B 帧的存在,编码顺序(Decode Order)与显示顺序(Display Order)不再一致,需要靠 POC(Picture Order Count)来标记显示次序。这也是时间戳与帧序列组织要解决的问题。
四、时间戳与帧序列组织
视频是“按时间播放的帧序列”,时间戳是播放同步的基准。常见概念包括:
- PTS(Presentation Time Stamp):显示时间戳,决定这一帧何时被显示。
- DTS(Decoding Time Stamp):解码时间戳,决定这一帧何时被送入解码器。
- 帧率(fps):如 25fps 表示每帧间隔 40ms,30fps 为 33.33ms。
GOP(Group of Pictures)是帧序列组织的基本单位,通常以 I 帧开始,包含若干 P/B 帧。例如 IPBBPBBP… 结构,GOP 长度决定随机访问的粒度和压缩效率。GOP 越长,压缩率越高,但拖动定位越慢。
五、原始视频数据格式与读取方法
编码前的原始视频通常以 YUV 裸数据保存,常见格式有 I420(YUV420P)、NV12、NV21、YV12 等。I420 的存储顺序为:先存全部 Y,再存全部 U,最后存全部 V。NV12 则是 Y 平面后跟交错的 UV 平面。
# 用 ffmpeg 将 MP4 解码为 I420 裸流
ffmpeg -i input.mp4 -pix_fmt yuv420p -f rawvideo output.yuv
# 读取一帧 1920x1080 的 I420 数据大小
# Y: 1920*1080 = 2073600 字节
# U: 960*540 = 518400 字节
# V: 960*540 = 518400 字节
# 合计: 3110400 字节/帧
读取时按帧大小循环读取即可:先读 Y 平面,再读 U、V 平面,然后送入编码器。若格式判断错误,画面会出现颜色错乱或“绿屏”。
六、案例与常见误区复盘
假设你从摄像头拿到 NV12 数据,却按 I420 解析,结果就是颜色错位、画面偏绿。正确做法是先确认 pix_fmt,再按对应平面布局读取。另一个常见误区是忽略色彩范围:把 Limited Range 的 YUV 当 Full Range 显示,画面会发灰;反之则会过曝。
此外,很多人以为“帧率就是时间戳”,其实帧率只决定间隔,PTS/DTS 才是同步依据。在含 B 帧的码流中,解码顺序与显示顺序不同,必须依赖 POC 与时间戳还原正确播放顺序。