You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在DX12中基于AVFrame创建并初始化DXGI_FORMAT_NV12资源

DXGI_FORMAT_NV12资源配置与YUV420P转NV12流程(DX12视频编码器输入)

核心结论

DXGI_FORMAT_NV12在DX12中是单资源双平面结构,不需要通过多数组或MIP层级实现平面分离,系统会根据格式自动识别两个子资源(Y平面和UV交错平面)。

D3D12_RESOURCE_DESC 配置细节

创建NV12资源时,关键参数设置如下:

D3D12_RESOURCE_DESC desc = {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Width = 视频宽度; // 对应Y平面的分辨率
desc.Height = 视频高度; // 对应Y平面的分辨率
desc.Format = DXGI_FORMAT_NV12;
desc.MipLevels = 1; // 视频编码场景无需MIP层级
desc.ArraySize = 1; // 单帧输入用1,批量编码可设为数组大小
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; // CPU上传场景用这个,GPU生成可设为UNKNOWN
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_SHADER_RESOURCE | D3D12_RESOURCE_FLAG_ALLOW_VIDEO_ENCODER_INPUT;
// 注意:视频编码器输入必须设置ALLOW_VIDEO_ENCODER_INPUT标志
  • 重点:DXGI_FORMAT_NV12本身就定义了双平面布局,子资源0对应Y分量,子资源1对应交错的U/V分量,无需额外配置数组或MIP。

从AV_PIX_FMT_YUV420P转NV12的CPU端处理流程

因为AVFrame的YUV420P是平面存储(Y、U、V各占一个独立缓冲区),转NV12需要手动交错U/V分量:

  1. 计算内存大小:
    • Y平面大小:width * height
    • UV平面大小:(width * height) / 2(每两个像素共用一组U/V,交错存储占2字节)
  2. 映射DX12资源:
    调用ID3D12Resource::Map(0, nullptr, reinterpret_cast<void**>(&pYData))获取Y平面指针;
    调用ID3D12Resource::Map(1, nullptr, reinterpret_cast<void**>(&pUVData))获取UV平面指针;
    注意:要使用D3D12_RANGE来指定映射范围,避免不必要的内存操作。
  3. 拷贝Y分量:
    按行拷贝AVFrame的data[0]到pYData,注意DX12的RowPitch(资源描述中的Width对齐后的值)可能和AVFrame的linesize[0]不一致,需逐行处理:
    for (int y = 0; y < height; y++) {
        memcpy(pYData + y * desc.RowPitch, avFrame->data[0] + y * avFrame->linesize[0], width);
    }
    
  4. 交错拷贝U/V分量:
    遍历UV平面的每一行(共height/2行),每次取U的一个字节和V的一个字节,依次写入UV缓冲区:
    int uvHeight = height / 2;
    int uvWidth = width / 2;
    for (int y = 0; y < uvHeight; y++) {
        uint8_t* pU = avFrame->data[1] + y * avFrame->linesize[1];
        uint8_t* pV = avFrame->data[2] + y * avFrame->linesize[2];
        uint8_t* pDst = pUVData + y * desc.RowPitch;
        for (int x = 0; x < uvWidth; x++) {
            pDst[2*x] = pU[x];
            pDst[2*x+1] = pV[x];
        }
    }
    
  5. 解除映射:调用ID3D12Resource::Unmap(0, nullptr)和Unmap(1, nullptr)。

SRV创建注意事项

你提到的两个SRV配置是对的,分别映射Y和UV分量:

  • Y分量SRV:格式设为DXGI_FORMAT_R8_UNORM,PlaneSlice设为0,将Y值映射到红通道;
  • UV分量SRV:格式设为DXGI_FORMAT_R8G8_UNORM,PlaneSlice设为1,将U映射到红通道、V映射到绿通道;
  • SRV的ViewDimension必须设为D3D12_SRV_DIMENSION_TEXTURE2D,且MipLevels设为1。

常见误区

  • 不要用ArraySize来区分平面:NV12的两个平面是同一资源的子资源,不是数组元素,ArraySize用于多帧批量处理;
  • CPU映射时必须对齐RowPitch:DX12的资源行宽会按硬件要求对齐,不能直接用AVFrame的linesize做整内存拷贝;
  • 视频编码器输入资源必须设置D3D12_RESOURCE_FLAG_ALLOW_VIDEO_ENCODER_INPUT,否则编码器无法识别。

内容的提问来源于stack exchange,提问作者mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 12:55:19