您的位置:首页 > 其它

利用DSHOW中的VMR9 filter 将视频渲染成纹理 供D3D使用

2012-04-20 10:10 295 查看
先说下VMR9,这个 filter是个视频混合的组件,可以很方便的将多路视频合成一路视频,添加字幕和静态图片,这个组件的内部实现采用了DX9的接口。如果想把VMR9混合输出后的视频图像当作纹理渲染到3D模型上,一个办法就是通过实现一个分配-演示器对象,然后将此对象替换掉VMR9中的默认分配-演示器对象。所谓分配演示器对象指的是一个实现了VMR9规定的的分配接口和演示接口的对象。也就是此对象实现了以下两个接口:

1) 分配接口(用来完成分配VMR9需要的各种D3D 表面资源 ):
virtual HRESULT STDMETHODCALLTYPE InitializeDevice(...) = 0;
virtual HRESULT STDMETHODCALLTYPE TerminateDevice(...) = 0;
virtual HRESULT STDMETHODCALLTYPE GetSurface(...) = 0;
virtual HRESULT STDMETHODCALLTYPE AdviseNotify( ...) = 0;

2 ) 演示接口 (用来将VMR9的输出视频展示出来):
virtual HRESULT STDMETHODCALLTYPE StartPresenting( ...) = 0;
virtual HRESULT STDMETHODCALLTYPE StopPresenting( ...) = 0;
virtual HRESULT STDMETHODCALLTYPE PresentImage( ...) = 0;

下面是主要的步骤:

// 获取VMR9滤波器
CoCreateInstance( CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, ( void** )&m_vmrFilter );

// 生成分配演示器对象
CAllocatorPresent * pAllocPresent = new CAllocatorPresent( m_vmrFilter );

// 然后将其替换VMR9的默认分配演示器对象
m_vmrFilter->QueryInterface( IID_IVMRSurfaceAllocatorNotify9, reinterpret_cast< void ** >( &lpIVMRSurfAllocNotify ) );

hr = lpIVMRSurfAllocNotify->AdviseSurfaceAllocator( usrId, m_allocator );

// 通知VMR9我们的DX9设备资源
HMONITOR hMonitor = Graphic::Instance().m_pD3D->GetAdapterMonitor( D3DADAPTER_DEFAULT );
hr = lpIVMRSurfAllocNotify->SetD3DDevice( Graphic::Instance().m_pd3dDevice, hMonitor );

// 添加我们的VMR9滤波器到dshow 图表里去
m_pGraphBuilder->AddFilter( m_vmrFilter, L"Video Mixing Renderer 9" );

// 连接所有滤波器
m_pGraphBuilder->RenderFile( L"C:\\Users\\sky\\Desktop\\big_buck_bunny_1080p.avi", NULL );

在完成了添加VMR9的基本操作之后,我们就可以在我们自定义的分配演示器中的PresentImage接口中获取视频合成的结果了。

HRESULT CAllocatorPresent::PresentImage( /* [in] */ DWORD_PTR dwUserID, /* [in] */ VMR9PresentationInfo * lpPresInfo )
{
// 将视频输出拷贝到D3D的纹理中去
SmartPtr< IDirect3DSurface9 > surface;
Graphic::Instance().m_pMovieTexture->GetSurfaceLevel( 0, &surface );
Graphic::Instance().m_pd3dDevice->StretchRect( lpPresInfo->lpSurf, NULL, surface, NULL, D3DTEXF_LINEAR );

// 绘制3D场景
m_scene.Render();

}

因为用的是DX9,所以需要注意下DX9的设备资源丢失问题。

通过将绘制结果显示到屏幕上,可以知道设备是否丢失,vmr9allocato这个例子中对设备丢失的处理是不正确的,可以参考下面的处理方法。
// Flip it
HRESULT hr = device->Present( NULL, NULL, NULL, NULL );

if( D3DERR_DEVICELOST == hr )
{
  while ( device->TestCooperativeLevel() != D3DERR_DEVICENOTRESET )
{

    Sleep( 100 );
}

  CVideoRenderingIn3DDlg::m_filterGraph.OnDeviceLost();

  Graphic::Instance().Clean();

Graphic::Instance().OnCreateDevice();

CVideoRenderingIn3DDlg::m_filterGraph.OnDeviceReset();

hr = S_OK;

}

通过上面的方法可以看到利用VMR9渲染视频到D3D的纹理采用的是DSHOW驱动的,D3D是辅助的结合方法。如果将VMR9和D3D独立进行个自的渲染是否可行呢?我进行过一些测试,发现一个问题。就是在D3D独立进行场景渲染时,有时会出现闪屏现象。也就是D3D的一些调用如,BeginScene等会出现失败。这可能和VMR9利用DX9进行视频合成时会占用DX9资源,和D3D进行场景绘制时产生冲突所致。这个问题也许要等VMR的升级来解决。

具体代码可以参考Microsoft Windows SDK v7.1 中一个叫做vmr9allocator的例子.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: