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中說的要明白。