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 
------------------------------------------------------------------------------------
























2015年1月28日 星期三

漢堂-大戰略 購買密技

漢堂-大戰略 購買密技
特別打這一篇文章是因為, 這套遊戲是當年我跟著我爸, 在台中電子街購買的第一套中文戰略遊戲. (第二套是炎龍騎士團一代!!! 附贈徽章可惜淹水後就丟了...) 時間的巨輪總是不會停下來, 電子街沒落了, 現在應該也很少人會自己組裝電腦了, 當年單機遊戲的盛況只能追憶, 而回憶總是最美. 我對這一套遊戲有一種特別的情感, 但是正常玩法真的不容易破關, 而小時候有記憶看過大戰略的密技, 但是在網路上一直尋覓無門.

剛好在電腦玩家第十一期中發現了這一則密技, 特別PO上網來與大家分享

以下是該密技的原文.

摘自 電腦玩家第十一期 93頁

1. 蘇聯兵器的購買密技

首先在任務簡報畫面出現後, 便按 (註: insert )鍵離開, 然後立案按(大寫) 不放. 等到商店畫面出現, 副官向你請示後再放開, 便可購買蘇聯的兵器了.

蘇聯的兵器與盟軍的一樣, 會隨著觀樹而日漸增多. 其中以Ka-34直升機及Su-27戰鬥機. 前者的攻擊力及防護力都頗強, 後者的飛彈射程可達四格. 由而這兩者都是最高級的, 因此初期可能得用Mi-24直昇機, 或MiG-21戰鬥機來升級.

2. 秘密兵器的購買密技
在進入商店畫面, 副官向你請示之後, 請照著下列方式輸入方向鍵:

左左右右三次後, 在按下上六次. (註 左左右右 左左右右 左左右右 下上 下上 下上 下上 下上 下上, G. R. X. <遊戲作者>真是整人啊)

這時螢幕下方會出現"Input Password:"的字樣, 輸入下列的其中一組密碼即可:

P.T               PATRIOT                  愛國者飛彈系統
A12              A-12                           復仇者攻擊機
X10               X-10                           X-10 戰車 (註 有點違合的戰車, 太過超現代)
Y.M.J            MCB-4 MK.2            MCB-4 MK.2 戰車
BOOMA      AH-142 ROC           洛克鳥直升機
JEAN           RYU                           龍 (註 快打旋風的龍, 步兵系中可以遠距離攻擊)
LYC               CRUSHER              毀面者攻擊機
GRX              SHAMPOO            珊璞(註 沒錯就是亂馬1/2裡面的珊璞, 那是個沒有版權的年代, 所以OK的)

了確了一件心事, 今年2015年了, 距離1993年也已過了 22年. 套句Z剛的台詞 真是"時代的眼淚"