原生视频渲染:YUV 帧怎样抵达屏幕
Stride、Color Conversion、GPU Texture 与 Zero-copy Surface
理解软件解码帧的 YUV Plane、Stride 与色彩转换,以及硬件解码 Surface 通过零拷贝进入 GPU 合成器的路径。
先明确本课目标
正确读取 Plane 与 stride
理解 YUV → RGB 色彩转换
解释硬件 Surface 和零拷贝价值
软件路径把 Y、U、V 平面上传成纹理再由 Shader 转 RGB;硬件路径让解码器直接写可被合成器消费的表面,尽量不搬像素。
为什么复制 Y 平面不能只 memcpy(width × height)?
Plane 的每行有效像素宽度可能小于 linesize,行尾包含对齐 padding。正确复制要逐行从 y×linesize 读取 width 个有效字节。
cppfor (int y = 0; y < height; ++y) {
const uint8_t* src = frame->data[0] + y * frame->linesize[0];
uint8_t* dst = tightlyPackedY + y * width;
std::memcpy(dst, src, width);
}- 用 linesize 定位源行 解码器每行跨度可能大于可见宽度。
- 目标按紧密宽度推进 上传缓冲只保存每行有效像素。
- 复制有效区域 不要把 padding 当成下一行画面数据。
任意对齐方式的 Y 平面都能被正确整理,不会出现错行、斜裂和越界读取。
OPTIONAL · 按需查阅完整术语表与概念边界遇到陌生术语时再展开,不打断主线实验。
展开⌄
边改边运行
浏览器加载后会激活代码编辑器、独立运行环境与实时输出。
写一个支持 I420 与 NV12 的 GPU Shader 上传布局和色彩转换方案。
回到原理,解释刚才发生了什么
先用结果建立反馈,再按运行流程、核心原理和知识关系逐层深入。
核心讲解
Stride 可能大于可见宽度
解码器为对齐和 SIMD 会让每行带 padding。逐行上传必须使用 linesize,不能假设平面是 width×height 紧密连续。
零拷贝减少内存带宽
硬件解码器输出的 Surface/PixelBuffer 可直接被 Metal、Vulkan、OpenGL 或系统合成器引用,避免 GPU→CPU→GPU 往返。
OPTIONAL · 迁移时查阅Web 与原生实现对照主线实验完成后,再用完整映射处理跨平台或跨 API 迁移。
展开⌄
Canvas/WebGPU 与 Metal/Vulkan 最终都消费纹理,原生更常直接握住解码 Surface。
WebGL
CVPixelBuffer、AHardwareBuffer、D3D11 Texture 等可跨解码器与渲染器共享。
WebGPU
VideoFrame 可作为 CanvasImageSource,也可通过 importExternalTexture 等路径交给 GPU。
视频渲染性能优先检查是否发生像素读回和重复色彩转换。