VMR9的定製AP

http://blog.csdn.net/foruok/archive/2008/07/27/2719724.aspx

我要在3D場景中播放電影,或者把電影作爲紋理來使用。
    有兩個辦法,一個是寫一個video render,用它來接管filter graph的最終輸出,將視頻數據拷貝到我們的私有紋理上,然後在合適的時候來使用它(主要是在D3D相關的主循環中)。
    另一個是利用VMR9。VMR9允許我們自己提供Allocator-Presenter,以便完成一些特殊的需要。我們提供自己的A/P,VMR9會調用AP的PresentImage函數來做實際的渲染。我們可以在這裏把視頻數據旁路到私有紋理中。
    第一個辦法比較麻煩,因爲視頻解碼後的格式多種多樣,這些你都要自己處理。比如YUV、RGB565、RGB32、YUY2等等,亂七八糟。當我播放輸出爲YUY2的電影時由於我的video render只接收RGB格式,ActiveMovie窗口彈了出來,大煞風景。
    而用後一種辦法,上面這些東西VMR9都幫你處理好了,你所要的只是一個StretchRect調用而已!
    SDK中有個MultiVMR9(Samples/cpp/DirectShow/VMR9/MultiVMR9/MultiPlayer)和GamePlayer(Samples/cpp/DirectShow/VMR9/MultiVMR9/MultiPlayer)的例子,還有一個VMR9Allocator(Samples/C++/DirectShow/VMR9/VMR9Allocator),這幾個例子相當有用。GamePlayer構造了一面電影牆,效果很酷。
    不過在這些例子中,D3D是作爲視頻播放流程的附屬而存在。我們現在想反過來,D3D爲主,視頻播放爲副,平常主要是3D渲染,視頻播放可以沒事兒一邊自我娛樂,把解碼好的視頻拷貝到一個紋理上,當3D場景想起它了,直接拿來用。
    OK,研究一下VMR9Allocator。似乎修改下面幾點就可以完成了。
    1. 構造CAllocator時傳遞D3D和D3DDEVICE給它
    2. 在InitializeDevice函數中,調用AllocateSurfaceHelper 創建文理後再創建一個私有紋理
    3. 在PresentImage中把視頻拷貝到私有文理
    4. 提高GetVideoTexture接口,允許D3D部分獲取視頻紋理用作渲染
   
    確實是這麼回事,但是在實際操作時我遇到了一些問題,怎麼都解決不了,花了我兩天時間!
    1. AllocateSurfaceHelper總是失敗
    2. 旁路的視頻斷斷續續
   
    第一個問題,我不需要分配帶有VMR9AllocFlag_TextureSurface標記的紋理表面,只需要分配offscreen surfaces。於是我這樣做:lpAllocInfo->dwFlags = VMR9AllocFlag_OffscreenSurface;(GamePlayer中也是這麼做的,而且沒問題),然後調用AllocateSurfaceHelper ,結果總是失敗!
    第二個問題更嚴重,我只能得到有限的幾楨圖象。

    後來在網上(http://www.gamedev.net/community/forums/topic.asp?topic_id=380264,http://www.gamedev.net/community/forums/topic.asp?topic_id=455515)查到需要用IVMRMixerControl9接口的SetMixingPrefs方法來設置MixerPref9_RenderTargetYUV標記。這樣VMR9就不再需要紋理表面而可以只需要離乒表面,同時也不會切換render target。於是我們可以得到完整且連續的視頻。不過必須是windows xp sp2以上的系統纔可以(http://msdn.microsoft.com/en-us/library/ms788177.aspx)。
    我實驗了一下,沒有結果,後來發現SetMixingPrefs的調用順序非常關鍵!一旦你放錯了位置,不但達不到你要的效果,可能會更糟。我遇到了這麼幾個問題:
    1. 查詢不到接口,返回E_NOTIMPL
    2. 得到了接口,設置了MixerPref9_RenderTargetYUV,沒什麼效果
    3. 設置MixerPref9_RenderTargetYUV後以VMR9AllocFlag_TextureSurface爲標記調用AllocateSurfaceHelper 會失敗
   
    一頭霧水,後來把這個調用放到RenderFile之前,AdviseSurfaceAllocator和AdviseNotify調用之後,這樣纔可以了。不過在播放rmvb文件時出了莫名其妙的錯誤(待查)。我現在擔心這麼做之後,會有一些編碼格式在播放時出現問題(假如它的解碼filter只輸出RGB格式?),這裏說明了它的一些限制http://msdn.microsoft.com/en-us/library/ms788177.aspx。
    在我查看GamePlayer及MultVMR9 DLL的源碼時,發現它並沒有像上面那樣做,但依然可以正常使用。可見很有可能是我的一些用法不太正確。

    這裏提一下使用VMR9定製A/P的步驟,因爲WINDOWS XP默認使用VMR7,我們要自己把VMR9加到filter  graph中。SDK中有相關的文檔可以看,按那個辦法是可以的,不過比較麻煩。上面提到的幾個例子是這麼做的:
    1. 創建filter graph
    2. 創建VMR9實例並加入到graph中
    3. 配置VMR9爲renderless模式
    4. 查詢IVMRSurfaceAllocatorNotify9接口
    5. 創建定製的A/P
    6. 建議VMR9使用你的A/P(調用IVMRSurfaceAllocatorNotify9::AdviseSurfaceAllocator)
    7. 把allocator notifier提供給你的A/P(AdviseNotify)
    8. 調用RenderFile或者其他方法,讓graph智能建立剩餘的graph
    9. 查詢IMediaControl,IMediaEvent等等接口供後續使用
    10. 刪除filter graph時,先停止graph,再斷開VMR9的所有pin並將其A/P設爲NULL

    文檔在Samples/C++/DirectShow/VMR9/MultiVMR9下面VMR9Multi_Help.htm。我感覺這個文檔比directshow的SDK中說的要明白。

發佈了18 篇原創文章 · 獲贊 1 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章