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剛的台詞 真是"時代的眼淚"

2014年12月27日 星期六

Spark 啟動流程追蹤隨筆

SparkContext 內 YARN 的啟動流程
https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/SparkContext.scala



 case "yarn-standalone" | "yarn-cluster" =>
        if (master == "yarn-standalone") {
          logWarning(
            "\"yarn-standalone\" is deprecated as of Spark 1.0. Use \"yarn-cluster\" instead.")
        }
        val scheduler = try {
          val clazz = Class.forName("org.apache.spark.scheduler.cluster.YarnClusterScheduler")
          val cons = clazz.getConstructor(classOf[SparkContext])
          cons.newInstance(sc).asInstanceOf[TaskSchedulerImpl]
        } catch {
          // TODO: Enumerate the exact reasons why it can fail
          // But irrespective of it, it means we cannot proceed !
          case e: Exception => {
            throw new SparkException("YARN mode not available ?", e)
          }
        }
        val backend = try {
          val clazz =
            Class.forName("org.apache.spark.scheduler.cluster.YarnClusterSchedulerBackend")
          val cons = clazz.getConstructor(classOf[TaskSchedulerImpl], classOf[SparkContext])
          cons.newInstance(scheduler, sc).asInstanceOf[CoarseGrainedSchedulerBackend]
        } catch {
          case e: Exception => {
            throw new SparkException("YARN mode not available ?", e)
          }
        }
        scheduler.initialize(backend)
        (backend, scheduler)


     case "yarn-client" =>
        val scheduler = try {
          val clazz =
            Class.forName("org.apache.spark.scheduler.cluster.YarnClientClusterScheduler")
          val cons = clazz.getConstructor(classOf[SparkContext])
          cons.newInstance(sc).asInstanceOf[TaskSchedulerImpl]

        } catch {
          case e: Exception => {
            throw new SparkException("YARN mode not available ?", e)
          }
        }

        val backend = try {
          val clazz =
            Class.forName("org.apache.spark.scheduler.cluster.YarnClientSchedulerBackend")
          val cons = clazz.getConstructor(classOf[TaskSchedulerImpl], classOf[SparkContext])
          cons.newInstance(scheduler, sc).asInstanceOf[CoarseGrainedSchedulerBackend]
        } catch {
          case e: Exception => {
            throw new SparkException("YARN mode not available ?", e)
          }
        }

YarnClusterScheduler
YarnClusterSchedulerBackend
YarnClientClusterScheduler
YarnClientSchedulerBackend
source code 位於
https://github.com/apache/spark/tree/master/yarn/src/main/scala/org/apache/spark/scheduler/cluster

2014年6月1日 星期日

Battle Isle 1 (1991,DOS) PART 11-15 Walkthrough 狂島浴血 11-15 關 (過關實錄)

狂島浴血 第十一關-第十五關 過關實錄

Part 11. VALEY 第十一關 總時間(time) 26:58

Part 12. TESTY 第十二關 總時間(time) 47:25

Part 13. TERRA 第十三關 總時間(time) 57:05

Part 14. SLAVE 第十四關 總時間(time) 1:02:47

Part 15. NEVER 第十五關 總時間(time) 42:28

Battle Isle 1 (1991,DOS) PART 6-10 Walkthrough 狂島浴血 6-10 關 (過關實錄)

狂島浴血 第六關-第十關 過關實錄

Part 6. RUSTY 第六關 總時間(time) 24:22


Part 7. FIFTH 第七關 總時間(time) 35:29

Part 8. VESUV 第八關 總時間(time) 32:29

Part 9. MAGIC 第九關 總時間(time) 44:11

Part 10. SPACE 第十關 總時間(time) 39:12

Battle Isle 1 (1991,DOS) PART 1-5 Walkthrough 狂島浴血 1-5 關 (過關實錄)

狂島浴血過關紀錄實況
Battle Isle 1 - 1991

Part 1. CONRA  第一關 總時間(time) 5:41

Part 2. PHASE 第二關 總時間(time) 7:44

Part 3. EXOTY 第三關 總時間(time) 9:02

Part 4. MOUNT 第四關 總時間(time) 18:54

Part 5. FIGHT 第五關 總時間(time) 20:46


2013年11月14日 星期四

sscanf with Android NDK

It seems that there are some bug in sscanf() with Android NDK-r9.

When read a line from a text file, i use the strtok() to tokenize the string, and call sscanf() to convert the token into hex value. There are some chance to get 0 from sscanf() , although it is not always occurred.

To solve the problem in NDK, i write a simple function to convert the token into hex,  the code is as follow listing:

// sscanf has bug in Android NDK
static unsigned char convertToHex( char c ) {
    if ( c >= '0' && c <= '9' ) {
        return c - '0';
    }
    if ( c >= 'A' && c <= 'F' ) {
        return c - 'A' + 10;
    }
    if ( c >= 'a' && c <= 'f' ) {
        return c - 'a' + 10;
    }
    return 0;
}
static unsigned char bytesToHex( char *token ) {
    unsigned char tmp ;
    unsigned char tmp1;
    unsigned char tmp2;
    tmp1 = convertToHex( token[0] );
    tmp2 = convertToHex( token[1] );
    tmp = tmp1 << 4 | tmp2;
    return tmp ;
};