Media Foundation 自定义混合器 MFT 出现错误 MF_E_CANNOT_CREATE_SINK

Media foundation custom mixer MFT getting error MF_E_CANNOT_CREATE_SINK

我正在使用 Windows Media Foundation 创建视频播放应用程序。

我已经使用 IMFTransform 接口和下面 link.
中提到的其他一些强制接口创建了自定义 EVR 混音器 https://msdn.microsoft.com/en-us/library/windows/desktop/ms701624(v=vs.85).aspx

我已经将自定义混音器创建为 DLL,并且也成功注册了它。

然后我在 EVR 中使用以下代码添加了这个自定义混音器:

// Create the video renderer.
hr = MFCreateVideoRendererActivate(hVideoWindow, &pActivate);

// Add custom mixer
hr = pActivate->SetGUID(MF_ACTIVATE_CUSTOM_VIDEO_MIXER_CLSID, CLSID_CMyCustomMixerMFT);

EVR 正在我的自定义混音器中调用所需的方法,但最后我收到错误 MF_E_CANNOT_CREATE_SINK

对于自定义混音器,我指的是我的混音器的 MFT 实现,我指的是来自 Windows 媒体基础示例的 mft_grayscale 示例应用程序。大部分 IMFTransform 实现都是从这个示例中复制的。
https://msdn.microsoft.com/en-us/library/windows/desktop/bb970487%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

我被这个错误困扰了很长时间,找不到解决这个问题的方法。

** 编辑 **

根据方法的文档 STDMETHODIMP GetDeviceID(IID *pDeviceID)

If a mixer or presenter uses Direct3D 9, it must return the value IID_IDirect3DDevice9 in pDeviceID.
The EVR's default mixer and presenter both return this value.
If you write a custom mixer or presenter, it can return some other value.
However, the mixer and presenter must use matching device identifiers.

自定义调音台应该return这个与演示者匹配的值。当我在我的代码中实现自定义混音器时,我 returning deviceID 为 IID_IDirect3DDevice9

更新

我只有一个视频流,其中包含音频和视频。

GetStreamLimits - 输入和输出流限制设置为 1
GetStreamIDs - 输入 ID 0 和输出 ID 0
AddInputStreams - 在我的混音器中我没有调用此方法

按照建议我将使用 MFTrace 进行调试。

您尝试将自定义代码以 Mixer 的形式注入到由 Microsoft 编写的具有 IMFMediaSink 接口的对象中,您得到了 MF_E_CANNOT_CREATE_SINK - 将任何错误归纳为错误消息媒体接收器。这个错误可能有一百个原因。针对这种情况微软开发了一个特殊的工具——MFTrace。它记录由 Microsoft 开发的代码内部调用。此外,不可能识别错误的原因,因为您没有提供您的代码。例如 - 您在方法 GetStreamLimits 中设置了多少流限制,或者您在方法 GetStreamIDs 中设置了什么 ID,或者代码如何处理调用方法 AddInputStreams。只有 IMFTransform 有 23 种方法。

你的问题信息太少,不可能推荐有用的东西。

此致。

这是实现自定义视频混合器的可能方法,它适用于 Windows 7 和 Microsoft MediaSession/Evr。

我发现 dxva2 可以与两种 NV12 流格式一起使用。当然,我们不能将流与 alpha 混合,但它可以工作。我的图形驱动程序告诉我 dxva2 子流只能处理 AYUV/AI44,但 NV12 也可以(奇怪地)。 NV12 没有 alpha,但如果我们不叠加它们,我们可以显示两个视频(也许更多)。我还发现 CLSID_CColorConvertDMO 无法为 AYUV 提供 MediaSession/Evr 和自定义视频混合器。颜色转换可以在自定义视频混合器中完成。

我会post分几次码,大家耐心点。很难在这里格式化代码。对于代码的某些部分,您将需要 MFNode

中的通用文件

一些界面简单return E_NOTIMPL,他们只是在这里检查Evr 需要什么。所以我省略了使用 E_NOTIMPL 的代码。

自定义视频混合器 class :

//----------------------------------------------------------------------------------------------
// CustomVideoMixer.h
//----------------------------------------------------------------------------------------------
#ifndef MFTCUSTOMVIDEOMIXER_H
#define MFTCUSTOMVIDEOMIXER_H

class CCustomVideoMixer :
    BaseObject,
    public IMFVideoDeviceID,
    public IMFGetService,
    public IMFTopologyServiceLookupClient,
    public IMFTransform,
    public IMFVideoMixerControl,
    public IMFVideoProcessor,
    public IMFAttributes,
    public IMFVideoMixerBitmap,
    public IMFVideoPositionMapper
{

public:

    // CustomVideoMixer.cpp
    static HRESULT CreateInstance(IUnknown*, REFIID, void**);

    // IUnknown - CustomVideoMixer.cpp
    STDMETHODIMP QueryInterface(REFIID, void**);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IMFVideoDeviceID - CustomVideoMixer.cpp
    STDMETHODIMP GetDeviceID(IID*);

    // IMFGetService - CustomVideoMixer.cpp
    STDMETHODIMP GetService(REFGUID, REFIID, LPVOID*);

    // IMFTopologyServiceLookupClient - CustomVideoMixer.cpp
    STDMETHODIMP InitServicePointers(IMFTopologyServiceLookup*);
    STDMETHODIMP ReleaseServicePointers();

    // IMFTransform - CustomVideoMixer_Transform.cpp
    STDMETHODIMP GetStreamLimits(DWORD*, DWORD*, DWORD*, DWORD*);
    STDMETHODIMP GetStreamCount(DWORD*, DWORD*);
    STDMETHODIMP GetStreamIDs(DWORD, DWORD*, DWORD, DWORD*);
    STDMETHODIMP GetInputStreamInfo(DWORD, MFT_INPUT_STREAM_INFO*);
    STDMETHODIMP GetOutputStreamInfo(DWORD, MFT_OUTPUT_STREAM_INFO*);
    STDMETHODIMP GetAttributes(IMFAttributes**);
    STDMETHODIMP GetInputStreamAttributes(DWORD, IMFAttributes**);
    STDMETHODIMP GetOutputStreamAttributes(DWORD, IMFAttributes**);
    STDMETHODIMP DeleteInputStream(DWORD);
    STDMETHODIMP AddInputStreams(DWORD, DWORD*);
    STDMETHODIMP GetInputAvailableType(DWORD, DWORD, IMFMediaType**);
    STDMETHODIMP GetOutputAvailableType(DWORD, DWORD, IMFMediaType**);
    STDMETHODIMP SetInputType(DWORD, IMFMediaType*, DWORD);
    STDMETHODIMP SetOutputType(DWORD, IMFMediaType*, DWORD);
    STDMETHODIMP GetInputCurrentType(DWORD, IMFMediaType**);
    STDMETHODIMP GetOutputCurrentType(DWORD, IMFMediaType**);
    STDMETHODIMP GetInputStatus(DWORD, DWORD*);
    STDMETHODIMP GetOutputStatus(DWORD*);
    STDMETHODIMP SetOutputBounds(LONGLONG, LONGLONG);
    STDMETHODIMP ProcessEvent(DWORD, IMFMediaEvent*);
    STDMETHODIMP ProcessMessage(MFT_MESSAGE_TYPE, ULONG_PTR);
    STDMETHODIMP ProcessInput(DWORD, IMFSample*, DWORD);
    STDMETHODIMP ProcessOutput(DWORD, DWORD, MFT_OUTPUT_DATA_BUFFER*, DWORD*);

    // IMFVideoMixerControl - CustomVideoMixer_Mixer.cpp
    STDMETHODIMP GetStreamOutputRect(DWORD, MFVideoNormalizedRect*);
    STDMETHODIMP GetStreamZOrder(DWORD, DWORD*);
    STDMETHODIMP SetStreamOutputRect(DWORD, const MFVideoNormalizedRect*);
    STDMETHODIMP SetStreamZOrder(DWORD, DWORD);

    // IMFVideoProcessor - CustomVideoMixer_Mixer.cpp
    STDMETHODIMP GetAvailableVideoProcessorModes(UINT*, GUID**);
    STDMETHODIMP GetBackgroundColor(COLORREF*);
    STDMETHODIMP GetFilteringRange(DWORD, DXVA2_ValueRange*);
    STDMETHODIMP GetFilteringValue(DWORD, DXVA2_Fixed32*);
    STDMETHODIMP GetProcAmpRange(DWORD, DXVA2_ValueRange*);
    STDMETHODIMP GetProcAmpValues(DWORD, DXVA2_ProcAmpValues*);
    STDMETHODIMP GetVideoProcessorCaps(LPGUID, DXVA2_VideoProcessorCaps*);
    STDMETHODIMP GetVideoProcessorMode(LPGUID);
    STDMETHODIMP SetBackgroundColor(COLORREF);
    STDMETHODIMP SetFilteringValue(DWORD, DXVA2_Fixed32*);
    STDMETHODIMP SetProcAmpValues(DWORD, DXVA2_ProcAmpValues*);
    STDMETHODIMP SetVideoProcessorMode(LPGUID);

    // IMFAttributes - CustomVideoMixer_Attributes.cpp
    STDMETHODIMP Compare(IMFAttributes*, MF_ATTRIBUTES_MATCH_TYPE, BOOL*);
    STDMETHODIMP CompareItem(REFGUID, REFPROPVARIANT, BOOL*);
    STDMETHODIMP CopyAllItems(IMFAttributes*);
    STDMETHODIMP DeleteAllItems();
    STDMETHODIMP DeleteItem(REFGUID);
    STDMETHODIMP GetAllocatedBlob(REFGUID, UINT8**, UINT32*);
    STDMETHODIMP GetAllocatedString(REFGUID, LPWSTR*, UINT32*);
    STDMETHODIMP GetBlob(REFGUID, UINT8*, UINT32, UINT32*);
    STDMETHODIMP GetBlobSize(REFGUID, UINT32*);
    STDMETHODIMP GetCount(UINT32*);
    STDMETHODIMP GetDouble(REFGUID, double*);
    STDMETHODIMP GetGUID(REFGUID, GUID*);
    STDMETHODIMP GetItem(REFGUID, PROPVARIANT*);
    STDMETHODIMP GetItemByIndex(UINT32, GUID*, PROPVARIANT*);
    STDMETHODIMP GetItemType(REFGUID, MF_ATTRIBUTE_TYPE*);
    STDMETHODIMP GetString(REFGUID, LPWSTR, UINT32, UINT32*);
    STDMETHODIMP GetStringLength(REFGUID, UINT32*);
    STDMETHODIMP GetUINT32(REFGUID, UINT32*);
    STDMETHODIMP GetUINT64(REFGUID, UINT64*);
    STDMETHODIMP GetUnknown(REFGUID, REFIID, LPVOID*);
    STDMETHODIMP LockStore();
    STDMETHODIMP SetBlob(REFGUID, const UINT8*, UINT32);
    STDMETHODIMP SetDouble(REFGUID, double);
    STDMETHODIMP SetGUID(REFGUID, REFGUID);
    STDMETHODIMP SetItem(REFGUID, REFPROPVARIANT);
    STDMETHODIMP SetString(REFGUID, LPCWSTR);
    STDMETHODIMP SetUINT32(REFGUID, UINT32);
    STDMETHODIMP SetUINT64(REFGUID, UINT64);
    STDMETHODIMP SetUnknown(REFGUID, IUnknown*);
    STDMETHODIMP UnlockStore();

    // IMFVideoMixerBitmap - CustomVideoMixer_Bitmap.cpp
    STDMETHODIMP ClearAlphaBitmap();
    STDMETHODIMP GetAlphaBitmapParameters(MFVideoAlphaBitmapParams*);
    STDMETHODIMP SetAlphaBitmap(const MFVideoAlphaBitmap*);
    STDMETHODIMP UpdateAlphaBitmapParameters(const MFVideoAlphaBitmapParams*);

    // IMFVideoPositionMapper - CustomVideoMixer_Bitmap.cpp
    STDMETHODIMP MapOutputCoordinateToInputStream(float, float, DWORD, DWORD, float*, float*);


private:

    // CustomVideoMixer.cpp
    CCustomVideoMixer();
    virtual ~CCustomVideoMixer();

    CriticSection m_CriticSection;
    volatile long m_nRefCount;

    CDxva2Manager m_cDxva2Manager;

    IMediaEventSink* m_pMediaEventSink;

    IMFMediaType* m_pRefInputType;
    IMFMediaType* m_pSubInputType;
    IMFMediaType* m_pOutputType;

    BOOL m_bDraining;
    DWORD m_dwInputStreamCount;
    BOOL m_bHaveRefOuput;
    BOOL m_bHaveSubOuput;

    // CustomVideoMixer.cpp
    HRESULT SetD3DManager(IDirect3DDeviceManager9*);
    HRESULT BeginStreaming(ULONG_PTR);
    HRESULT Flush();

    // CustomVideoMixer_Type.cpp
    HRESULT GetOutputType(IMFMediaType**);
};

#endif

CustomVideoMixer.cpp :

//----------------------------------------------------------------------------------------------
// CustomVideoMixer.cpp
//----------------------------------------------------------------------------------------------
#include "StdAfx.h"

CCustomVideoMixer::CCustomVideoMixer()
    : m_nRefCount(1),
    m_pMediaEventSink(NULL),
    m_pRefInputType(NULL),
    m_pSubInputType(NULL),
    m_pOutputType(NULL),
    m_bDraining(FALSE),
    m_dwInputStreamCount(1),
    m_bHaveRefOuput(FALSE),
    m_bHaveSubOuput(FALSE)
{
    TRACE_TRANSFORM((L"CustomVideoMixer::CTOR"));
}

CCustomVideoMixer::~CCustomVideoMixer() {

    TRACE_TRANSFORM((L"CustomVideoMixer::DTOR"));

    AutoLock lock(m_CriticSection);

    Flush();

    m_cDxva2Manager.ReleaseDxva2();
    SAFE_RELEASE(m_pMediaEventSink);
    SAFE_RELEASE(m_pRefInputType);
    SAFE_RELEASE(m_pSubInputType);
    SAFE_RELEASE(m_pOutputType);
}

HRESULT CCustomVideoMixer::CreateInstance(IUnknown* pUnkOuter, REFIID iid, void** ppv) {

    TRACE_TRANSFORM((L"CustomVideoMixer::CreateInstance"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (ppv == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (pUnkOuter != NULL ? CLASS_E_NOAGGREGATION : S_OK));

    CCustomVideoMixer* pMFT = new (std::nothrow)CCustomVideoMixer;

    IF_FAILED_RETURN(pMFT == NULL ? E_OUTOFMEMORY : S_OK);

    LOG_HRESULT(hr = pMFT->QueryInterface(iid, ppv));

    SAFE_RELEASE(pMFT);

    return hr;
}

ULONG CCustomVideoMixer::AddRef() {

    LONG lRef = InterlockedIncrement(&m_nRefCount);

    TRACE_REFCOUNT((L"CustomVideoMixer::AddRef m_nRefCount = %d", lRef));

    return lRef;
}

ULONG CCustomVideoMixer::Release() {

    ULONG uCount = InterlockedDecrement(&m_nRefCount);

    TRACE_REFCOUNT((L"CustomVideoMixer::Release m_nRefCount = %d", uCount));

    if (uCount == 0) {
        delete this;
    }

    return uCount;
}

HRESULT CCustomVideoMixer::QueryInterface(REFIID riid, void** ppv) {

    TRACE_TRANSFORM((L"CustomVideoMixer::QI : riid = %s", GetIIDString(riid)));

    // IMFQualityAdvise
    // IEVRTrustedVideoPlugin

    static const QITAB qit[] = {
        QITABENT(CCustomVideoMixer, IMFVideoDeviceID),
        QITABENT(CCustomVideoMixer, IMFGetService),
        QITABENT(CCustomVideoMixer, IMFTopologyServiceLookupClient),
        QITABENT(CCustomVideoMixer, IMFTransform),
        QITABENT(CCustomVideoMixer, IMFVideoMixerControl),
        QITABENT(CCustomVideoMixer, IMFVideoProcessor),
        QITABENT(CCustomVideoMixer, IMFAttributes),
        QITABENT(CCustomVideoMixer, IMFVideoMixerBitmap),
        QITABENT(CCustomVideoMixer, IMFVideoPositionMapper),
        { 0 }
    };

    return QISearch(this, qit, riid, ppv);
}

HRESULT CCustomVideoMixer::GetDeviceID(IID* pDeviceID) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetDeviceID"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (pDeviceID == NULL ? E_POINTER : S_OK));

    *pDeviceID = IID_IDirect3DDevice9;
    return hr;
}

HRESULT CCustomVideoMixer::GetService(REFGUID guidService, REFIID riid, LPVOID* ppvObject) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetService : guidService = %s - riid = %s", MFServiceString(guidService), GetIIDString(riid)));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (ppvObject == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (guidService != MR_VIDEO_MIXER_SERVICE ? MF_E_UNSUPPORTED_SERVICE : S_OK));

    if (riid == IID_IMFVideoMixerControl || riid == IID_IMFVideoProcessor || riid == IID_IMFTransform) {

        hr = QueryInterface(riid, ppvObject);
    }
    else {

        LOG_HRESULT(hr = MF_E_UNSUPPORTED_SERVICE);
    }

    return hr;
}

HRESULT CCustomVideoMixer::InitServicePointers(IMFTopologyServiceLookup* pLookup) {

    TRACE_TRANSFORM((L"CustomVideoMixer::InitServicePointers"));

    // https://msdn.microsoft.com/en-us/library/windows/desktop/dd319606(v=vs.85).aspx
    // https://msdn.microsoft.com/en-us/library/windows/desktop/dd406901(v=vs.85).aspx

    HRESULT hr;
    IF_FAILED_RETURN(hr = (pLookup == NULL ? E_POINTER : S_OK));

    AutoLock lock(m_CriticSection);

    //IF_FAILED_RETURN(hr = (IsActive() ? MF_E_INVALIDREQUEST : S_OK));

    SAFE_RELEASE(m_pMediaEventSink);

    DWORD dwObjectCount = 1;

    (void)pLookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_pMediaEventSink), &dwObjectCount);

    IF_FAILED_RETURN(hr = (m_pMediaEventSink == NULL ? E_POINTER : S_OK));

    // IMFClock* pInterface = NULL;
    // (void)pLookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pInterface), &dwObjectCount);
    // SAFE_RELEASE(pInterface);

    // IMFVideoPresenter* pInterface = NULL;
    // (void)pLookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pInterface), &dwObjectCount);
    // IF_FAILED_RETURN(hr = (pInterface == NULL ? E_POINTER : S_OK));
    // SAFE_RELEASE(pInterface);

    // IMFVideoRenderer* pInterface2 = NULL;
    // (void)pLookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pInterface2), &dwObjectCount);
    // IF_FAILED_RETURN(hr = (pInterface2 == NULL ? E_POINTER : S_OK));
    // SAFE_RELEASE(pInterface2);

    return hr;
}

HRESULT CCustomVideoMixer::ReleaseServicePointers() {

    TRACE_TRANSFORM((L"CustomVideoMixer::ReleaseServicePointers"));

    AutoLock lock(m_CriticSection);

    SAFE_RELEASE(m_pMediaEventSink);

    return S_OK;
}

HRESULT CCustomVideoMixer::SetD3DManager(IDirect3DDeviceManager9* pDeviceManager) {

    TRACE_TRANSFORM((L"CustomVideoMixer::SetD3DManager"));

    HRESULT hr = S_OK;

    m_cDxva2Manager.ReleaseDxva2();

    if (pDeviceManager != NULL) {

        if (m_pRefInputType != NULL && m_pOutputType != NULL)
            IF_FAILED_RETURN(hr = m_cDxva2Manager.InitDxva2(pDeviceManager, m_pOutputType, m_pRefInputType, m_pSubInputType));
    }

    return hr;
}

HRESULT CCustomVideoMixer::BeginStreaming(ULONG_PTR ulParam) {

    TRACE_TRANSFORM((L"CustomVideoMixer::BeginStreaming"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (m_pMediaEventSink == NULL ? E_POINTER : S_OK));

    //IF_FAILED_RETURN(hr = m_pMediaEventSink->Notify(EC_SAMPLE_NEEDED, ulParam, 0));
    IF_FAILED_RETURN(hr = m_pMediaEventSink->Notify(EC_SAMPLE_NEEDED, 0, 0));
    IF_FAILED_RETURN(hr = m_pMediaEventSink->Notify(EC_SAMPLE_NEEDED, 1, 0));

    // MF_E_INVALIDSTREAMNUMBER
    // MF_E_TRANSFORM_TYPE_NOT_SET

    return hr;
}

HRESULT CCustomVideoMixer::Flush() {

    TRACE_TRANSFORM((L"CustomVideoMixer::Flush"));

    m_bDraining = FALSE;
    m_bHaveRefOuput = FALSE;
    m_bHaveSubOuput = FALSE;
    return S_OK;
}

CustomVideoMixer_Transform.cpp :

//----------------------------------------------------------------------------------------------
// CustomVideoMixer_Transform.cpp
//----------------------------------------------------------------------------------------------
#include "StdAfx.h"

HRESULT CCustomVideoMixer::GetStreamLimits(DWORD* pdwInputMinimum, DWORD* pdwInputMaximum, DWORD* pdwOutputMinimum, DWORD* pdwOutputMaximum) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetStreamLimits"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = ((pdwInputMinimum == NULL || pdwInputMaximum == NULL || pdwOutputMinimum == NULL || pdwOutputMaximum == NULL) ? E_POINTER : S_OK));

    *pdwInputMinimum = 1;
    *pdwInputMaximum = 16;
    *pdwOutputMinimum = 1;
    *pdwOutputMaximum = 1;

    return hr;
}

HRESULT CCustomVideoMixer::GetStreamCount(DWORD* pcInputStreams, DWORD* pcOutputStreams) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetStreamCount"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = ((pcInputStreams == NULL || pcOutputStreams == NULL) ? E_POINTER : S_OK));

    *pcInputStreams = m_dwInputStreamCount;
    *pcOutputStreams = 1;

    return hr;
}

HRESULT CCustomVideoMixer::GetStreamIDs(DWORD dwInputIDArraySize, DWORD* pdwInputIDs, DWORD dwOutputIDArraySize, DWORD* pdwOutputIDs) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetStreamIDs"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (dwInputIDArraySize == 0 || dwOutputIDArraySize  == 0 ? MF_E_BUFFERTOOSMALL : S_OK));
    IF_FAILED_RETURN(hr = (pdwInputIDs == NULL || pdwOutputIDs == NULL ? E_POINTER : S_OK));

    *pdwOutputIDs = 0;

    if (m_dwInputStreamCount == 1)
        *pdwInputIDs = 0;
    else
        IF_FAILED_RETURN(hr = E_FAIL);

    return hr;
}

HRESULT CCustomVideoMixer::GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO* pStreamInfo) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetInputStreamInfo"));

    TRACE_TRANSFORM((L"dwInputStreamID = %d", dwInputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (pStreamInfo == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (dwInputStreamID > 1 ? MF_E_INVALIDSTREAMNUMBER : S_OK));

    pStreamInfo->dwFlags =
        MFT_INPUT_STREAM_WHOLE_SAMPLES |
        MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER |
        MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE |
        MFT_INPUT_STREAM_DOES_NOT_ADDREF;
    pStreamInfo->hnsMaxLatency = 0;
    pStreamInfo->cbSize = 0;
    pStreamInfo->cbMaxLookahead = 0;
    pStreamInfo->cbAlignment = 0;

    return hr;
}

HRESULT CCustomVideoMixer::GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO* pStreamInfo) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetOutputStreamInfo"));

    TRACE_TRANSFORM((L"dwOutputStreamID = %d", dwOutputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (pStreamInfo == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (dwOutputStreamID != 0 ? MF_E_INVALIDSTREAMNUMBER : S_OK));

    AutoLock lock(m_CriticSection);

    pStreamInfo->dwFlags =
        MFT_OUTPUT_STREAM_WHOLE_SAMPLES |
        MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER |
        MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE |
        MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;

    pStreamInfo->cbAlignment = 0;
    pStreamInfo->cbSize = 0;

    return hr;
}

HRESULT CCustomVideoMixer::GetAttributes(IMFAttributes** ppAttributes) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetAttributes"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (ppAttributes == NULL ? E_POINTER : S_OK));

    *ppAttributes = this;
    (*ppAttributes)->AddRef();

    return hr;
}

HRESULT CCustomVideoMixer::GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes** ppAttributes) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetInputStreamAttributes"));

    TRACE_TRANSFORM((L"dwInputStreamID = %d", dwInputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (dwInputStreamID > 1 ? MF_E_INVALIDSTREAMNUMBER : S_OK));
    IF_FAILED_RETURN(hr = (ppAttributes == NULL ? E_POINTER : S_OK));

    *ppAttributes = this;
    (*ppAttributes)->AddRef();

    return hr;
}

HRESULT CCustomVideoMixer::GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes** ppAttributes) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetOutputStreamAttributes"));

    TRACE_TRANSFORM((L"dwOutputStreamID = %d", dwOutputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (dwOutputStreamID != 0 ? MF_E_INVALIDSTREAMNUMBER : S_OK));
    IF_FAILED_RETURN(hr = (ppAttributes == NULL ? E_POINTER : S_OK));

    *ppAttributes = this;
    (*ppAttributes)->AddRef();

    return hr;
}

HRESULT CCustomVideoMixer::DeleteInputStream(DWORD dwStreamID) {

    TRACE_TRANSFORM((L"CustomVideoMixer::DeleteInputStream"));

    TRACE_TRANSFORM((L"dwStreamID = %d", dwStreamID));

    if (dwStreamID == 0)
        return MF_E_INVALIDREQUEST;
    else if (dwStreamID != 1)
        return MF_E_INVALIDSTREAMNUMBER;
    else if(m_dwInputStreamCount != 2)
        return MF_E_INVALIDREQUEST;

    //MF_E_TRANSFORM_INPUT_REMAINING

    m_dwInputStreamCount--;

    return S_OK;
}

HRESULT CCustomVideoMixer::AddInputStreams(DWORD cStreams, DWORD* adwStreamIDs) {

    TRACE_TRANSFORM((L"CustomVideoMixer::AddInputStreams"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (cStreams != 1 ? E_INVALIDARG : S_OK));
    IF_FAILED_RETURN(hr = (adwStreamIDs == NULL ? E_INVALIDARG : S_OK));
    IF_FAILED_RETURN(hr = (*adwStreamIDs != 1 ? E_INVALIDARG : S_OK));

    if (m_dwInputStreamCount == 1)
        m_dwInputStreamCount++;
    else
        IF_FAILED_RETURN(hr = E_INVALIDARG);

    return S_OK;
}

HRESULT CCustomVideoMixer::GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType** ppType) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetInputAvailableType"));

    TRACE_TRANSFORM((L"dwInputStreamID = %d - dwTypeIndex = %d", dwInputStreamID, dwTypeIndex));

    return MF_E_NO_MORE_TYPES;
}

HRESULT CCustomVideoMixer::GetOutputAvailableType(DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType** ppType) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetOutputAvailableType"));

    TRACE_TRANSFORM((L"dwOutputStreamID = %d - dwTypeIndex = %d", dwOutputStreamID, dwTypeIndex));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (ppType == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (dwOutputStreamID != 0 ? MF_E_INVALIDSTREAMNUMBER : S_OK));
    IF_FAILED_RETURN(hr = (dwTypeIndex != 0 ? MF_E_NO_MORE_TYPES : S_OK));

    AutoLock lock(m_CriticSection);

    if (m_pRefInputType == NULL) {
        hr = MF_E_TRANSFORM_TYPE_NOT_SET;
    }
    else {
        hr = GetOutputType(ppType);
    }

    return hr;
}

HRESULT CCustomVideoMixer::SetInputType(DWORD dwInputStreamID, IMFMediaType* pType, DWORD dwFlags) {

    TRACE_TRANSFORM((L"CustomVideoMixer::SetInputType"));

    TRACE_TRANSFORM((L"dwInputStreamID = %d", dwInputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (dwInputStreamID > 1 ? MF_E_INVALIDSTREAMNUMBER : S_OK));
    IF_FAILED_RETURN(hr = (dwFlags & ~MFT_SET_TYPE_TEST_ONLY ? E_INVALIDARG : S_OK));

    BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0);

    TRACE_TRANSFORM((L"bReallySet = %s", bReallySet ? L"TRUE" : L"FALSE"));

    AutoLock lock(m_CriticSection);

    if (pType) {

        LogMediaType(pType);
    }
    else {

        if (dwInputStreamID == 0)
            SAFE_RELEASE(m_pRefInputType);
        else
            SAFE_RELEASE(m_pSubInputType);

        return hr;
    }

    if (bReallySet) {

        if (dwInputStreamID == 0) {

            SAFE_RELEASE(m_pRefInputType);
            m_pRefInputType = pType;
            m_pRefInputType->AddRef();
        }
        else {

            SAFE_RELEASE(m_pSubInputType);
            m_pSubInputType = pType;
            m_pSubInputType->AddRef();
        }
    }

    return hr;
}

HRESULT CCustomVideoMixer::SetOutputType(DWORD dwOutputStreamID, IMFMediaType* pType, DWORD dwFlags) {

    TRACE_TRANSFORM((L"CustomVideoMixer::SetOutputType"));

    TRACE_TRANSFORM((L"dwOutputStreamID = %d", dwOutputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (dwOutputStreamID != 0 ? MF_E_INVALIDSTREAMNUMBER : S_OK));
    IF_FAILED_RETURN(hr = (dwFlags & ~MFT_SET_TYPE_TEST_ONLY ? E_INVALIDARG : S_OK));

    BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0);

    TRACE_TRANSFORM((L"bReallySet = %s", bReallySet ? L"TRUE" : L"FALSE"));

    AutoLock lock(m_CriticSection);

    if (pType) {

        LogMediaType(pType);
    }
    else {

        SAFE_RELEASE(m_pOutputType);
        return hr;
    }

    if (bReallySet) {

        SAFE_RELEASE(m_pOutputType);
        m_pOutputType = pType;
        m_pOutputType->AddRef();
    }

    return hr;
}

HRESULT CCustomVideoMixer::GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType** ppType) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetInputCurrentType"));

    TRACE_TRANSFORM((L"dwInputStreamID = %d", dwInputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (ppType == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (dwInputStreamID > 1 ? MF_E_INVALIDSTREAMNUMBER : S_OK));

    AutoLock lock(m_CriticSection);

    IMFMediaType* m_pInputType = dwInputStreamID == 0 ? m_pRefInputType : m_pSubInputType;

    if (!m_pInputType) {
        hr = MF_E_TRANSFORM_TYPE_NOT_SET;
    }
    else {

        // Todo : clone MediaType
        *ppType = m_pInputType;
        (*ppType)->AddRef();
    }

    return hr;
}

HRESULT CCustomVideoMixer::GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType** ppType) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetOutputCurrentType"));

    TRACE_TRANSFORM((L"dwOutputStreamID = %d", dwOutputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (ppType == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (dwOutputStreamID != 0 ? MF_E_INVALIDSTREAMNUMBER : S_OK));

    AutoLock lock(m_CriticSection);

    if (!m_pOutputType) {
        hr = MF_E_TRANSFORM_TYPE_NOT_SET;
    }
    else {

        // Todo : clone MediaType
        *ppType = m_pOutputType;
        (*ppType)->AddRef();
    }

    return hr;
}

HRESULT CCustomVideoMixer::GetInputStatus(DWORD dwInputStreamID, DWORD* pdwFlags) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetInputStatus"));

    TRACE_TRANSFORM((L"dwInputStreamID = %d", dwInputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (pdwFlags == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (dwInputStreamID > 1 ? MF_E_INVALIDSTREAMNUMBER : S_OK));

    AutoLock lock(m_CriticSection);

    // I think we can always process
    *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA;

    return hr;
}

HRESULT CCustomVideoMixer::GetOutputStatus(DWORD* pdwFlags) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetOutputStatus"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (pdwFlags == NULL ? E_POINTER : S_OK));

    AutoLock lock(m_CriticSection);

    /*if (m_bHaveOuput) {
        *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY;
    }
    else {
        *pdwFlags = 0;
    }*/

    return hr;
}

HRESULT CCustomVideoMixer::SetOutputBounds(LONGLONG /*hnsLowerBound*/, LONGLONG /*hnsUpperBound*/) {

    TRACE_TRANSFORM((L"CustomVideoMixer::SetOutputBounds"));

    return E_NOTIMPL;
}

HRESULT CCustomVideoMixer::ProcessEvent(DWORD /*dwInputStreamID*/, IMFMediaEvent* /*pEvent */) {

    TRACE_TRANSFORM((L"CustomVideoMixer::ProcessEvent"));

    return E_NOTIMPL;
}

HRESULT CCustomVideoMixer::ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) {

    TRACE_TRANSFORM((L"CustomVideoMixer::ProcessMessage : %s (Param = %d)", MFTMessageString(eMessage), ulParam));

    HRESULT hr = S_OK;

    AutoLock lock(m_CriticSection);

    switch (eMessage) {

    case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
        //case MFT_MESSAGE_NOTIFY_START_OF_STREAM:
        hr = BeginStreaming(ulParam);
        break;

    case MFT_MESSAGE_COMMAND_FLUSH:
    case MFT_MESSAGE_NOTIFY_END_STREAMING:
    case MFT_MESSAGE_NOTIFY_END_OF_STREAM:
        hr = Flush();
        break;

    case MFT_MESSAGE_COMMAND_DRAIN:
        m_bDraining = TRUE;
        break;

    case MFT_MESSAGE_SET_D3D_MANAGER:
        hr = SetD3DManager(reinterpret_cast<IDirect3DDeviceManager9*>(ulParam));
        // hr = MF_E_UNSUPPORTED_D3D_TYPE...
        break;
    }

    return hr;
}

HRESULT CCustomVideoMixer::ProcessInput(DWORD dwInputStreamID, IMFSample* pSample, DWORD dwFlags) {

    TRACE_TRANSFORM((L"CustomVideoMixer::ProcessInput"));

    TRACE_TRANSFORM((L"dwInputStreamID = %d", dwInputStreamID));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (pSample == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (dwInputStreamID > 1 ? MF_E_INVALIDSTREAMNUMBER : S_OK));
    IF_FAILED_RETURN(hr = (dwFlags != 0 ? E_INVALIDARG : S_OK));

    AutoLock lock(m_CriticSection);

    if (m_bHaveRefOuput || m_bHaveSubOuput) {
        return MF_E_NOTACCEPTING;
    }

    if (SUCCEEDED(hr = m_cDxva2Manager.ProcessInput(pSample, dwInputStreamID))) {

        if (dwInputStreamID == 0) {

            m_bHaveRefOuput = TRUE;

            LOG_HRESULT(hr = m_pMediaEventSink->Notify(EC_SAMPLE_NEEDED, 0, 0));
        }
        else {

            m_bHaveSubOuput = TRUE;

            LOG_HRESULT(hr = m_pMediaEventSink->Notify(EC_SAMPLE_NEEDED, 1, 0));
        }
    }

    return hr;
}

HRESULT CCustomVideoMixer::ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER* pOutputSamples, DWORD* pdwStatus) {

    TRACE_TRANSFORM((L"CustomVideoMixer::ProcessOutput"));

    HRESULT hr;
    IF_FAILED_RETURN(hr = (dwFlags != 0 ? E_INVALIDARG : S_OK));
    IF_FAILED_RETURN(hr = (cOutputBufferCount != 1 ? E_INVALIDARG : S_OK));
    IF_FAILED_RETURN(hr = ((pOutputSamples == NULL || pdwStatus == NULL) ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (pOutputSamples[0].dwStreamID != 0 ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (pOutputSamples[0].pSample == NULL ? E_INVALIDARG : S_OK));

    AutoLock lock(m_CriticSection);

    if (m_bHaveRefOuput || m_bHaveSubOuput) {

        IF_FAILED_RETURN(hr = m_cDxva2Manager.ProcessOutput(pOutputSamples[0].pSample));

        if(m_bHaveRefOuput)
            m_bHaveRefOuput = FALSE;

        if (m_bHaveSubOuput)
            m_bHaveSubOuput = FALSE;
    }
    else {

        return MF_E_TRANSFORM_NEED_MORE_INPUT;
    }

    return hr;
}

剩下的代码。

CustomVideoMixer_Attributes.cpp :

//----------------------------------------------------------------------------------------------
// CustomVideoMixer_Attributes.cpp
//----------------------------------------------------------------------------------------------
#include "StdAfx.h"

HRESULT CCustomVideoMixer::GetUINT32(REFGUID guidKey, UINT32* punValue) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetUINT32"));

    if (punValue == NULL)
        return E_POINTER;

    if (guidKey == MF_SA_D3D_AWARE) {

        TRACE_TRANSFORM((L"MF_SA_D3D_AWARE"));

        *punValue = TRUE;
        return S_OK;
    }
    else if(guidKey == MF_SA_REQUIRED_SAMPLE_COUNT) {

        TRACE_TRANSFORM((L"MF_SA_REQUIRED_SAMPLE_COUNT"));

        *punValue = 1;
        return S_OK;
    }
    else {

        TRACE_TRANSFORM((L"ERROR : MF_E_ATTRIBUTENOTFOUND"));
    }

    return MF_E_ATTRIBUTENOTFOUND;
}

HRESULT CCustomVideoMixer::SetBlob(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize) {

    TRACE_TRANSFORM((L"CustomVideoMixer::SetBlob"));

    if (guidKey == VIDEO_ZOOM_RECT) {

        TRACE_TRANSFORM((L"VIDEO_ZOOM_RECT"));
        return S_OK;
    }
    else {

        TRACE_TRANSFORM((L"ERROR : MF_E_ATTRIBUTENOTFOUND"));
    }

    return MF_E_ATTRIBUTENOTFOUND;
}

CustomVideoMixer_Type.cpp :

//----------------------------------------------------------------------------------------------
// CustomVideoMixer_Type.cpp
//----------------------------------------------------------------------------------------------
#include "StdAfx.h"

HRESULT CCustomVideoMixer::GetOutputType(IMFMediaType** ppType) {

    TRACE_TRANSFORM((L"CustomVideoMixer::GetOutputType"));

    HRESULT hr = S_OK;
    IMFMediaType* pOutputType = NULL;

    try {

        IF_FAILED_THROW(hr = MFCreateMediaType(&pOutputType));

        IF_FAILED_THROW(hr = pOutputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
        // MFVideoFormat_ARGB32 MFVideoFormat_RGB32
        IF_FAILED_THROW(hr = pOutputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32));
        IF_FAILED_THROW(hr = pOutputType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE));
        IF_FAILED_THROW(hr = pOutputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE));
        IF_FAILED_THROW(hr = pOutputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
        IF_FAILED_THROW(hr = MFSetAttributeRatio(pOutputType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1));

        *ppType = pOutputType;
        (*ppType)->AddRef();
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pOutputType);
    return hr;
}

Dxva2 部分:

//----------------------------------------------------------------------------------------------
// Dxva2Manager.h
//----------------------------------------------------------------------------------------------
#ifndef DXVA2MANAGER_H
#define DXVA2MANAGER_H

class CDxva2Manager {

    public:

        CDxva2Manager();
        ~CDxva2Manager() { ReleaseDxva2(); }

        HRESULT InitDxva2(IDirect3DDeviceManager9*, IMFMediaType*, IMFMediaType*, IMFMediaType*);
        void ReleaseDxva2();
        HRESULT ProcessInput(IMFSample*, const DWORD);
        HRESULT ProcessOutput(IMFSample*);

    private:

        IDirectXVideoProcessor* m_pVideoProcessor;
        IDirect3DSurface9* m_pRefSurface9;
        IDirect3DSurface9* m_pSubSurface9;

        LONGLONG m_llDuration;
        LONGLONG m_llTime;

        UINT32 m_uiRefWidth;
        UINT32 m_uiRefHeight;
        UINT32 m_uiRefLine;
        UINT32 m_uiSubWidth;
        UINT32 m_uiSubHeight;
        UINT32 m_uiSubLine;

        HRESULT GetDxva2VideoDesc(DXVA2_VideoDesc*, IMFMediaType*);
};

#endif

Dxva2Manager.cpp :

//----------------------------------------------------------------------------------------------
// Dxva2Manager.cpp
//----------------------------------------------------------------------------------------------
#include "StdAfx.h"

CDxva2Manager::CDxva2Manager() :
    m_pVideoProcessor(NULL),
    m_pRefSurface9(NULL),
    m_pSubSurface9(NULL),
    m_llDuration(0LL),
    m_llTime(0LL),
    m_uiRefWidth(0),
    m_uiRefHeight(0),
    m_uiRefLine(0),
    m_uiSubWidth(0),
    m_uiSubHeight(0),
    m_uiSubLine(0)
{
}

HRESULT CDxva2Manager::InitDxva2(IDirect3DDeviceManager9* pDeviceManager, IMFMediaType* pOutputType, IMFMediaType* pRefInputType, IMFMediaType* pSubInputType) {

    assert(m_pVideoProcessor == NULL);
    assert(m_pRefSurface9 == NULL);
    assert(m_pSubSurface9 == NULL);

    HRESULT hr;

    IF_FAILED_RETURN(hr = (pDeviceManager == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (pOutputType == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (pRefInputType == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (pSubInputType == NULL ? E_POINTER : S_OK));

    IDirectXVideoProcessorService* pVideoProcessorService = NULL;
    HANDLE hD3d9Device = INVALID_HANDLE_VALUE;

    GUID subtype = GUID_NULL;
    UINT32 uiWidth = 0;
    UINT32 uiHeight = 0;
    D3DFORMAT D3DFormat = D3DFMT_UNKNOWN;

    DXVA2_VideoDesc dxva2VideoDesc = { 0 };
    UINT uiCount = 0;
    UINT uiStreamCount = 1;
    GUID* guids = NULL;

    try {

        IF_FAILED_THROW(hr = pDeviceManager->OpenDeviceHandle(&hD3d9Device));
        IF_FAILED_THROW(hr = pDeviceManager->GetVideoService(hD3d9Device, IID_PPV_ARGS(&pVideoProcessorService)));

        IF_FAILED_THROW(hr = GetDxva2VideoDesc(&dxva2VideoDesc, pRefInputType));
        IF_FAILED_THROW(hr = pVideoProcessorService->GetVideoProcessorDeviceGuids(&dxva2VideoDesc, &uiCount, &guids));
        IF_FAILED_THROW(hr = pVideoProcessorService->CreateVideoProcessor(guids[0], &dxva2VideoDesc, D3DFMT_X8R8G8B8, uiStreamCount, &m_pVideoProcessor));

        IF_FAILED_THROW(hr = pRefInputType->GetGUID(MF_MT_SUBTYPE, &subtype));
        IF_FAILED_THROW(hr = MFGetAttributeSize(pRefInputType, MF_MT_FRAME_SIZE, &uiWidth, &uiHeight));

        if (subtype == MFVideoFormat_NV12)
            D3DFormat = (D3DFORMAT)D3DFMT_NV12;
        else
            IF_FAILED_THROW(hr = E_FAIL);

        IF_FAILED_THROW(hr = pVideoProcessorService->CreateSurface(uiWidth, uiHeight, 0, D3DFormat, D3DPOOL_DEFAULT, 0, DXVA2_VideoProcessorRenderTarget, &m_pRefSurface9, NULL));

        m_uiRefWidth = uiWidth;
        m_uiRefHeight = uiHeight;
        m_uiRefLine = m_uiRefHeight + (m_uiRefHeight / 2);

        IF_FAILED_THROW(hr = pSubInputType->GetGUID(MF_MT_SUBTYPE, &subtype));
        IF_FAILED_THROW(hr = MFGetAttributeSize(pSubInputType, MF_MT_FRAME_SIZE, &uiWidth, &uiHeight));

        /*if (subtype == MFVideoFormat_AYUV)
            D3DFormat = (D3DFORMAT)D3DFMT_AYUV;
        else
            IF_FAILED_THROW(hr = E_FAIL);*/

        m_uiSubWidth = uiWidth;
        m_uiSubHeight = uiHeight;
        m_uiSubLine = m_uiSubHeight + (m_uiSubHeight / 2);

        IF_FAILED_THROW(hr = pVideoProcessorService->CreateSurface(uiWidth, uiHeight, 0, D3DFormat, D3DPOOL_DEFAULT, 0, DXVA2_VideoProcessorRenderTarget, &m_pSubSurface9, NULL));
    }
    catch (HRESULT) {}

    CoTaskMemFree(guids);

    if (hD3d9Device != INVALID_HANDLE_VALUE) {

        LOG_HRESULT(pDeviceManager->CloseDeviceHandle(hD3d9Device));
    }

    SAFE_RELEASE(pVideoProcessorService);

    return hr;
}

void CDxva2Manager::ReleaseDxva2() {

    SAFE_RELEASE(m_pVideoProcessor);
    SAFE_RELEASE(m_pRefSurface9);
    SAFE_RELEASE(m_pSubSurface9);

    m_llDuration = 0LL;
    m_llTime = 0LL;
    m_uiRefWidth = 0;
    m_uiRefHeight = 0;
    m_uiRefLine = 0;
    m_uiSubWidth = 0;
    m_uiSubHeight = 0;
    m_uiSubLine = 0;
}

HRESULT CDxva2Manager::ProcessInput(IMFSample* pSample, const DWORD dwStreamId) {

    HRESULT hr = S_OK;

    IMFMediaBuffer* pBuffer = NULL;
    BYTE* pData = NULL;
    DWORD dwLength = 0;

    IDirect3DSurface9* pSurface9 = NULL;
    D3DLOCKED_RECT d3dRect;
    LONG lStride = 0;
    UINT32 uiWidth;
    UINT32 uiLine;

    IMF2DBuffer* p2DBuffer = NULL;

    try {

        if (dwStreamId == 0) {

            IF_FAILED_THROW(hr = pSample->GetSampleTime(&m_llTime));
            IF_FAILED_THROW(hr = pSample->GetSampleDuration(&m_llDuration));
        }

        IF_FAILED_THROW(hr = pSample->GetBufferByIndex(0, &pBuffer));
        IF_FAILED_THROW(hr = pBuffer->QueryInterface(IID_PPV_ARGS(&p2DBuffer)));

        IF_FAILED_THROW(hr = p2DBuffer->Lock2D(&pData, &lStride));

        if (dwStreamId == 0) {

            pSurface9 = m_pRefSurface9;
            uiWidth = m_uiRefWidth;
            uiLine = m_uiRefLine;
        }
        else if (dwStreamId == 1) {

            pSurface9 = m_pSubSurface9;
            //uiWidth = m_uiSubWidth * 4;
            //uiLine = m_uiSubHeight;
            uiWidth = m_uiSubWidth;
            uiLine = m_uiSubLine;
        }

        IF_FAILED_THROW(hr = pSurface9->LockRect(&d3dRect, NULL, 0));

        IF_FAILED_THROW(hr = MFCopyImage((BYTE*)d3dRect.pBits, d3dRect.Pitch, pData, lStride, uiWidth, uiLine));

        IF_FAILED_THROW(hr = pSurface9->UnlockRect());
    }
    catch (HRESULT) {}

    if (pBuffer && pData) {
        LOG_HRESULT(p2DBuffer->Unlock2D());
    }

    SAFE_RELEASE(p2DBuffer);
    SAFE_RELEASE(pBuffer);

    return hr;
}

HRESULT CDxva2Manager::ProcessOutput(IMFSample* pSample) {

    HRESULT hr = S_OK;
    IMFMediaBuffer* pBuffer = NULL;
    IDirect3DSurface9* pSurface = NULL;

    DXVA2_VideoProcessBltParams blt = { 0 };
    RECT rc = { 0, 0, m_uiRefWidth, m_uiRefHeight };
    DXVA2_AYUVSample16 color;
    color.Cr = 0x0000;
    color.Cb = 0xFFFF;
    color.Y = 0x0000;
    color.Alpha = 0xFFFF;

    const UINT EX_COLOR_INFO[][2] =
    {
        // SDTV ITU-R BT.601 YCbCr to driver's optimal RGB range
        { DXVA2_VideoTransferMatrix_BT601, DXVA2_NominalRange_Unknown },
        // SDTV ITU-R BT.601 YCbCr to studio RGB [16...235]
        { DXVA2_VideoTransferMatrix_BT601, DXVA2_NominalRange_16_235 },
        // SDTV ITU-R BT.601 YCbCr to computer RGB [0...255]
        { DXVA2_VideoTransferMatrix_BT601, DXVA2_NominalRange_0_255 },
        // HDTV ITU-R BT.709 YCbCr to driver's optimal RGB range
        { DXVA2_VideoTransferMatrix_BT709, DXVA2_NominalRange_Unknown },
        // HDTV ITU-R BT.709 YCbCr to studio RGB [16...235]
        { DXVA2_VideoTransferMatrix_BT709, DXVA2_NominalRange_16_235 },
        // HDTV ITU-R BT.709 YCbCr to computer RGB [0...255]
        { DXVA2_VideoTransferMatrix_BT709, DXVA2_NominalRange_0_255 }
    };

    DXVA2_Fixed32 ProcAmpValues[4] = { 0 };
    DXVA2_Fixed32 NFilterValues[6] = { 0 };
    DXVA2_Fixed32 DFilterValues[6] = { 0 };

    DXVA2_VideoSample samples[2] = { 0 };
    UINT uiStreamCount = 2;

    blt.TargetFrame = m_llTime;
    blt.TargetRect = rc;
    blt.ConstrictionSize.cx = rc.right;
    blt.ConstrictionSize.cy = rc.bottom;
    blt.BackgroundColor = color;
    blt.DestFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_Unknown;
    blt.DestFormat.NominalRange = EX_COLOR_INFO[0][1];
    blt.DestFormat.VideoTransferMatrix = DXVA2_VideoTransferMatrix_Unknown;
    blt.DestFormat.VideoLighting = DXVA2_VideoLighting_dim;
    blt.DestFormat.VideoPrimaries = DXVA2_VideoPrimaries_BT709;
    blt.DestFormat.VideoTransferFunction = DXVA2_VideoTransFunc_709;
    blt.DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
    blt.ProcAmpValues.Brightness = ProcAmpValues[0];
    blt.ProcAmpValues.Contrast.Value = 1;
    blt.ProcAmpValues.Hue = ProcAmpValues[2];
    blt.ProcAmpValues.Saturation.Value = 1;
    blt.Alpha = DXVA2_Fixed32OpaqueAlpha();
    blt.NoiseFilterLuma.Level = NFilterValues[0];
    blt.NoiseFilterLuma.Threshold = NFilterValues[1];
    blt.NoiseFilterLuma.Radius = NFilterValues[2];
    blt.NoiseFilterChroma.Level = NFilterValues[3];
    blt.NoiseFilterChroma.Threshold = NFilterValues[4];
    blt.NoiseFilterChroma.Radius = NFilterValues[5];
    blt.DetailFilterLuma.Level = DFilterValues[0];
    blt.DetailFilterLuma.Threshold = DFilterValues[1];
    blt.DetailFilterLuma.Radius = DFilterValues[2];
    blt.DetailFilterChroma.Level = DFilterValues[3];
    blt.DetailFilterChroma.Threshold = DFilterValues[4];
    blt.DetailFilterChroma.Radius = DFilterValues[5];

    samples[0].Start = m_llTime;
    samples[0].End = m_llTime + m_llDuration;
    samples[0].SampleFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_MPEG2;
    samples[0].SampleFormat.NominalRange = DXVA2_NominalRange_16_235;
    samples[0].SampleFormat.VideoTransferMatrix = EX_COLOR_INFO[0][0];
    samples[0].SampleFormat.VideoLighting = DXVA2_VideoLighting_dim;
    samples[0].SampleFormat.VideoPrimaries = DXVA2_VideoPrimaries_BT709;
    samples[0].SampleFormat.VideoTransferFunction = DXVA2_VideoTransFunc_709;
    samples[0].SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
    samples[0].SrcSurface = m_pRefSurface9;
    samples[0].SrcRect = rc;
    rc.bottom = m_uiRefHeight / 2;
    samples[0].DstRect = rc;
    samples[0].PlanarAlpha = DXVA2FloatToFixed(float(0xFF) / 0xFF);

    rc.right = m_uiSubWidth;
    rc.bottom = m_uiSubHeight;

    samples[1] = samples[0];
    samples[1].SampleFormat = samples[0].SampleFormat;
    samples[1].SampleFormat.SampleFormat = DXVA2_SampleSubStream;
    samples[1].SrcSurface = m_pSubSurface9;
    samples[1].SrcRect = rc;
    rc.top = m_uiSubHeight / 2;
    samples[1].DstRect = rc;

    try {

        IF_FAILED_THROW(hr = pSample->ConvertToContiguousBuffer(&pBuffer));
        IF_FAILED_THROW(hr = MFGetService(pBuffer, MR_BUFFER_SERVICE, __uuidof(IDirect3DSurface9), (void**)&pSurface));

        IF_FAILED_THROW(hr = m_pVideoProcessor->VideoProcessBlt(pSurface, &blt, samples, uiStreamCount, NULL));
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pBuffer);
    SAFE_RELEASE(pSurface);

    return hr;
}

HRESULT CDxva2Manager::GetDxva2VideoDesc(DXVA2_VideoDesc* dxva2VideoDesc, IMFMediaType* pRefInputType) {

    HRESULT hr;

    IF_FAILED_RETURN(hr = (dxva2VideoDesc == NULL ? E_POINTER : S_OK));
    IF_FAILED_RETURN(hr = (pRefInputType == NULL ? E_POINTER : S_OK));

    D3DFORMAT D3DFormat = D3DFMT_UNKNOWN;
    GUID subtype = { 0 };
    UINT32 uiWidth = 0;
    UINT32 uiHeight = 0;
    UINT32 uiNumerator = 0;
    UINT32 uiDenominator = 0;

    const UINT EX_COLOR_INFO[][2] =
    {
        // SDTV ITU-R BT.601 YCbCr to driver's optimal RGB range
        { DXVA2_VideoTransferMatrix_BT601, DXVA2_NominalRange_Unknown },
        // SDTV ITU-R BT.601 YCbCr to studio RGB [16...235]
        { DXVA2_VideoTransferMatrix_BT601, DXVA2_NominalRange_16_235 },
        // SDTV ITU-R BT.601 YCbCr to computer RGB [0...255]
        { DXVA2_VideoTransferMatrix_BT601, DXVA2_NominalRange_0_255 },
        // HDTV ITU-R BT.709 YCbCr to driver's optimal RGB range
        { DXVA2_VideoTransferMatrix_BT709, DXVA2_NominalRange_Unknown },
        // HDTV ITU-R BT.709 YCbCr to studio RGB [16...235]
        { DXVA2_VideoTransferMatrix_BT709, DXVA2_NominalRange_16_235 },
        // HDTV ITU-R BT.709 YCbCr to computer RGB [0...255]
        { DXVA2_VideoTransferMatrix_BT709, DXVA2_NominalRange_0_255 }
    };

    IF_FAILED_RETURN(hr = pRefInputType->GetGUID(MF_MT_SUBTYPE, &subtype));
    IF_FAILED_RETURN(hr = MFGetAttributeSize(pRefInputType, MF_MT_FRAME_SIZE, &uiWidth, &uiHeight));
    IF_FAILED_RETURN(hr = MFGetAttributeRatio(pRefInputType, MF_MT_FRAME_RATE, &uiNumerator, &uiDenominator));

    if (subtype == MFVideoFormat_NV12)
        D3DFormat = (D3DFORMAT)D3DFMT_NV12;
    else
        IF_FAILED_RETURN(hr = E_FAIL);

    dxva2VideoDesc->SampleWidth = uiWidth;
    dxva2VideoDesc->SampleHeight = uiHeight;
    dxva2VideoDesc->SampleFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_MPEG2;
    dxva2VideoDesc->SampleFormat.NominalRange = DXVA2_NominalRange_16_235;
    dxva2VideoDesc->SampleFormat.VideoTransferMatrix = EX_COLOR_INFO[0][0];
    dxva2VideoDesc->SampleFormat.VideoLighting = DXVA2_VideoLighting_dim;
    dxva2VideoDesc->SampleFormat.VideoPrimaries = DXVA2_VideoPrimaries_BT709;
    dxva2VideoDesc->SampleFormat.VideoTransferFunction = DXVA2_VideoTransFunc_709;
    dxva2VideoDesc->SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
    dxva2VideoDesc->Format = D3DFormat;
    dxva2VideoDesc->InputSampleFreq.Numerator = uiNumerator;
    dxva2VideoDesc->InputSampleFreq.Denominator = uiDenominator;
    dxva2VideoDesc->OutputFrameFreq.Numerator = uiNumerator;
    dxva2VideoDesc->OutputFrameFreq.Denominator = uiDenominator;

    return hr;
}

最后,EvrMediaSession代码:

//----------------------------------------------------------------------------------------------
// Main.cpp
//----------------------------------------------------------------------------------------------
#pragma once
#define WIN32_LEAN_AND_MEAN
#define STRICT

#include <WinSDKVer.h>
#include <new>
#include <windows.h>

//----------------------------------------------------------------------------------------------
// Common MFNode Files
#ifdef _DEBUG
#define MF_USE_LOGGING 1
#else
#define MF_USE_LOGGING 0
#endif

#include "C:\Project\MFNode\Common\MFInclude.h"

// {B2F74C92-79DF-45DE-9C55-A99DE8276679}
DEFINE_GUID(CLSID_CustomVideoMixer, 0xb2f74c92, 0x79df, 0x45de, 0x9c, 0x55, 0xa9, 0x9d, 0xe8, 0x27, 0x66, 0x79);

#define WINDOWAPPLICATION_CLASS L"WindowApplication"

// Hardcoded : change if needed
#define VIDEO_WIDTH_1 320
#define VIDEO_HEIGHT_1 240
#define VIDEO_FILE_1 L"C:\Project\h264\big_buck_bunny_240p_5mb.mp4"
#define VIDEO_FILE_2 L"C:\Project\h264\big_buck_bunny_240p_5mb - Copie.mp4"

HWND g_hWnd = NULL;
HANDLE g_hSessionEvent = NULL;
IMFMediaSession* g_pSession = NULL;
IMFMediaSource* g_pVideoSource1 = NULL;
IMFMediaSource* g_pVideoSource2 = NULL;
IMFMediaSource* g_pAggregatedSource = NULL;

class CCustomAsyncCallback : public IMFAsyncCallback {

public:

    CCustomAsyncCallback() : m_nRefCount(1) {}
    virtual ~CCustomAsyncCallback() {}

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {

        static const QITAB qit[] = {
            QITABENT(CCustomAsyncCallback, IMFAsyncCallback),
            { 0 }
        };

        return QISearch(this, qit, riid, ppv);
    }
    STDMETHODIMP_(ULONG) AddRef() {

        LONG lRef = InterlockedIncrement(&m_nRefCount);
        return lRef;
    }
    STDMETHODIMP_(ULONG) Release() {

        ULONG uCount = InterlockedDecrement(&m_nRefCount);

        if (uCount == 0) {
            delete this;
        }

        return uCount;
    }

    // IMFAsyncCallback
    STDMETHODIMP GetParameters(DWORD*, DWORD*) { return E_NOTIMPL; }
    STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult) {

        IMFMediaEvent* pEvent = NULL;
        HRESULT hr = S_OK;
        HRESULT hrStatus;
        MediaEventType EventType;

        AutoLock lock(m_CriticSection);

        try {

            IF_FAILED_THROW(hr = g_pSession->EndGetEvent(pAsyncResult, &pEvent));

            IF_FAILED_THROW(hr = pEvent->GetType(&EventType));

            TRACE((L"Invoke %s", MFEventString(EventType)));

            IF_FAILED_THROW(hr = pEvent->GetStatus(&hrStatus));

            if (FAILED(hrStatus)) {

                LOG_HRESULT(hr = hrStatus);

                LOG_HRESULT(hr = g_pSession->BeginGetEvent(this, NULL));
                SAFE_RELEASE(pEvent);

                //SetEvent(g_hSessionEvent);

                return S_OK;
            }

            if (EventType == MESessionTopologyStatus) {

                MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID;
                LOG_HRESULT(hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, (UINT32*)&TopoStatus));
                TRACE((L"TopoStatus %s", MFTopologyStatusString(TopoStatus)));

                if(TopoStatus == MF_TOPOSTATUS_READY)
                    SetEvent(g_hSessionEvent);
            }

            if (EventType != MESessionClosed) {

                LOG_HRESULT(hr = g_pSession->BeginGetEvent(this, NULL));
            }
            else {
                SetEvent(g_hSessionEvent);
            }
        }
        catch (HRESULT) {}

        SAFE_RELEASE(pEvent);

        return S_OK;
    }

private:

    CriticSection m_CriticSection;
    volatile long m_nRefCount;
};

CCustomAsyncCallback* g_pCustomAsyncCallback = NULL;

void FreeMediaObject();
HRESULT ProcessVideo();
HRESULT CreateMediaSource(IMFMediaSource**, LPCWSTR);
HRESULT CreateAggregatedSource(IMFMediaSource*, IMFMediaSource*, IMFMediaSource**);
HRESULT CreateTopologyAggregated(IMFTopology**, IMFMediaSource*);
HRESULT BuildTopology(IMFTopology*, IMFPresentationDescriptor*, IMFMediaSource*, IMFStreamSink*, IMFStreamSink*);
HRESULT CreateSourceStreamNode(IMFMediaSource*, IMFPresentationDescriptor*, IMFStreamDescriptor*, IMFTopologyNode**);
HRESULT CreateOutputNode(IMFStreamDescriptor*, IMFTopologyNode**, IMFStreamSink*);
HRESULT InitWindow(const UINT, const UINT);
LRESULT CALLBACK WindowApplicationMsgProc(HWND, UINT, WPARAM, LPARAM);

void main() {

    HRESULT hr;

    LOG_HRESULT(hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));

    if (SUCCEEDED(hr)) {

        LOG_HRESULT(hr = MFStartup(MF_VERSION, MFSTARTUP_LITE));

        if (SUCCEEDED(hr)) {

            LOG_HRESULT(hr = ProcessVideo());

            if (SUCCEEDED(hr)) {

                MSG msg;
                ZeroMemory(&msg, sizeof(MSG));

                while (GetMessage(&msg, NULL, 0, 0) > 0) {

                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }

            FreeMediaObject();

            LOG_HRESULT(hr = MFShutdown());
        }

        CoUninitialize();
    }
}

void FreeMediaObject() {

    HRESULT hr = S_OK;

    if (g_pSession != NULL) {

        LOG_HRESULT(hr = g_pSession->Close());

        DWORD dwWaitResult = WaitForSingleObject(g_hSessionEvent, 10000);

        if (dwWaitResult == WAIT_TIMEOUT)
        {
            assert(FALSE);
        }
    }

    if (g_pAggregatedSource) {

        g_pAggregatedSource->Shutdown();
        SAFE_RELEASE(g_pAggregatedSource);
    }

    SAFE_RELEASE(g_pVideoSource1);
    SAFE_RELEASE(g_pVideoSource2);
    SAFE_RELEASE(g_pCustomAsyncCallback);

    if (g_pSession) {

        LOG_HRESULT(hr = g_pSession->Shutdown());

        ULONG ulTest = g_pSession->Release();
        g_pSession = NULL;

        assert(ulTest == 0);
    }

    if (g_hSessionEvent)
    {
        CloseHandle(g_hSessionEvent);
        g_hSessionEvent = NULL;
    }

    if (IsWindow(g_hWnd)) {

        DestroyWindow(g_hWnd);
        UnregisterClass(WINDOWAPPLICATION_CLASS, GetModuleHandle(NULL));
        g_hWnd = NULL;
    }
}

HRESULT ProcessVideo() {

    HRESULT hr = S_OK;
    IMFTopology* pTopology = NULL;

    PROPVARIANT varStart;
    PropVariantInit(&varStart);
    varStart.vt = VT_EMPTY;

    try {

        g_pCustomAsyncCallback = new (std::nothrow)CCustomAsyncCallback();
        IF_FAILED_THROW(hr = (g_pCustomAsyncCallback == NULL ? E_OUTOFMEMORY : S_OK));

        g_hSessionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        IF_FAILED_THROW(hr = (g_hSessionEvent == NULL ? E_OUTOFMEMORY : S_OK));

        IF_FAILED_THROW(hr = CreateMediaSource(&g_pVideoSource1, VIDEO_FILE_1));
        IF_FAILED_THROW(hr = CreateMediaSource(&g_pVideoSource2, VIDEO_FILE_2));
        IF_FAILED_THROW(hr = CreateAggregatedSource(g_pVideoSource1, g_pVideoSource2, &g_pAggregatedSource));

        IF_FAILED_THROW(hr = CreateTopologyAggregated(&pTopology, g_pAggregatedSource));
        IF_FAILED_THROW(hr = MFCreateMediaSession(NULL, &g_pSession));
        IF_FAILED_THROW(hr = g_pSession->BeginGetEvent((IMFAsyncCallback*)g_pCustomAsyncCallback, NULL));
        IF_FAILED_THROW(hr = g_pSession->SetTopology(0, pTopology));

        DWORD dwWaitResult = WaitForSingleObject(g_hSessionEvent, 10000);

        if (dwWaitResult == WAIT_TIMEOUT)
        {
            IF_FAILED_THROW(hr = E_FAIL);
        }

        LOG_HRESULT(hr = g_pSession->Start(&GUID_NULL, &varStart));
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pTopology);

    PropVariantClear(&varStart);

    return hr;
}

HRESULT CreateMediaSource(IMFMediaSource** ppSource, LPCWSTR szURL) {

    HRESULT hr = S_OK;
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver* pSourceResolver = NULL;
    IUnknown* pSource = NULL;

    try {

        IF_FAILED_THROW(hr = MFCreateSourceResolver(&pSourceResolver));
        IF_FAILED_THROW(hr = pSourceResolver->CreateObjectFromURL(szURL, MF_RESOLUTION_MEDIASOURCE, NULL, &ObjectType, &pSource));
        IF_FAILED_THROW(hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource)));
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pSource);
    SAFE_RELEASE(pSourceResolver);

    return hr;
}

HRESULT CreateAggregatedSource(IMFMediaSource* pSource1, IMFMediaSource* pSource2, IMFMediaSource** ppAggregatedSource) {

    IMFCollection* pCollection = NULL;

    HRESULT hr = MFCreateCollection(&pCollection);

    if (SUCCEEDED(hr)) {

        hr = pCollection->AddElement(pSource1);
    }

    if (SUCCEEDED(hr)) {

        hr = pCollection->AddElement(pSource2);
    }

    if (SUCCEEDED(hr)) {

        hr = MFCreateAggregateSource(pCollection, ppAggregatedSource);
    }

    SAFE_RELEASE(pCollection);

    return hr;
}

HRESULT CreateTopologyAggregated(IMFTopology** ppTopology, IMFMediaSource* pSource) {

    assert(ppTopology != NULL);
    assert(pSource != NULL);

    HRESULT hr = S_OK;

    IMFTopology* pTopology = NULL;
    IMFPresentationDescriptor* pSourcePD = NULL;
    IMFActivate* pEvrActivate = NULL;
    //IMFVideoRenderer* pVideoRenderer = NULL;
    IMFMediaSink* pEvrSink = NULL;
    IMFStreamSink* pStreamSink1 = NULL;
    IMFStreamSink* pStreamSink2 = NULL;

    try {

        IF_FAILED_THROW(hr = MFCreateTopology(&pTopology));

        IF_FAILED_THROW(hr = pSource->CreatePresentationDescriptor(&pSourcePD));

        IF_FAILED_THROW(hr = InitWindow(VIDEO_WIDTH_1, VIDEO_HEIGHT_1));
        IF_FAILED_THROW(hr = MFCreateVideoRendererActivate(g_hWnd, &pEvrActivate));
        IF_FAILED_THROW(hr = pEvrActivate->SetGUID(MF_ACTIVATE_CUSTOM_VIDEO_MIXER_CLSID, CLSID_CustomVideoMixer));
        //IF_FAILED_THROW(hr = pEvrActivate->ActivateObject(__uuidof(IMFVideoRenderer), reinterpret_cast<void**>(&pVideoRenderer)));
        //IF_FAILED_THROW(hr = pVideoRenderer->InitializeRenderer(NULL, NULL));

        IF_FAILED_THROW(hr = pEvrActivate->ActivateObject(__uuidof(IMFMediaSink), reinterpret_cast<void**>(&pEvrSink)));
        IF_FAILED_THROW(hr = pEvrSink->GetStreamSinkByIndex(0, &pStreamSink1));
        IF_FAILED_THROW(hr = pEvrSink->AddStreamSink(1, NULL, &pStreamSink2));

        IF_FAILED_THROW(hr = BuildTopology(pTopology, pSourcePD, pSource, pStreamSink1, pStreamSink2));

        *ppTopology = pTopology;
        (*ppTopology)->AddRef();
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pStreamSink2);
    SAFE_RELEASE(pStreamSink1);
    SAFE_RELEASE(pEvrSink);
    //SAFE_RELEASE(pVideoRenderer);
    SAFE_RELEASE(pEvrActivate);
    SAFE_RELEASE(pTopology);
    SAFE_RELEASE(pSourcePD);

    return hr;
}

HRESULT BuildTopology(IMFTopology* pTopology, IMFPresentationDescriptor* pSourcePD, IMFMediaSource* pSource, IMFStreamSink* pStreamSink1, IMFStreamSink* pStreamSink2) {

    assert(pTopology != NULL);

    HRESULT hr = S_OK;

    IMFStreamDescriptor* pSourceSD = NULL;
    IMFTopologyNode* pSourceNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;
    IMFMediaTypeHandler* pHandler = NULL;
    BOOL bSelected = FALSE;
    DWORD dwStreamCount;
    GUID guidMajorType = GUID_NULL;
    BOOL bRef = TRUE;

    try {

        IF_FAILED_THROW(hr = pSourcePD->GetStreamDescriptorCount(&dwStreamCount));

        for (DWORD i = 0; i < dwStreamCount; i++) {

            IF_FAILED_THROW(hr = pSourcePD->GetStreamDescriptorByIndex(i, &bSelected, &pSourceSD));

            if (bSelected) {

                IF_FAILED_THROW(hr = pSourceSD->GetMediaTypeHandler(&pHandler));
                IF_FAILED_THROW(hr = pHandler->GetMajorType(&guidMajorType));

                if (guidMajorType == MFMediaType_Video) {

                    IF_FAILED_THROW(hr = CreateSourceStreamNode(pSource, pSourcePD, pSourceSD, &pSourceNode));

                    if (bRef) {

                        bRef = FALSE;
                        IF_FAILED_THROW(hr = CreateOutputNode(pSourceSD, &pOutputNode, pStreamSink1));

                        IF_FAILED_THROW(hr = pTopology->AddNode(pSourceNode));
                        IF_FAILED_THROW(hr = pTopology->AddNode(pOutputNode));

                        IF_FAILED_THROW(hr = pSourceNode->ConnectOutput(0, pOutputNode, 0));
                    }
                    else {

                        IF_FAILED_THROW(hr = CreateOutputNode(pSourceSD, &pOutputNode, pStreamSink2));

                        IF_FAILED_THROW(hr = pTopology->AddNode(pSourceNode));
                        IF_FAILED_THROW(hr = pTopology->AddNode(pOutputNode));

                        IF_FAILED_THROW(hr = pSourceNode->ConnectOutput(0, pOutputNode, 0));
                    }
                }
                /*else if (guidMajorType == MFMediaType_Audio) {

                    IF_FAILED_THROW(hr = CreateSourceStreamNode(pSource, pSourcePD, pSourceSD, &pSourceNode));
                    IF_FAILED_THROW(hr = CreateOutputNode(pSourceSD, &pOutputNode, NULL));

                    IF_FAILED_THROW(hr = pTopology->AddNode(pSourceNode));
                    IF_FAILED_THROW(hr = pTopology->AddNode(pOutputNode));

                    IF_FAILED_THROW(hr = pSourceNode->ConnectOutput(0, pOutputNode, 0));
                }*/
                else {

                    IF_FAILED_THROW(hr = pSourcePD->DeselectStream(i));
                }

                SAFE_RELEASE(pHandler);
                SAFE_RELEASE(pOutputNode);
                SAFE_RELEASE(pSourceNode);
            }

            SAFE_RELEASE(pSourceSD);
        }
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pHandler);
    SAFE_RELEASE(pOutputNode);
    SAFE_RELEASE(pSourceNode);
    SAFE_RELEASE(pSourceSD);

    return hr;
}

HRESULT CreateSourceStreamNode(IMFMediaSource* pSource, IMFPresentationDescriptor* pSourcePD, IMFStreamDescriptor* pSourceSD, IMFTopologyNode** ppNode) {

    if (!pSource || !pSourcePD || !pSourceSD || !ppNode) {
        return E_POINTER;
    }

    IMFTopologyNode* pNode = NULL;
    HRESULT hr = S_OK;

    try {

        IF_FAILED_THROW(hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode));

        IF_FAILED_THROW(hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource));

        IF_FAILED_THROW(hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pSourcePD));

        IF_FAILED_THROW(hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSourceSD));

        *ppNode = pNode;
        (*ppNode)->AddRef();
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pNode);

    return hr;
}

HRESULT CreateOutputNode(IMFStreamDescriptor* pSourceSD, IMFTopologyNode** ppNode, IMFStreamSink* pStreamSink) {

    IMFTopologyNode* pNode = NULL;
    IMFMediaTypeHandler* pHandler = NULL;
    IMFMediaType* pMediaType = NULL;
    IMFActivate* pActivate = NULL;

    GUID guidMajorType = GUID_NULL;
    HRESULT hr = S_OK;

    try {

        IF_FAILED_THROW(hr = pSourceSD->GetMediaTypeHandler(&pHandler));

        IF_FAILED_THROW(hr = pHandler->GetMajorType(&guidMajorType));

        IF_FAILED_THROW(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode));

        if (MFMediaType_Video == guidMajorType) {

            IF_FAILED_THROW(hr = pHandler->GetCurrentMediaType(&pMediaType));
            IF_FAILED_THROW(hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
            IF_FAILED_THROW(hr = pHandler->SetCurrentMediaType(pMediaType));

            IF_FAILED_THROW(hr = pNode->SetObject(pStreamSink));
        }
        else if (MFMediaType_Audio == guidMajorType) {

            IF_FAILED_THROW(hr = MFCreateAudioRendererActivate(&pActivate));
            IF_FAILED_THROW(hr = pNode->SetObject(pActivate));
        }
        else {

            IF_FAILED_THROW(hr = E_FAIL);
        }

        *ppNode = pNode;
        (*ppNode)->AddRef();
    }
    catch (HRESULT) {}

    SAFE_RELEASE(pNode);
    SAFE_RELEASE(pHandler);
    SAFE_RELEASE(pMediaType);
    SAFE_RELEASE(pActivate);

    return hr;
}

HRESULT InitWindow(const UINT uiWidth, const UINT uiHeight) {

    WNDCLASSEX WndClassEx;

    WndClassEx.cbSize = sizeof(WNDCLASSEX);
    WndClassEx.style = CS_HREDRAW | CS_VREDRAW;
    WndClassEx.lpfnWndProc = WindowApplicationMsgProc;
    WndClassEx.cbClsExtra = 0L;
    WndClassEx.cbWndExtra = 0L;
    WndClassEx.hInstance = GetModuleHandle(NULL);
    WndClassEx.hIcon = NULL;
    WndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClassEx.hbrBackground = NULL;
    WndClassEx.lpszMenuName = NULL;
    WndClassEx.lpszClassName = WINDOWAPPLICATION_CLASS;
    WndClassEx.hIconSm = NULL;

    if (!RegisterClassEx(&WndClassEx)) {
        return E_FAIL;
    }

    int iWndL = uiWidth + 8 + GetSystemMetrics(SM_CXSIZEFRAME) * 2;
    int iWndH = uiHeight + 8 + GetSystemMetrics(SM_CYSIZEFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION);

    int iXWnd = (GetSystemMetrics(SM_CXSCREEN) - iWndL) / 2;
    int iYWnd = (GetSystemMetrics(SM_CYSCREEN) - iWndH) / 2;

    if ((g_hWnd = CreateWindowEx(WS_EX_ACCEPTFILES, WINDOWAPPLICATION_CLASS, WINDOWAPPLICATION_CLASS, WS_OVERLAPPEDWINDOW, iXWnd, iYWnd,
    iWndL, iWndH, GetDesktopWindow(), NULL, GetModuleHandle(NULL), NULL)) == NULL) {
        return E_FAIL;
    }

    RECT rc;
    GetClientRect(g_hWnd, &rc);

    // If failed change iWndL or/and iWndH to be TRUE
    assert(rc.right == VIDEO_WIDTH_1 && rc.bottom == VIDEO_HEIGHT_1);

    ShowWindow(g_hWnd, SW_SHOW);

    return S_OK;
}

LRESULT CALLBACK WindowApplicationMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {

    if (msg == WM_PAINT) {

        ValidateRect(hWnd, NULL);
        return 0L;
    }
    else if (msg == WM_ERASEBKGND) {

        return 1L;
    }
    else if (msg == WM_CLOSE) {

        PostQuitMessage(0);
        return 0L;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}