2015年7月7日 星期二

Source Study : DirectX video rendering sample from MSDN

DirectX Video Rendering Sample
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(static_cast(this));
      
      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 (m_pStream));
  
   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 
------------------------------------------------------------------------------------