Chapter 94 · 图像与纹理

YUV420P 纹理

视频解码器输出 YUV420P 格式,XGE 提供专用纹理类型直接在 GPU 端完成 YUV→RGB 转换,避免 CPU 转换开销。

94

概念

YUV420P 是视频编码的标准像素格式:Y 平面(亮度)+ U/V 平面(色度,各为 Y 的 1/4 尺寸)。 XGE 的 YUV 纹理在 GPU shader 中完成色彩空间转换,每帧只需上传三个平面数据。

API 速查

函数说明
int xgeTextureCreateYUV420P(xge_texture pTexture, int iWidth, int iHeight)创建 YUV420P 纹理
int xgeTextureUpdateYUV420P(xge_texture pTexture, const void* pY, int iStrideY, const void* pU, int iStrideU, const void* pV, int iStrideV)更新三平面数据

编译本章示例

gcc -O2 -I. -o ch94.exe ch94_main.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch94.exe

示例:视频帧渲染

// 创建 YUV 纹理(视频分辨率)
xge_texture_t videoTex;
xgeTextureCreateYUV420P(&videoTex, 1920, 1080);

// 每帧:从解码器获取 YUV 平面
const uint8_t* pY = frame->data[0];
const uint8_t* pU = frame->data[1];
const uint8_t* pV = frame->data[2];
int strideY = frame->linesize[0];
int strideU = frame->linesize[1];
int strideV = frame->linesize[2];

// 上传到 GPU(内部完成 YUV→RGB)
xgeTextureUpdateYUV420P(&videoTex,
    pY, strideY, pU, strideU, pV, strideV);

// 绘制视频帧
xgeDraw(&videoTex, 0, 0);

完整代码

将以下代码保存为 ch94_main.c,编译后运行即可看到 YUV 纹理更新与渲染效果(模拟视频帧):

#include "xge.h"
#include <stdlib.h>
#include <math.h>

#define VID_W 320
#define VID_H 240

static xge_texture_t g_videoTex;
static float g_time = 0.0f;
static xge_font g_font = NULL;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);
    g_time += 0.016f;

    // 模拟生成 YUV 平面数据(彩条动画)
    static uint8_t planeY[VID_H * VID_W];
    static uint8_t planeU[VID_H/2 * VID_W/2];
    static uint8_t planeV[VID_H/2 * VID_W/2];

    int shift = (int)(g_time * 60.0f) % VID_W;
    for (int y = 0; y < VID_H; y++)
        for (int x = 0; x < VID_W; x++)
            planeY[y * VID_W + x] =
                (uint8_t)((x + shift) % 256);

    for (int y = 0; y < VID_H/2; y++)
        for (int x = 0; x < VID_W/2; x++) {
            planeU[y * VID_W/2 + x] = 128;
            planeV[y * VID_W/2 + x] =
                (uint8_t)(128 + 80 * sinf(g_time + x*0.05f));
        }

    // 上传 YUV 数据到 GPU
    xgeTextureUpdateYUV420P(&g_videoTex,
        planeY, VID_W,
        planeU, VID_W/2,
        planeV, VID_W/2);

    // 绘制视频帧(放大到窗口)
    xgeDraw(&g_videoTex, 0.0f, 0.0f);

    xgeTextDraw(g_font, "YUV420P Video Simulation",
        10.0f, 10.0f, 0xFFFFFFFF)

    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch94 - YUV Video Texture";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);

    xgeFontLoad(&g_font, "Roboto.ttf", 18.0f);

    // 创建 YUV420P 纹理
    xgeTextureCreateYUV420P(&g_videoTex, VID_W, VID_H);

    xgeRun(onFrame, NULL);

    xgeTextureFree(&g_videoTex);
    xgeFontFree(g_font);
    xgeUnit();
    return 0;
}

代码讲解

  • CreateYUV420P:创建指定分辨率的 YUV 三平面纹理,U/V 平面自动为 1/2 宽高。
  • UpdateYUV420P:每帧上传 Y/U/V 三个平面,GPU 端完成 YUV→RGB 转换。
  • Stride:解码器行对齐可能大于宽度,必须传入实际 stride。
  • 模拟视频:用数学函数生成动态 YUV 数据,实际应用中替换为 FFmpeg 解码输出。

要点与陷阱

  • 尺寸:创建时指定视频分辨率,U/V 平面自动为 1/2 宽高。
  • Stride:解码器行对齐可能大于宽度,必须传入实际 stride。
  • 性能:GPU 端转换比 CPU 快 10 倍以上,适合实时视频。
  • 更新频率:每帧调用 UpdateYUV420P,内部做增量上传。

进阶练习

  1. 实现视频播放器:FFmpeg 解码 → YUV 纹理 → 全屏绘制。
  2. 画中画:两个 YUV 纹理分别绘制到不同区域。
YUV420P 纹理效果
图:YUV420P 纹理渲染效果