将 IMFSample* 转换为 ID3D11ShaderResourceView*

Convert IMFSample* to ID3D11ShaderResourceView*

我是 DirectX 的新手,我正在尝试做一个简单的应用程序来读取视频并将其显示在 Quad 上。

我使用 Windows Media Foundation (IMFSourceReader) 阅读视频,它会在样本解码 (IMFSample) 时向我发送回调。

我想将此 IMFSample* 转换为 ID3D11ShaderResourceView*,以便将其用作纹理来绘制我的四边形,但转换失败。

这是我所做的(我删除了不相关的错误检查):

HRESULT SourceReaderCB::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
{
    ...
    DWORD NumBuffers = 0;
    hr = pSample->GetBufferCount(&NumBuffers);

    if (FAILED(hr) || NumBuffers < 1)
    {
        ...
    }

    IMFMediaBuffer* SourceMediaPtr = nullptr;
    hr = pSample->GetBufferByIndex(0, &SourceMediaPtr);

    if (FAILED(hr))
    {
        ...
    }

    ComPtr<IMFMediaBuffer> _pInputBuffer = SourceMediaPtr;
    ComPtr<IMF2DBuffer2> _pInputBuffer2D2;

    bool isVideoFrame = (_pInputBuffer.As(&_pInputBuffer2D2) == S_OK);
    if (isVideoFrame)
    {
        IMFDXGIBuffer* pDXGIBuffer = NULL;
        ID3D11Texture2D* pSurface = NULL;

        hr = _pInputBuffer->QueryInterface(__uuidof(IMFDXGIBuffer), (LPVOID*)&pDXGIBuffer);
        if (FAILED(hr))
        {
            SafeRelease(&SourceMediaPtr);
            goto done;
        }

        hr = pDXGIBuffer->GetResource(__uuidof(ID3D11Texture2D), (LPVOID*)&pSurface);
        if (FAILED(hr))
        {
            ...
        }

        ID3D11ShaderResourceView* resourceView;

        if (pSurface)
        {
            D3D11_TEXTURE2D_DESC textureDesc;
            pSurface->GetDesc(&textureDesc);

            D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
            shaderResourceViewDesc.Format = DXGI_FORMAT_R8_UNORM;
            shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
            shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
            shaderResourceViewDesc.Texture2D.MipLevels = 1;

            ID3D11ShaderResourceView* resourceView;
            hr = d3d11device->CreateShaderResourceView(pSurface, &shaderResourceViewDesc, &resourceView);
            if (FAILED(hr))
            {
                ... // CODE FAILS HERE
            }
            ...
        }
    }
}

我的第一个问题是我将 shaderResourceViewDesc.Format 设置为 DXGI_FORMAT_R8_UNORM,这可能只会给我红色图像(稍后我将不得不对此进行调查)。

我面临的第二个阻碍问题是 ID3D11Texture2D 到 ID3D11ShaderResourceView 的转换失败并显示以下错误消息:

ID3D11Device::CreateShaderResourceView: A ShaderResourceView cannot be created of a Resource that did not specify the D3D11_BIND_SHADER_RESOURCE BindFlag. [ STATE_CREATION ERROR #129: CREATESHADERRESOURCEVIEW_INVALIDRESOURCE]

我知道在创建纹理时缺少一个标志,阻止我做我想做的事,但由于数据缓冲区是由 WMF 创建的,我不确定我应该做什么解决这个问题。

感谢您的帮助

调试输出表明纹理不兼容,因为它是在没有 D3D11_BIND_SHADER_RESOURCE 标志的情况下创建的(在 D3D11_TEXTURE2D_DESC structureBindFlag 字段中指定)。

您阅读了由 Media Foundation 基元创建的纹理。在某些情况下,您可以更改创建标志,但一般情况是您需要自己创建一个兼容的纹理,在纹理之间复制数据,然后以您的纹理作为参数调用 CreateShaderResourceView 方法,而不是比原来的纹理。

我看到你的代码,我可以说你的方法是错误的 - 无意冒犯。首先,视频解码器创建简单的纹理 - 在你的情况下 DirectX11 纹理 - 它是一个常规纹理 - 它不是着色器资源,因此它不能在着色器代码中使用。在我看来,有两种方法可以解决您的任务:

  1. 研究 - Walkthrough: Using MF to render video in a Direct3D app - 这种 link 当前 "Walkthrough: Using Microsoft Media Foundation for Windows Phone 8" 的方法 - 从你的代码中我看到你尝试为 WindowsStore 编写解决方案- Windows Phone 的 UWP 和代码是可行的 - 此代码需要 MediaEnginePlayer - MediaEnginePlayer class 用作包装 MF API 的助手 class;

  2. 在 GitHub Windows-classic-samples 上找到并在其中找到 DX11VideoRenderer - 这是媒体基础渲染器的完整代码DirectX11 - 它包括使用 DirectX11 视频处理器的非常好的示例,该处理器将常规视频纹理从解码器传输到交换链的渲染视频纹理: 2.1.从交换链获取渲染纹理:

        // Get Backbuffer
    hr = m_pSwapChain1->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pDXGIBackBuffer);
    if (FAILED(hr))
    {
        break;
    }
    

    2.2。从视频处理器的渲染纹理输出视图创建:

    //
    // Create Output View of Output Surfaces.
    //
    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc;
    ZeroMemory( &OutputViewDesc, sizeof( OutputViewDesc ) );
    if (m_b3DVideo && m_bStereoEnabled)
    {
        OutputViewDesc.ViewDimension =  D3D11_VPOV_DIMENSION_TEXTURE2DARRAY;
    }
    else
    {
        OutputViewDesc.ViewDimension =  D3D11_VPOV_DIMENSION_TEXTURE2D;
    }
    OutputViewDesc.Texture2D.MipSlice = 0;
    OutputViewDesc.Texture2DArray.MipSlice = 0;
    OutputViewDesc.Texture2DArray.FirstArraySlice = 0;
    if (m_b3DVideo && 0 != m_vp3DOutput)
    {
        OutputViewDesc.Texture2DArray.ArraySize = 2; // STEREO
    }
    
    QueryPerformanceCounter(&lpcStart);
    
    hr  = m_pDX11VideoDevice->CreateVideoProcessorOutputView(pDXGIBackBuffer, m_pVideoProcessorEnum, &OutputViewDesc, &pOutputView);
    

2.3。从视频处理器的常规解码器视频纹理输入视图创建:

    D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputLeftViewDesc;
    ZeroMemory( &InputLeftViewDesc, sizeof( InputLeftViewDesc ) );
    InputLeftViewDesc.FourCC = 0;
    InputLeftViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
    InputLeftViewDesc.Texture2D.MipSlice = 0;
    InputLeftViewDesc.Texture2D.ArraySlice = dwLeftViewIndex;

    hr = m_pDX11VideoDevice->CreateVideoProcessorInputView(pLeftTexture2D, m_pVideoProcessorEnum, &InputLeftViewDesc, &pLeftInputView);
    if (FAILED(hr))
    {
        break;
    }

2.4。对来自交换链的渲染纹理进行常规解码器视频纹理的 blitting:

    D3D11_VIDEO_PROCESSOR_STREAM StreamData;
    ZeroMemory( &StreamData, sizeof( StreamData ) );
    StreamData.Enable = TRUE;
    StreamData.OutputIndex = 0;
    StreamData.InputFrameOrField = 0;
    StreamData.PastFrames = 0;
    StreamData.FutureFrames = 0;
    StreamData.ppPastSurfaces = NULL;
    StreamData.ppFutureSurfaces = NULL;
    StreamData.pInputSurface = pLeftInputView;
    StreamData.ppPastSurfacesRight = NULL;
    StreamData.ppFutureSurfacesRight = NULL;

    if (m_b3DVideo && MFVideo3DSampleFormat_MultiView == m_vp3DOutput && pRightTexture2D)
    {
        StreamData.pInputSurfaceRight = pRightInputView;
    }

    hr = pVideoContext->VideoProcessorBlt(m_pVideoProcessor, pOutputView, 0, 1, &StreamData );
    if (FAILED(hr))
    {
        break;
    }

是的,它们是复杂代码的一部分,需要研究整个 DX11VideoRenderer 项目才能理解它 - 这将花费大量时间。

此致,

叶夫根尼·佩雷古达