目录
[1. pix_fmt 决定的是「内存布局」,不是「颜色」](#1. pix_fmt 决定的是「内存布局」,不是「颜色」)
[2. YUV 的三个关键维度](#2. YUV 的三个关键维度)
[二、YUV Planar 三剑客](#二、YUV Planar 三剑客)
[1.YUV420P(AV_PIX_FMT_YUV420P,别名 I420)](#1.YUV420P(AV_PIX_FMT_YUV420P,别名 I420))
[三、Semi-Planar:NV12 / NV21](#三、Semi-Planar:NV12 / NV21)
[四、Packed YUV(采集卡 / UVC 摄像头)](#四、Packed YUV(采集卡 / UVC 摄像头))
[1.YUYV422(AV_PIX_FMT_YUYV422,也叫 YUY2)](#1.YUYV422(AV_PIX_FMT_YUYV422,也叫 YUY2))
[五、RGB 家族(给人看 / 给算法看)](#五、RGB 家族(给人看 / 给算法看))
[3.RGBA / BGRA / ARGB](#3.RGBA / BGRA / ARGB)
[六、10bit / HDR 常用格式(别再只认 8bit 了)](#六、10bit / HDR 常用格式(别再只认 8bit 了))
[七、带 Alpha 的 YUV](#七、带 Alpha 的 YUV)
[坑 1:颜色发灰](#坑 1:颜色发灰)
[坑 2:sws_scale 崩溃](#坑 2:sws_scale 崩溃)
[坑 3:Stride ≠ Width](#坑 3:Stride ≠ Width)
觉得有用,就请您帮忙点赞转发收藏吧,您的鼓励是我创作的动力,多谢看官。
由于能力水平有限,文中的错误或不严谨的地方在所难免,还请批评指正。
做音视频开发的人,基本都曾被 pix_fmt坑过:解码出来是 nv12,OpenCV 死活打不开,转成 yuv420p后颜色发灰。sws_scale崩了,发现是 planar / semi-planar 没分清。硬解出来一堆 cuda、vaapi不知道怎么落地,本文就把FFmpeg 里最常见、最实用的 pix_fmt按「是什么 / 内存怎么排 / 用在哪」捋一遍。
一、先建立两个底层认知
1. pix_fmt 决定的是「内存布局」,不是「颜色」
同一个画面,可以存成:
-
YUV420P(省一半带宽)
-
NV12(给 GPU/硬编吃)
-
RGB24(给人眼和 OpenCV 看)
颜色最终一样,但字节排列方式完全不同。
FFmpeg 中对应的是枚举:
enum AVPixelFormat;
命令行里就是:
-pix_fmt yuv420p
查全部支持格式:
ffmpeg -pix_fmts
2. YUV 的三个关键维度
| 维度 | 说明 |
|---|---|
| 色度子采样 | 444 / 422 / 420 |
| 存储方式 | Planar / Semi-Planar / Packed |
| 位深 | 8bit / 10bit / 16bit |
人眼对亮度敏感、对色度不敏感,所以 YUV 能「色度降采样」省数据。
二、YUV Planar 三剑客
1.YUV420P(AV_PIX_FMT_YUV420P,别名 I420)
布局:Y 平面 → U 平面 → V 平面
采样:4:2:0(每 2×2 像素共享 1 个 U、1 个 V)
占用:12 bit/pixel = 1.5 字节/像素
YY YY YY YY
YY YY YY YY
U U U U
V V V V
FFmpeg 软解默认
x264 / x265 / VP9 / AV1 最爱吃
OpenCV、libswscale、自写算法最友好
ffmpeg -i in.mp4 -pix_fmt yuv420p out.yuv
工程里说「I420」基本就是它。
2.YUV422P(AV_PIX_FMT_YUV422P)
-
水平色度减半,垂直不变
-
16 bit/pixel
-
专业剪辑、广播、ProRes 中间格式
适合:多次转码、调色、字幕渲染。
3.YUV444P(AV_PIX_FMT_YUV444P)
无子采样,YUV 完全 1:1
24 bit/pixel,体积爆炸
影视母带、绿幕抠像、无损中间件
ffmpeg -i in.mov -pix_fmt yuv444p -c:v libx264 -qp 0 out.mkv
三、Semi-Planar:NV12 / NV21
1.NV12(AV_PIX_FMT_NV12)
Y 单独一面
UV 交错存:UVUVUV...
仍是 4:2:0,1.5 字节/像素
YYYYYYYY
UVUVUVUV
优点:
GPU 纹理上传极快
iOS Camera、Metal、Vulkan、DXVA、MediaCodec 原生喜欢
硬编硬解零拷贝友好
骁龙/iOS 上 NV12 → GPU 纹理比 I420 快好几倍。
2.NV21(AV_PIX_FMT_NV21)
-
和 NV12 唯一区别:VUVUVU...
-
Android Camera 历史默认输出
Y 平面
VUVUVU...
NV12 ↔ NV21 只交换 U/V,不重算像素。
四、Packed YUV(采集卡 / UVC 摄像头)
1.YUYV422(AV_PIX_FMT_YUYV422,也叫 YUY2)
打包存储:
Y0 U Y1 V2 像素共用 UV
16 bit/pixel
USB 摄像头、UVC 设备常见
2.UYVY422(AV_PIX_FMT_UYVY422)
-
U Y0 V Y1 -
专业 SDI、采集卡
Packed 特点:只有一个 data0,没有 plane 概念,sws_scale 时要注意 stride。
五、RGB 家族(给人看 / 给算法看)
1.RGB24(AV_PIX_FMT_RGB24)
-
RGB RGB RGB -
24 bit/pixel
-
截图、图像算法、rawvideo 管道
ffmpeg -i in.mp4 -pix_fmt rgb24 out.rgb
2.BGR24(AV_PIX_FMT_BGR24)
-
OpenCV
Mat默认就是它 -
顺序反了,直接当 RGB 会蓝脸
3.RGBA / BGRA / ARGB
-
32 bit/pixel
-
带透明通道
-
SDL、OpenGL、UI 合成、水印
注意大小端:
rgb32在小端其实是 BGRA。
4.GRAY8
-
单通道 8bit
-
人脸识别、亮度图、预处理
六、10bit / HDR 常用格式(别再只认 8bit 了)
| 格式 | 说明 |
|---|---|
yuv420p10le |
10bit 420,H.265/VP9/AV1 主流 |
yuv422p10le |
ProRes 4444 XQ 常用 |
yuv444p10le |
HDR 调色中间格式 |
p010le |
NV12 的 10bit 版(半平面) |
ffmpeg -i hdr.mov -pix_fmt yuv420p10le -c:v libx265 out.mkv
10bit 不是"颜色更多",是"梯度更细、少色带"。
七、带 Alpha 的 YUV
YUVA420P(AV_PIX_FMT_YUVA420P)
YUV420 + 独立 A 平面
网页动画、合成、转场
普通播放器不支持,别拿来喂 H.264
八、一张选型速查表
| 场景 | 推荐 pix_fmt |
|---|---|
| H264/HEVC 编码输入 | yuv420p/ nv12 |
| FFmpeg 软解 + CPU 算法 | yuv420p |
| iOS / GPU 纹理 | nv12 |
| Android Camera | nv21(转 nv12) |
| USB 摄像头 | yuyv422 |
| OpenCV 处理 | bgr24 |
| 专业调色/母带 | yuv422p/ yuv444p |
| HDR / 10bit | yuv420p10le |
| 带透明视频 | yuva420p/ rgba |
九、三个新手必踩的坑
坑 1:颜色发灰
不是格式错了,是色彩范围没对齐:
有限范围 TV 16--235
全范围 PC 0--255
-color_range tv / pc
坑 2:sws_scale 崩溃
Planar 格式:
data[0]=Y, data[1]=U, data[2]=V
NV12:
data[0]=Y, data[1]=UV
别把 NV12 当 YUV420P 拷。
坑 3:Stride ≠ Width
frame->linesize[0]经常比width大(内存对齐)。写裸数据一定要按 linesize 走,不然花屏。
十、总结
YUV420P 是软件标准,NV12 是硬件标准,RGB24/BGR24 是人眼与算法标准;420 省带宽、444 保画质、10bit 防色带。
真正做项目时,格式选择不是"哪个高级用哪个",而是:
解码器输出 → 处理链路 → 编码器/渲染器输入 三段对齐。