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

先说下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的例子.


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 C# 下使用 ffmpeg 开发录制视频工具,可以通过调用 ffmpeg 的命令行参数实现。在 Unity 使用时,可以将 ffmpeg 的可执行文件放到 Unity 项目的 Assets 文件夹下,并在代码调用命令行参数来实现录制视频的功能。 以下是一个简单的示例代码: ```csharp using System.Diagnostics; public class VideoRecorder : MonoBehaviour { private Process process; private string ffmpegPath = Application.dataPath + "/ffmpeg.exe"; private string videoName = "output.mp4"; public void StartRecording() { if (!File.Exists(ffmpegPath)) { Debug.LogError("ffmpeg.exe not found!"); return; } string arguments = "-f dshow -i video=\"screen-capture-recorder\" -r 60 \"" + videoName + "\""; ProcessStartInfo processInfo = new ProcessStartInfo(ffmpegPath, arguments); processInfo.CreateNoWindow = true; processInfo.UseShellExecute = false; process = Process.Start(processInfo); } public void StopRecording() { if (process != null && !process.HasExited) { process.Kill(); process = null; } } } ``` 在上述代码,StartRecording() 方法启动了一个新的进程,调用 ffmpeg 的命令行参数进行视频录制,StopRecording() 方法则停止了该进程,结束录制。其,参数 "-f dshow -i video=\"screen-capture-recorder\" -r 60" 表示使用 DirectShow API 捕获屏幕视频,并以 60 帧的速度进行录制。 请注意,上述代码仅适用于 Windows 系统下的 ffmpeg 可执行文件,如果使用其他系统或版本的 ffmpeg,需要相应地修改命令行参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值