今年も高専プロコンが終わりました。というわけで、プロコンのプログラムから、自分が担当した部分で最も重要な存在であるD3DImageの部分を抜き出してサンプルプログラムにしようとしたのですが、結局1から書き直したも同然となってしまいました。特に、D3DImage.IsFrontBufferAvailableChangedの扱いは、プロコン前だと完全に無視していましたし、後でプロコンのプログラムへフィードバックするつもりです。D3DImage/VMR9 Renderlessのサンプルと比較用に普通のウィンドウへ出力するサンプルをここに置きます。あくまで比較用なのでさらにいいかげんです。DShowWPFはVC++ Expressでもコンパイルできるようにしているつもり(試してはいない)ですが、DShowWin32はWTLを使っています。ご容赦ください。
ちょっと今までここに載せてきたサンプルより規模が大きいので、ライセンスを明記することにしました。といっても、NYSL、利用にどんな制約を設ける意図も全くありません。
このサンプルプログラムの主役のD3DImageは、.NET Framework 3.5 SP1で追加されたクラスです。これは何かというと、Direct3D 9での描画した結果をWPFの描画システムへ取り込めるというものです。今回、DirectShowの映像をWPFへ送り込むために用いました。具体的には、DirectShowからVMR9 RenderlessモードによってDirect3D 9 サーフェースの形で映像を取得し、それをD3DImageへ送り込むという構成になっています。数ヶ月前までWPF、Direct3D 9/9Ex、VMR9すべて未体験、DirectShowもちょびっとかじっただけという状態でしたが、なんとかなりました。
通常、WPFで動画を再生するにはMediaElementやVideoDrawingを用いればよいのですが、今回はそれらで対応できず、DirectShowを使う羽目になりました。また、DirectShowの出力を描画するだけなら、HwndHostを使って子ウィンドウを作りそこへ描画させればよいのですが、それでは映像に加工ができず困るという点が問題でした。
D3DImageの基本的な使い方はSetBackBufferでバックバッファを指定後、Lock→バックバッファへ描画→AddDirtyRect→Unlockです。そして、VMR9 Renderlessの基本的な使い方は、PresentImageで描画すべきイメージがサーフェースとして渡されるので、PresentImage内でBeginScene→描画→EndScene→Presentです。この2つを組み合わせると……、頭が爆発します。
単純に考えて、PresentImage内でLock–Unlockしたいのですが、D3DImageはDispatcherによりスレッドに結びつけられているので不可能です(PresentImageは独自のスレッドで呼ばれます)。そのため、このサンプルではDispatcher.BeginInvokeを使ってメインスレッド内でLock–AddDirtyRect–Unlockしています。ただ、これだとバックバッファへの描画がLockの外側で行われることが気になります。川西さんのMedia Foundationと組み合わせる例(川西 裕幸のブログ : Media Foundation ⑥ WebCam + WPF XAMLとC#の実装)もそうやっているし、プログラムが落っこちるなどといった問題も生じていないので気にしないことにします。ここら辺、プロコンのプログラムではまた少し違った形になっています。
もう1つの難関がIsFrontBufferAvailableChangedでした。今週はこいつをどうするかに大半を費やしました。IsFrontBufferAvailableがtrueになったときには必ずSetBackBufferを呼ばなければならないのですが、その前に自分の管理しているDirect3Dオブジェクトも駄目になっていたら作り直さないといけません。そこら辺はデバイスロストした場合の話を参考にしています(ただし、IsFrontBufferAvailableChangedが呼ばれたとしてもロストしていない場合もあるので注意です)。デバイスに関連するオブジェクトを全部リリースしてResetすればいいという話も見つかるのですが、どうもうまくいきません。Windows SDKのサンプルVMR9Allocatorでも、デバイスまで破棄して作り直しているのでそうしました。この点、Direct3D 9Exは楽です、破棄せずResetExで動くんですから。
Vista/7以上ではD3DImageにおいてDirect3D 9ExのほうがD3DImageの性能がよいとMSDNライブラリに書いてある(Direct3D9 および WPF の相互運用性のパフォーマンスに関する考慮事項)のでそれに従っています。これ、さっき書いたようにデバイスロスト時の対処が楽になったのが良いと思いました。というより、逆にデバイスロストの面倒くささの一片を味わった気分です。
スポンサード リンク |