This sample shows how to create a media sink that renders video output to a window using DirectX 11. Specifically, this sample shows how to:
- Decode the video using the Media Foundation APIs
- Render the decoded video using the DirectX 11 APIs
- Output the video stream to multi-monitor displays
他的 Ouptupt 檔案是一個 DLL 檔案, 為了閱讀流程方便, 一些細節省略
註冊主流程 DllRegisterServer() -- > RegisterObject() --> MFTRegister()
DLLMain.pp
------------------------------------------------------------------------------------
感想:
透過註冊 Class ID 的方式, 讓 其他 APPLICATION 利用 COM 與 CLS ID 啟動對應的服務元件或程式邏輯, 為了能在 Media Foundation Transform (MFT) 內列舉(Enumerate) 所以呼叫 MFTRegister , 注意 GUID 都是 CLSID_DX11VideoRenderer
COM 可以想做是 Android 的 Binder
都有 Client 和 Server, Client 發出請求, 實作程式則運作在 Server , 呼叫都是透過ABI方式
------------------------------------------------------------------------------------
DllRegisterServer(void) {
HRESULT hr = S_OK;
do {
hr = RegisterObject( CLSID_DX11VideoRenderer,
L"DX11 Video Renderer", L"Both");
if (FAILED(hr)) { break; }
hr = RegisterObject( CLSID_DX11VideoRendererActivate,
L"DX11 Video Renderer Activate", L"Both");
if (FAILED(hr)) { break; }
hr = MFTRegister(
CLSID_DX11VideoRenderer, // CLSID
MFT_CATEGORY_OTHER, // Category
L"DX11 Video Renderer", // Friendly name
0, // Reserved, must be zero.
0, NULL, 0, NULL, NULL );
}
while (FALSE);
return hr;
}
RegisterObject(GUID guid, const TCAHR* pszDescription, const TCHAR* pszThreadingModel ) {
do {
CreateObjectKeyName(guid, pszTemp, MAX_PATH);
RegCreateKeyEx(HKEY_CLASSES_ROOT,pszTemp, &hkey);
SetKeyValue(hKey, NULL, pszDescription);
RegCreateKeyEx(HKEY_CLASSES_ROOT,"InprocServer32", &hSubkey);
GetModuleFileName(g_hModule, pszTemp);
SetKeyValue(hSubKey, NULL, pszTemp);
} while (FALSE);
RegCloseKey(hSubKey);
RegCloseKey(hKey);
}
------------------------------------------------------------------------------------
何謂 MFT :Media Foundation transforms (MFTs) provide a generic model for processing media data.
可以把它想做跟 Gstreamer 的 source sink element 的概念, 兩者都很類似為的是讓每個元件可獨立發展, 做對應的工作, 但又富有彈性 可以動態重組(雖然實務上還是有限制)
------------------------------------------------------------------------------------
DLL Export
STDAPI CreateDX11VideoRenderer ( REFIID riid, void **ppvObject) {
return DX11VideoRenderer::CMediaSink::CreateInstance(riid, ppvObject);
}
STDAPI CreateDX11VideoRendererActivate(HWND hwnd, IMFActivate **ppActivate) {
return DX11VideoRenderer::CActivate::CreateInstance(hwnd, ppActivate);
}
------------------------------------------------------------------------------------
Double potiner 的寫法在 COM 裡面很常見, 目的是 pass 指標的指標, 回填指標的值
例如 **ppvObject
實際上是丟進去的值為 void *pObject;
呼叫 CreateDX11VideoRenderer( RIID, &pObject );
這樣就可以讓 function 幫我們填寫指標的值
------------------------------------------------------------------------------------
HRESULT DX11VideoRenderer::CMediaSink::CreateInstance
( _In_ REFIID iid, _COM_Outptr_ void **ppSink ) {
CMediaSink *psink = new CMediaSink();
do_error_check
pSink->Initialize();
pSink->QueryInterface(iid, ppSink);
SafeRelease( pSink );
return HRESULT;
}
HRESULT DX11VideoRenderer::CMediaSink::QueryInterface
(REFIID iid, __RPC__deref_out _Result_nullonfailure_ void **ppv)
{
if ( iid == IID_IUnknown ) ppv = static_cast
else if ( iid == __uuidof(IMFMediaSink )
ppv = static_cast<IMFMediaSink*>(this);
else if ( iid == __uuidof(IMFClockStateSink )
ppv = static_cast<IMFClockStateSink*>(this);
else if ( iid == __uuidof(IMFGetService)
ppv = static_cast<IMFGetService*>(this);
else if ( iid == __uuidof(IMFRateSupport)
ppv = static_cast<IMFRateSupport*>(this);
else if ( iid == __uuidof(IMFMediaSinkPreroll)
ppv = static_cast<IMFMediaSinkPreroll*>(this);
else ppv = NULL;
AddRef(); // 這裡與 SafeRelease 互相呼應
}
------------------------------------------------------------------------------------
注意 _in_ 與 _COM_Outptr_ 表是一個虛的MARCO, 目的是讓 programmer 知道
這個參數是 input 參數, 或是 ouptut 參數, 實際上的定義通常為
#define _In_
#define _COM_Outptr_
#define __RPC__deref_out
#define __Result_nullonfailure
類似 Java的 annotation
其他請參考 https://msdn.microsoft.com/zh-tw/library/hh916382.aspx
static_cast就是C++的轉型, 可以把 this 指標安全的轉換成不同的 class 指標
IMFMediaSink 與 IMFClockStateSink 的定義在 Media Foundation Interface 中都有定義
https://msdn.microsoft.com/en-us/library/windows/desktop/ms696268(v=vs.85).aspx
static_cast 可參考
http://windsplife.blogspot.tw/2010/09/cstaticcast-dynamiccast-reinterpretcast.html
new CMediaSink() 把內部 class 的 member 清除為 0
------------------------------------------------------------------------------------
HRESULT DX11VideoRenderer::CMediaSink::Initialize(void)
{
m_pScheduler = new CScheduler()
m_pStream = new CStreamSink()
m_pPresenter = new CPresenter(); // let ref count = 1
QueryInterface(IID_PPV_ARGS(&pSink));
m_pStream->Initialize(pSink, m_pPresenter);
m_pScheduler->SetCallback( static_cast
if ( FAILED )Shutdown () ;
SafeRelease(pSink);
}
------------------------------------------------------------------------------------
這邊呼叫 QueryInterface 是呼叫 MediaSink 的 QueryInterface
IID_PPV_ARGS 的定義在 ObjBase.h
extern "C++"
{
template<typename T> void** IID_PPV_ARGS_Helper(T** pp)
{
// make sure everyone derives from IUnknown
static_cast(*pp);
return reinterpret_cast<void**>(pp);
}
}
#define IID_PPV_ARGS(ppType) __uuidof(**(ppType)), IID_PPV_ARGS_Helper(ppType)
http://blogs.msdn.com/b/yvesdolc/archive/2006/12/27/iid-ppv-args-macro.aspx
new CPresenter 會讓 MediaSink 的 Reference Count + 1
------------------------------------------------------------------------------------