DirectX:以 DXGI_FORMAT_NV12 格式从 ID3D11Texture2D 获取 RGB 数据的最佳方法?

Posted

技术标签:

【中文标题】DirectX:以 DXGI_FORMAT_NV12 格式从 ID3D11Texture2D 获取 RGB 数据的最佳方法?【英文标题】:DirectX: The best way to get RGB data from ID3D11Texture2D with DXGI_FORMAT_NV12 format? 【发布时间】:2018-04-09 18:51:53 【问题描述】:

我正在使用 DirectX 来绘制视频。通过 Intel Media SDK 解码后。然后我通过以下英特尔代码绘制它:

mfxStatus CD3D11Device::RenderFrame(mfxFrameSurface1 * pSrf, mfxFrameAllocator * pAlloc)

    HRESULT hres = S_OK;
    mfxStatus sts;

    sts = CreateVideoProcessor(pSrf);
    MSDK_CHECK_STATUS(sts, "CreateVideoProcessor failed");

    hres = m_pSwapChain->GetBuffer(0, __uuidof( ID3D11Texture2D ), (void**)&m_pDXGIBackBuffer.p);
    if (FAILED(hres))
        return MFX_ERR_DEVICE_FAILED;

    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc;
    if (2 == m_nViews)
    
        m_pVideoContext->VideoProcessorSetStreamStereoFormat(m_pVideoProcessor, 0, TRUE,D3D11_VIDEO_PROCESSOR_STEREO_FORMAT_SEPARATE,
            TRUE, TRUE, D3D11_VIDEO_PROCESSOR_STEREO_FLIP_NONE, NULL);
        m_pVideoContext->VideoProcessorSetOutputStereoMode(m_pVideoProcessor,TRUE);

        OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2DARRAY;
        OutputViewDesc.Texture2DArray.ArraySize = 2;
        OutputViewDesc.Texture2DArray.MipSlice = 0;
        OutputViewDesc.Texture2DArray.FirstArraySlice = 0;
    
    else
    
        OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
        OutputViewDesc.Texture2D.MipSlice = 0;
    

    if (1 == m_nViews || 0 == pSrf->Info.FrameId.ViewId)
    
        hres = m_pDX11VideoDevice->CreateVideoProcessorOutputView(
            m_pDXGIBackBuffer,
            m_VideoProcessorEnum,
            &OutputViewDesc,
            &m_pOutputView.p );
        if (FAILED(hres))
            return MFX_ERR_DEVICE_FAILED;
    

    D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputViewDesc;
    InputViewDesc.FourCC = 0;
    InputViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
    InputViewDesc.Texture2D.MipSlice = 0;
    InputViewDesc.Texture2D.ArraySlice = 0;

    mfxHDLPair pair = NULL;
    sts = pAlloc->GetHDL(pAlloc->pthis, pSrf->Data.MemId, (mfxHDL*)&pair);
    MSDK_CHECK_STATUS(sts, "pAlloc->GetHDL failed");

    ID3D11Texture2D  *pRTTexture2D = reinterpret_cast<ID3D11Texture2D*>(pair.first);
    D3D11_TEXTURE2D_DESC RTTexture2DDesc;

    if(!m_pTempTexture && m_nViews == 2)
    
        pRTTexture2D->GetDesc(&RTTexture2DDesc);
        hres = m_pD3D11Device->CreateTexture2D(&RTTexture2DDesc,NULL,&m_pTempTexture.p);
        if (FAILED(hres))
            return MFX_ERR_DEVICE_FAILED;
    

    // Creating input views for left and righ eyes
    if (1 == m_nViews)
    
        hres = m_pDX11VideoDevice->CreateVideoProcessorInputView(
            pRTTexture2D,
            m_VideoProcessorEnum,
            &InputViewDesc,
            &m_pInputViewLeft.p );

    
    else if (2 == m_nViews && 0 == pSrf->Info.FrameId.ViewId)
    
        m_pD3D11Ctx->CopyResource(m_pTempTexture,pRTTexture2D);
        hres = m_pDX11VideoDevice->CreateVideoProcessorInputView(
            m_pTempTexture,
            m_VideoProcessorEnum,
            &InputViewDesc,
            &m_pInputViewLeft.p );
    
    else
    
        hres = m_pDX11VideoDevice->CreateVideoProcessorInputView(
            pRTTexture2D,
            m_VideoProcessorEnum,
            &InputViewDesc,
            &m_pInputViewRight.p );
    
    if (FAILED(hres))
        return MFX_ERR_DEVICE_FAILED;

    //  NV12 surface to RGB backbuffer
    RECT rect = 0;
    rect.right  = pSrf->Info.CropW;
    rect.bottom = pSrf->Info.CropH;

    D3D11_VIDEO_PROCESSOR_STREAM StreamData;

    if (1 == m_nViews || pSrf->Info.FrameId.ViewId == 1)
    
        StreamData.Enable = TRUE;
        StreamData.OutputIndex = 0;
        StreamData.InputFrameOrField = 0;
        StreamData.PastFrames = 0;
        StreamData.FutureFrames = 0;
        StreamData.ppPastSurfaces = NULL;
        StreamData.ppFutureSurfaces = NULL;
        StreamData.pInputSurface = m_pInputViewLeft;
        StreamData.ppPastSurfacesRight = NULL;
        StreamData.ppFutureSurfacesRight = NULL;
        StreamData.pInputSurfaceRight = m_nViews == 2 ? m_pInputViewRight : NULL;

        m_pVideoContext->VideoProcessorSetStreamSourceRect(m_pVideoProcessor, 0, true, &rect);
        m_pVideoContext->VideoProcessorSetStreamFrameFormat( m_pVideoProcessor, 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
        hres = m_pVideoContext->VideoProcessorBlt( m_pVideoProcessor, m_pOutputView, 0, 1, &StreamData );
        if (FAILED(hres))
            return MFX_ERR_DEVICE_FAILED;
    

    if (1 == m_nViews || 1 == pSrf->Info.FrameId.ViewId)
    
        DXGI_PRESENT_PARAMETERS parameters = 0;
        hres = m_pSwapChain->Present1(0, 0, &parameters);
        if (FAILED(hres))
            return MFX_ERR_DEVICE_FAILED;
    

    return MFX_ERR_NONE;

从代码行:

ID3D11Texture2D  *pRTTexture2D = reinterpret_cast<ID3D11Texture2D*>(pair.first);

我的pRTTexture2DID3D11Texture2D,格式为DXGI_FORMAT_NV12。

我想从这个纹理中获取 RGB 数据,我尝试使用以下方式:

1) 贴图使用d3dContext-&gt;Map(Texture, 0, D3D11_MAP_READ, 0, &amp;mapInfo) =&gt; must copy to the staging resource in my case

2) 在系统内存上创建一个RGB Array 并计算将mapInfo 上的NV12 转换为RGB Array

这种方式工作正常,但我想用更好的方式来做。因为我猜在渲染(RenderFrame() Function) 时,DirectX 将纹理转换为 BackBuffer 中的 RGB,如果我能从该 BackBuffer 中获取数据,那就太好了。

有人可以告诉我上面的代码。或者有没有更好的实现方式?

非常感谢!

【问题讨论】:

【参考方案1】:

D3DXSaveSurfaceToFileInMemory,使用这个API,或许能给你带来一些想法。

【讨论】:

以上是关于DirectX:以 DXGI_FORMAT_NV12 格式从 ID3D11Texture2D 获取 RGB 数据的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

DirectX 和 Xbox 兼容性

如何编写 DirectX 音频推送源

使用 DirectX 11 后重置窗口

如何正确读取 DirectX 的 FBX 2014 索引?

了解 DXGI DirectX 11 桌面复制以获取缓冲区或数组的问题

DirectX 11:运行简单的 DirectX 应用程序时运行时崩溃