Here is a possible way to implement a Custom Video Mixer wich works on Windows 7 and Microsoft MediaSession/Evr.
I discovered that dxva2 can be use with two NV12 stream format. Of course, we can't mix streams with alpha, but it works. My graphic driver tells me that the dxva2 substream can handle only AYUV/AI44, but NV12 works too (strangely). NV12 has no alpha, but we can show two videos (and perhaps more) if we don't superimposed them. I also discovered that CLSID_CColorConvertDMO failed to provide AYUV with a MediaSession/Evr and a Custom Video Mixer. The color conversion could be done in the Custom Video Mixer.
I will post code in several times, so be patient. It is hard to format code here. For some parts of the code, you will need common files from MFNode
Some interface simply return E_NOTIMPL, they are just here to check what Evr needs. So i ommited code where E_NOTIMPL is used.
The Custom Video Mixer 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;
}