使用Direct3D 11 Video API播放视频

        最近看了下微软的Media Foundation,发现Media Foundation里面的EVR使用比较旧的Direct3D API,于是想直接使用Direct3D 11 Video API来播放视频。并且可以在视频上添加图片和显示文字,又不想使用Media Foundation的pipeline。视频采集利用Media Foundation 的source reader,或者使用ffMpeg获取视频,然后直接通过Direct3D 11/12 Video API来渲染视频。这里只给出采样后的实现部分:

HRESULT CDevice::ProcessSample(ComPtr<IMFMediaType>& pType, UINT32* punInterlaceMode, IMFSample *pSample)
{
    HRESULT hr = S_OK;

    do
    {
        DWORD cCounts;
        hr = pSample->GetBufferCount(&cCounts);
        if(FAILED(hr)) break;

        ComPtr<IMFMediaBuffer> mfMB;
        if(cCounts == 1)
        {
            hr = pSample->GetBufferByIndex(0, &mfMB);
        }
        if(FAILED(hr)) break;

        MFVideoInterlaceMode unInterlaceMode = (MFVideoInterlaceMode) MFGetAttributeUINT32( pType.Get(), MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive );
        if (MFVideoInterlace_MixedInterlaceOrProgressive == unInterlaceMode)
        {
            BOOL fInterlaced = MFGetAttributeUINT32( pSample, MFSampleExtension_Interlaced, FALSE );
            if ( !fInterlaced )
            {
                *punInterlaceMode = MFVideoInterlace_Progressive;
            }
            else
            {
                BOOL fBottomFirst = MFGetAttributeUINT32( pSample, MFSampleExtension_BottomFieldFirst, FALSE );
                if ( fBottomFirst )
                {
                    *punInterlaceMode = MFVideoInterlace_FieldInterleavedLowerFirst;
                }
                else
                {
                    *punInterlaceMode = MFVideoInterlace_FieldInterleavedUpperFirst;
                }
            }
        }

        ComPtr<IMFDXGIBuffer> dxgiBuffer;
        hr = mfMB.As(&dxgiBuffer);
        if(FAILED(hr)) break;

        ComPtr<ID3D11Texture2D> d3d11Texture;
        hr = dxgiBuffer->GetResource(IID_PPV_ARGS(&d3d11Texture));
        if(FAILED(hr)) break;

        UINT dwViewIndex;
        hr = dxgiBuffer->GetSubresourceIndex(&dwViewIndex);
        if(FAILED(hr)) break;

        RECT rcDest;
        ZeroMemory(&rcDest, sizeof(rcDest));
        GetClientRect(mHWnd, &rcDest);
        if(IsRectEmpty(&rcDest)) break;

        ProcessFrameUsingD3D11(d3d11Texture.Get(), dwViewIndex, rcDest, *punInterlaceMode, NULL);

    } while (FALSE);

    return hr;
}

HRESULT CDevice::ProcessFrameWithD3D11(ID3D11Texture2D *pInTexture2D, UINT dwInViewIndex, RECT rcDest, UINT32 unInterlaceMode, IMFSample **ppVideoOutFrame)
{
    HRESULT hr = S_OK;

    do
    {
        if (!mD3D11VD)
        {
            hr = mD3D11Device.As(&mD3D11VD);
            if (FAILED(hr))
                break;
        }

        ComPtr<ID3D11VideoContext> d3d11VC;
        hr = mD3D11DC.As(&d3d11VC);
        if (FAILED(hr))
            break;

        // remember the original rectangles
        RECT TRectOld = mDstRect;
        RECT SRectOld = mDstRect;
        UpdateRectangles(&TRectOld, &SRectOld);

        //Update destination rect with current client rect
        mDstRect = rcDest;

        D3D11_TEXTURE2D_DESC surfaceDesc;
        pInTexture2D->GetDesc(&surfaceDesc);

        if(!mD3D11VPE || !mD3D11VP || m_imageHeightInPixels != surfaceDesc.Height || m_imageWidthInPixels != surfaceDesc.Width)
        {
            m_imageWidthInPixels = surfaceDesc.Width;
            m_imageHeightInPixels = surfaceDesc.Height;

            D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
            ZeroMemory(&contentDesc, sizeof(contentDesc));
            contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
            contentDesc.InputWidth = surfaceDesc.Width;
            contentDesc.InputHeight = surfaceDesc.Height;
            contentDesc.OutputWidth = surfaceDesc.Width;
            contentDesc.OutputHeight = surfaceDesc.Height;
            contentDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;

            hr = mD3D11VD->CreateVideoProcessorEnumerator(&contentDesc, &mD3D11VPE);
            if(FAILED(hr)) break;

            UINT uiFlags;
            DXGI_FORMAT vp_output_format = DXGI_FORMAT_B8G8R8A8_UNORM;
            hr = mD3D11VPE->CheckVideoProcessorFormat(vp_output_format, &uiFlags);
            if(FAILED(hr) || 0 == (uiFlags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
            {
                hr = MF_E_UNSUPPORTED_D3D_TYPE;
                break;
            }

            mSrcRect.left = 0;
            mSrcRect.top = 0;
            mSrcRect.right = m_uiRealDisplayWidth;
            mSrcRect.bottom = m_uiRealDisplayHeight;

            DWORD index;
            hr = FindBOBProcessorIndex(&index);
            if(FAILED(hr)) break;

            hr = mD3D11VD->CreateVideoProcessor(mD3D11VPE.Get(), index, &mD3D11VP);
            if(FAILED(hr)) break;
        }

        RECT TRect = mDstRect;
        RECT SRect = mSrcRect;
        UpdateRectangles(&TRect, &SRect);
        const BOOL fDestRectChanged = !EqualRect(&TRect, &TRectOld);
        if(fDestRectChanged)
        {
            // Get the DXGISwapChain1
            DXGI_SWAP_CHAIN_DESC1 scd;
            ZeroMemory(&scd, sizeof(scd));
            scd.SampleDesc.Count = 1;
            scd.SampleDesc.Quality = 0;
            scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
            scd.Scaling = DXGI_SCALING_STRETCH;
            scd.Width = mDstRect.right;
            scd.Height = mDstRect.bottom;
            scd.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
            scd.Stereo = FALSE;
            scd.BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;
            scd.Flags = 0; //opt in to do direct flip;
            scd.BufferCount = 4;

            hr = mDXGISC1->ResizeBuffers
                (
                4,
                mDstRect.right,
                mDstRect.bottom,
                scd.Format,
                scd.Flags
                );

            if(FAILED(hr)) break;
        }

        ComPtr<ID3D11Texture2D> pTexture2D;
        hr = mDXGISC1->GetBuffer(0, IID_PPV_ARGS(&pTexture2D));
        if(FAILED(hr)) break;

        ComPtr<IMFSample> pSample;
        hr = MFCreateSample(&pSample);
        if(FAILED(hr)) break;

        ComPtr<IMFMediaBuffer> pMediaBuffer;
        hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pTexture2D.Get(), 0, FALSE, &pMediaBuffer);
        if(FAILED(hr)) break;

        hr = pSample->AddBuffer(pMediaBuffer.Get());
        if(FAILED(hr)) break;

        D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputViewDesc;
        ZeroMemory(&outputViewDesc, sizeof(outputViewDesc));
        outputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
        outputViewDesc.Texture2D.MipSlice = 0;
        outputViewDesc.Texture2DArray.FirstArraySlice = 0;
        outputViewDesc.Texture2DArray.MipSlice = 0;

        ComPtr<ID3D11VideoProcessorOutputView> pOutputView;
        hr = mD3D11VD->CreateVideoProcessorOutputView(pTexture2D.Get(), mD3D11VPE.Get(), &outputViewDesc, &pOutputView);
        if(FAILED(hr)) break;

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

        ComPtr<ID3D11VideoProcessorInputView> pInputView;
        hr = mD3D11VD->CreateVideoProcessorInputView(pInTexture2D, mD3D11VPE.Get(), &inputViewDesc, &pInputView);
        if(FAILED(hr)) break;

        SetVideoContextParameters(d3d11VC.Get(), &SRect, &TRect, unInterlaceMode);

        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 = pInputView.Get();
        streamData.ppPastSurfacesRight = NULL;
        streamData.ppFutureSurfacesRight = NULL;

        hr = d3d11VC->VideoProcessorBlt(mD3D11VP.Get(), pOutputView.Get(), 0, 1, &streamData );
        if(FAILED(hr)) break;

        //视频处理完,在这里就可以添加自己图片和文字

        hr = mDXGISC1->Present(0, 0);

    } while (FALSE);

    return hr;
}

Direct3D Video API也不需要处理窗口大小变化的消息,就可以自动缩放。 视频帧处理完,在注释的地方在画上自己的图片和文字。