Media Foundation ④ WebCam + WPF 同期か非同期か
Windows 7 SDk (RC) にある Media Foundation のサンプル MFCaptureD3D を使って WPF で WebCam のビデオを表示してみましょう。D3DSurface → D3DImage → WPF という流れは、以前 D3DImage チュートリアルで紹介したとおりですが、WebCam の画像をWPFで表示するときには、同期サンプリングか非同期サンプリングかのどちらの戦略をとるかを選ばなければなりません。
非同期サンプリング
MFCaptureD3D では元々サンプリングを非同期で行っています。アトリビュート ストアでMF_SOURCE_READER_ASYNC_CALLBACKを指定し、非同期コールバックを設定しています。
// ③非同期コールバックを設定
hr = pAttributes->SetUnknown(
MF_SOURCE_READER_ASYNC_CALLBACK,
this
);
サンプリングを待つ時間を他のUIスレッドで使えるので、WPFのようなアプリには非同期が理想的です。
このサンプルコードでは 非同期コールバックの場所として this が指定されているので、サンプリングが完了すると自クラスの OnReadSample メソッドが呼び出されます。このメソッドで、サンプリングした画像をD3DSurfaceに書き込んでから、C++ から C# に何らかのメッセージを送ってD3DSurfaceにビデオ画像が書き込まれたことを通知する仕組みが必要です。
同期サンプリング
以前の D3DImage チュートリアルでは、C# 側から Render メソッドで C++ の Direct3D レンダリングを実行し、それが戻ったら、ロックして AddDirtyRect メソッドでブリットしていました。同期サンプリングの戦略であれば、WebCam から画像を取得し D3DSrufaceへの書き込みを待ってブリットするという、同じ手順でレンダリングできます。
しかし、WebCam デバイスのサンプリングには待ち時間が発生します。しかも1秒間に30フレーム程度確保しようとすると、待ち時間は全体のかなりの割合を占める可能性があります。その間 UI を操作できず、パフォーマンスが悪化する可能性があります。
同期サンプリングを実装するには、アトリビュートストアに前述の非同期指定(MF_SOURCE_READER_ASYNC_CALLBACK)を入れずに、ソースリーダーを取得して、次のようにReadSampleを呼び出すだけです。同期サンプリングでは ReadSample が帰ったときにサンプル画像が取得できます(実装では取得できるまでサンプリングを繰り返します)。
DWORD dwStream;
DWORD dwFlags;
IMFSample* pSample = NULL;
LONGLONG llTimeStamp;
while( !pSample )
{
hr = m_pReader->ReadSample(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
&dwStream,
&dwFlags,
&llTimeStamp,
&pSample
);
}
結論として、今回のサンプルコードでは、非同期サンプリングの戦略で実装することにしました。両方とも実装してテストしてみましたが、やはり同期サンプリングでは UI 操作の引っ掛かりが発生することと、C++ コードの修正が多くなるためです。