Build DirectShow Source Filter with Media Foundation

  Microsoft 在 Windows 7 里增加了对 H.264 和 AAC 这两种日渐流行的编码格式的 native support。对于 H.264,在 DirectShow 里你可以找到一个名字叫“Microsoft DTV-DVD Video Decoder”的 filter。相应的,在 Media Foundation 里有一个叫“Microsoft H264 Video Decoder”的 MFT;对于 AAC,在 DirectShow 里你可以找到一个名字叫“Microsoft DTV-DVD Audio Decoder”的 filter。在 Media Foundation 里与之对应的 MFT 叫做“Microsoft AAC Audio Decoder”。

  虽然 Windows 7 提供了对上述格式的支持,但是基于 DirectShow 构建的应用程序如果想 playback MP4 视频文件的话却不能直接从这个改进里获得好处,原因是 Microsoft 并没有随 H.264/AAC decoder 一起发布 MP4 的 source filter。所以基于 DirectShow 架构的程序想要回放 MP4 文件的话,有两个解决方案:第一种方法是自己实现一个 MP4 文件的 parser,输出合适的 media type 来配接系统自带的 H.264/AAC decoder,当然也可以用现成的。第二种方法是用 Media Foundation 来实现一个特殊的 DirectShow source filter,这个 source filter 调用 MF 来解码 MP4 数据,拿到原始数据以后直接推送给 A/V renderer。

  第一种方法需要熟悉 MP4 容器的结构,我没有动手实现过,所以这里不详述。现成的解决方案有 Haali Media SplitterGabest MP4 Splitter

  Haali Media Splitter 其实不是一个单纯的 MP4 parser,它还支持其它几种容器格式,具体可以看它官网上的介绍。Gabest MP4 Splitter 作为 guliverkli(MPC) 的一部分发行,没发现它有独立的官方网站。据两者都用过的用户说 Gabest MP4 Splitter 相对来说更稳定一些。我只尝试了 Gabest MP4 Splitter,注册过它的 DLL/ax 以后系统里多了两个新的 filter(MP4 source 和 MP4 splitter),这时如果用 GraphEdit render 一个 MP4 文件的话,会发现 MP4 source 连接了 MP4 splitter,MP4 splitter 分别连接了 Microsoft DTV-DVD Audio Decoder 和 Microsoft DTV-DVD Video Decoder,最后连接至 Audio Renderer 和 Video Renderer。

  第二种解决方案,如果只看过 Media Session 的文档的话,会感觉这是个不可能的任务,幸好 Microsoft 在 Windows 7 里对 Media Foundation 做了改进,新增的一些新组件和接口使得这个任务变得简单起来。在 Windows 7 里,我们可以用 Source Reader 来实现一个可以直接输出 uncompressed data 的 source filter。

  Source Reader 可以在某些场合下替代 Media Session 来处理多媒体音视频数据,好处是接口非常简单,还可以选择是读取 compressed data 还是 uncompressed data。你可以简单的把 Source Reader 看作是没有接 renderer 的 Media Session。具体的实现就不多说了,感兴趣的可以自行参考 Source Reader 的使用文档例子。下面侃侃 Source Reader 和 DXVA。

  Source Reader 的使用文档里其实是简单的提过一下如何配合 DXVA 做硬件加速的,但是我的实做结果是——这要看你把 Source Reader 用在什么地方了。如果在创建 Source Reader 实例的模块里你同时也做 render,那么这个硬件加速和可能实现的(这里之所以用“可能”这两个字是因为我没有去做这个尝试,因为这里提到的方案属于下面要说的情况);如果 render 是交给其它模块做的(像我这里提到的把 Source Reader 包装成一个 DS 的 source filter,rendering 是在 renderer 里做的),那 Source Reader 跟 renderer 之间的数据交换和 D3D 设施共享就不容易完成。

  把 Source Reader 的硬件加速打开可以简化成如下代码:

  1. IDirect3D9 *m_pD3D9 = Direct3DCreate9( D3D_SDK_VERSION );
  2.  
  3.  IDirect3DDevice9 *m_pD3D9Device = NULL;
  4.  HRESULT hResult = m_pD3D9->CreateDevice( ..., &m_pD3D9Device );
  5.  
  6.  UINT uResetToken = 0;
  7.  IDirect3DDeviceManager9 *m_pD3D9DeviceManager = NULL;
  8.  hResult = DXVA2CreateDirect3DDeviceManager9( &uResetToken, &m_pD3D9DeviceManager );
  9.  
  10.  hResult = m_pD3D9DeviceManager->ResetDevice( m_pD3D9Device, uResetToken );
  11.  
  12.  IMFAttributes *pAttributes = NULL;
  13.  hResult = MFCreateAttributes( &pAttributes, 1 );
  14.  hResult = pAttributes->SetUnknown( MF_SOURCE_READER_D3D_MANAGER, m_pD3D9DeviceManager );
  15.  hResult = pAttributes->SetUINT32( MF_SOURCE_READER_DISABLE_DXVA, FALSE );

  Source Reader 开启 DXVA 硬件加速要用到 Direct3D device manager,D3D device manager 其实是用来在 decoder 和 renderer 之间共享 D3D device 的。如果作为 Source Reader 使用者的我们没有直接负责 render 的话,已经用 GPU 解压到显存的 uncompressed data 要被复制出来,这样做的性能损耗据说比较大。所以在这种情况下,没什么好的方法来开启 DXVA,而且开了也没用。

-=-= 我是很普通的分割线 =-=-

WinMEnc 之后,又发现了一个可以压 iPod Touch 视频的好工具,而且同样开源——Handbrake。WinMEnc 什么都好,就是在处理 WMV 和某些 AVI 的时候,压完的 M4V 视频有问题,而且 iPT 不认,Handbrake 就没这个问题,推荐 iPT 机主使用,双剑合璧,基本上无敌;-)

Leave a Reply