1. 核心概念:什么是 Plane?
在 FFmpeg/PyAV 中,Plane(平面) 是帧数据的物理存储单元。一个视频帧(Frame)由一个或多个 Plane 组成,具体数量取决于像素格式:
💡 关键认知:
Plane继承自Buffer,它代表的是原始字节内存视图,而非结构化像素数组。直接操作 Plane 等同于操作 C 层面的裸指针。
2. Plane 作为 Buffer 的核心能力
由于 Plane 继承自 av.buffer.Buffer,它提供以下底层内存接口:
⚠️ line_size ≠ 逻辑宽度
plane = frame.planes[0] # Y plane of yuv420p
# ❌ 错误假设
row_bytes = frame.width # 忽略了 stride/padding
# ✅ 正确做法
row_bytes = plane.line_size # 实际内存行宽,通常 >= width
FFmpeg 为 SIMD 优化会对行做对齐(通常 32/64 字节),line_size 包含了这些 padding。逐行处理时必须以 line_size 为步长。
3. 安全访问模式
只读分析(零拷贝)
frame = next(container.decode(video=0))
y_plane = frame.planes[0]
# ✅ 零拷贝获取内存视图,适合 NumPy 处理
import numpy as np
y_array = np.frombuffer(y_plane.to_memoryview(), dtype=np.uint8) \
.reshape(frame.height, y_plane.line_size)[:, :frame.width]
写入修改(必须先确保可写)
frame = next(container.decode(video=0))
# ✅ 安全写入流程
frame.make_writable() # 1. 确保独占可写副本
plane = frame.planes[0] # 2. 获取平面
mv = plane.to_memoryview() # 3. 获取内存视图
# 4. 通过 memoryview 或 update() 写入数据
❌ 危险操作清单
4. 常见像素格式的 Plane 遍历
YUV420P(最常用)
y, u, v = frame.planes[0], frame.planes[1], frame.planes[2]
# Y: height × line_size[0]
# U: (height//2) × line_size[1]
# V: (height//2) × line_size[2]
NV12(移动端/硬件编码常用)
y, uv = frame.planes[0], frame.planes[1]
# Y: height × line_size[0]
# UV: (height//2) × line_size[1],U/V 交错排列
RGB24 / BGR24
rgb = frame.planes[0] # 仅一个平面
# 大小: height × line_size[0],每像素 3 字节交错
5. 与外部库交互速查
6. 开发排查备忘
💡 核心记忆点
Plane = 原始内存视图,不是像素数组
行步进永远用 line_size,永远不要用 width × bpp
改数据前必 make_writable(),零开销保安全
对外交互首选 to_memoryview(),避免 to_bytes() 拷贝
Plane 生命周期绑定 Frame,绝不单独缓存