VMR9的定制AP

来源:互联网 发布:淘宝商品链接在哪 编辑:程序博客网 时间:2024/05/01 05:51

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