这是实现自定义视频混合器的一种可能方法,该混合器适用于 Windows 7 和 Microsoft MediaSession/Evr。
我发现 dxva2 可以与两种 NV12 流格式一起使用。当然,我们不能将流与 alpha 混合,但它可以工作。我的图形驱动程序告诉我 dxva2 子流只能处理 AYUV/AI44,但 NV12 也可以(奇怪)。 NV12 没有 alpha,但如果我们不叠加它们,我们可以显示两个视频(也许更多)。我还发现 CLSID_CColorConvertDMO 未能为 AYUV 提供 MediaSession/Evr 和自定义视频混合器。颜色转换可以在自定义视频混合器中完成。
我会多次发布代码,请耐心等待。这里很难格式化代码。对于代码的某些部分,您将需要来自MFNode的公共文件
有些接口只是简单地返回 E_NOTIMPL,他们只是在这里检查 Evr 需要什么。所以我省略了使用 E_NOTIMPL 的代码。
自定义视频混合器类:
//----------------------------------------------------------------------------------------------
// 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;
}