VMR9的定制AP

我要在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中说的要明白。

你可能感兴趣的:(VM)