VMR9 On DirectShow (1)

題名が面倒になってきたのでこの書き方で終わらせます。

というわけでやっとの事で入れるVMR9を使ってみるおよび実装してみます。

VMR9は「Video Mixing Renderer9」の略語ですね。最後の9はDirectX9と密接に結びついている、という意味を表しています。

なので、途中でいきなりIDirect3DDevice9とかDirect3D9に関わるオブジェクトが登場することになります。しなくてもいい場合もありますが。

それらはすでに作成済みということで了承しておいて下さい。

あと、今回の説明ではVMR9をRenderlessとして扱います。このモードはビデオのデコードは行うがウィンドウへの描画を実装者が正しく描画するように、というモードです。

Windowed(ウィンドウモード)やWindowless(ウィンドウレスモード)ではゲームとして使う分には汎用性に欠けるといわざるを得ない部分が大きいのでこちらで説明します。

この両者を使うパターンの場合はサンプルをそのままコピペした方が早いです。といってもRenderlessのこのパターンでもVMR9Allocatorというサンプルパターンをちょっと変形して使用するだけです。

そちらを見てわかる型はそちらを見ちゃった方が圧倒的に早いです。

以下は必要な部分の実装の前に必要となる動作コード部分です。

1.DirectShowの使用例にしたがってグラフを作って再生

この部分は過去の記事にもありますので面倒なので伏せます。

後の作業は

... //ここでVMR9の初期化処理を行う
hr = m_lpGraphBuilder->RenderFile(lpwFileName,NULL)

のコメント行を置き換える形で行います。

2.フィルタをCOMとして作成する

VMR9のフィルタはCOMとしてシステムに登録されている(ことになっている)ので、作成にはCoCreateInstanceの力を借ります。

IBaseFilter *m_lpVMRFilter = NULL;
hr = CoCreateInstance(CLSID_VideoMixingRenderer9,0,CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**)&m_lpVMRFilter);

ちょっと今までで見たことがない形になっているのは、作成しているのはVMR9のフィルタのはずなのに受け取っている変数はIBaseFilterのポインタになっています。

これは、IVMRFilterみたいな型を標準で用意していない(というより必要ない)からで、この場合はただのベースフィルタとして扱ってよい、という意味になります。

3.VMR9をRenderlessへと変更

描画モードをレンダーレスモードへと移行させます。ついでにミキシングも停止して単一のストリームのみを受け付けるように指示します。

モードの変更はフィルタからIVMRFilterConfig9を受け取ってそれを使います。変更したら役目が終わるのでさっさと解放します。

IVMRFilterConfig9 *lpFilterConfig;
hr = m_lpVMRFilter->QueryInterface(IID_IVMRFilterConfig9,(void **)&lpFilterConfig);
lpFilterConfig->SetNumberOfStreams(1);
lpFilterConfig->SetRenderingMode(VMR9Mode_Renderless);
lpFilterConfig->Release();

なお、ヘルプにもありますがSetRenderingModeを呼び出せるのは1回だけでそれ以降は受け入れを拒否されますので気をつけてください。

4.VMR9にサーフェイスアロケータを設定する

この場合のサーフェイスアロケータとは以下のようなオブジェクトです。

VMR9はレンダーレスモードとなっているため、画面へのレンダリングを行わない。そのかわり、実装者からビデオのレンダリング対象となるサーフェイスを提供してもらい、

その部分にレンダリングを行い、その作業を持って1フレームの描画を完了したことにする。このとき、レンダリング対象となるサーフェイスを提供させるために

実装者が実装するオブジェクトがサーフェイスアロケータである。

これの実装は次回以降行います。これがまた大変です・・・。とりあえず、サーフェイスアロケータを呼び出してそれを設定することで処理を行います。

class CVMRSurfaceAllocator : public CUnknown, public IVMRSurfaceAllocator9, public IVMRImagePresenter9
{
	//実装は次回以降に示す
};
IDirect3DDevice9 *lpDirect3DDevice;
CVMRSurfaceAllocator *m_lpSurfaceAllocator;
IVMRSurfaceAllocator9 *lpSurfaceAllocator;
IVMRSurfaceAllocatorNotify9 *lpSurfaceAllocNotify;
m_lpSurfaceAllocator = new CVMRSurfaceAllocator(lpDirect3DDevice,&hr);
hr = m_lpVMRFilter->QueryInterface(IID_IVMRSurfaceAllocatorNotify9,(void **)&lpSurfaceAllocNotify);
hr = m_lpSurfaceAllocator->QueryInterface(IID_IVMRSurfaceAllocator9,(void **)&lpSurfaceAllocator);
hr = lpSurfaceAllocNotify->AdviseSurfaceAllocator(NULL,lpSurfaceAllocator);
lpSurfaceAllocator->AdviseNotify(lpSurfaceAllocNotify);
lpSurfaceAllocator->Release(); lpSurfaceAllocNotify->Release();

自前で実装したサーフェイスアロケータにDirect3DDeviceとSurfaceAllocatorNotify(サーフェイスの確保が必要なときの通知インターフェイス)を設定することで

以降必要なタイミングでIVMRSurfaceAllocator9とIVMRImagePresenter9の機能が呼び出されて描画処理が行われます。

ちなみに見てわかると思いますが、IVMRSurfaceAllocator9のAdviceNotifyは自前で実装するのですが、呼び出すのも実は自分です。システムが呼び出すわけではないです。

5.グラフにVMR9を登録する

準備が完了したらグラフにフィルタを登録して使ってもらえるようにしましょう。

m_lpGraphBuilder->AddFilter(m_lpVMRFilter,L"Video Mixing Renderer9");

ちなみに、第二引数の文字列はデバッグ用の文字列で実際には何でもいいです。が、GraphEditやGraphStudioなどのDirectShowのフィルタのつながりを直接見るツールを使うと

この名前がそのまま表示されますので見苦しくならない程度にはちゃんとつけておいた方がいいと思います。

6.ファイルを読み込んで普通に再生

埋め込むべきコードは紹介し終えたので後は普通に再生処理を行えます。

が、もちろんそれでは描画できません。描画にはサーフェイスアロケータ内でいろいろと処理が必要になります。

ヘルプには「ファイルを読み込むときに~~~」とか難しい説明がありますが、基本的にはフィルタを登録した時点でそのフィルタを使える限りはできるだけ使うという

IGraphBuilderのインテリジェント接続を利用することになるのでビデオラインがあればこれが使われるようになります。

7.解放する前に使い終わったフィルタを解放する

使い終わったらちゃんと解放しましょう。じゃないとフィルタの参照カウントが残ってしまいます。

m_lpSurfaceAllocator->Release();
m_lpVMRFilter->Release();

一通りの流れを紹介しました。

しかし、やっぱり毎日書かないとアクセス数が保てないもんですね・・・。あまりないものではありますが・・・。


コメントを残す

メールアドレスが公開されることはありません。

*

この記事のトラックバック用URL