将IMFSample *转换为ID3D11ShaderResourceView *

时间:2017-08-04 14:45:30

标签: c++ video directx-11 ms-media-foundation

我是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创建的,我不知道我应该做些什么来修复这个问题。

感谢您的帮助

2 个答案:

答案 0 :(得分:2)

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

  1. 研究 - Walkthrough: Using MF to render video in a Direct3D app - 此链接呈现“演练:使用Microsoft Media Foundation for Windows Phone 8” - 从您的代码我看到您尝试为WindowsStore编写解决方案 - UWP和Windows代码电话是可行的 - 此代码需要MediaEnginePlayer - MediaEnginePlayer类充当包装MF API的帮助程序类;

  2. 在GitHub上查找Windows-classic-samples并找到DX11VideoRenderer - 这是Media Foundation渲染器与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);
    
  3. 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项目才能理解它 - 这需要花费大量时间。

    此致

    Evgeny Pereguda

答案 1 :(得分:1)

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

您已阅读Media Foundation原语已创建的纹理。在某些情况下,您可以更改创建标志,但一般情况下您需要自己创建兼容纹理,在纹理之间复制数据,然后使用纹理作为参数调用cd ./symlink-folder/ find ../ -type d -name "*search*" -exec ln -s {} . ';' 方法比原始纹理。