DirectShowで悩んだメモ

マルチモニタでの問題を把握する

DirectShowを使って、ウィンドウにムービーデータを描画しているプログラムを書いていたのだが、デュアルディスプレイ(マルチモニタ)対応でハマっている。
現在は参考書のサンプルコードをほぼそのままに使用して、RenderFile 関数で描画まで行っている。なので内部のGraphがどのような状態になっているかはわからない。
その状態で実行するとそれなりに期待通りの動作をしてくれて問題なかったのだが、マルチモニタ環境では問題が発生していた。プライマリモニタ以外では映像が描画されないのだ。モニタをまたぐようにウィンドウを配置すると、見事にプライマリモニタのみで描画され、セカンダリモニタでは真っ黒になる。
また、GraphEditで直接メディアファイルを開いてみても全く同じ動作になることがわかった。

GraphEditで解決方法を模索

何とかならないものかとGraphEditのフィルター一覧を眺めていると、"Video Renderer"という名前のフィルタが二つあることがわかり、ためしにもうひとつのほうにつなぎ変えてみたらなんとうまくいくことが判明したのです。
↓マルチモニタでダメなフィルタ。

↓マルチモニタでOKなフィルタ。

おそらくこれと同じことをコードで実現すれば解決するはずです。

しかし、フィルタの作り方がわからない。

とりあえず以下のコードで現在つながっているフィルタを外すことは出来そうだ。

IBaseFilter* pDecoder = NULL;
hr = mpGraph->FindFilterByName( _T("WMVideo Decoder DMO"), &pDecoder );

IBaseFilter* pRenderer = NULL;
hr = mpGraph->FindFilterByName( _T("Video Renderer"), &pRenderer );

おそらくこの処理のあとに、pRendererを削除(IFilterGraph::RemoveFilter)して、マルチモニタ対応のフィルタを作成(CoCreateInstance)し、pDecoderからつなげば(IFilterGraph::ConnectDirect)いいと思うのだ。
しかし、マルチモニタ対応のレンダラーフィルタの作り方がわからない。
MSDN ビデオ レンダラ フィルタ
を参考にする限り、フィルタ CLSID は、CLSID_VideoRenderer でよさそうだが、要はこれで表現されるのがどちらか、ということだ。。
MSDN IFilterGraph インターフェイス
↓下記のページを参考にして任意の IPin を取得する関数も実装しておく必要がある。
DirectShow:ピンの列挙
と、VisualStudioで の宣言を調べてみたら以下のようなコード(uuids.h)に行き着いた。

// 70e102b0-5556-11ce-97c0-00aa0055595a           Video renderer
OUR_GUID_ENTRY(CLSID_VideoRenderer,
0x70e102b0, 0x5556, 0x11ce, 0x97, 0xc0, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a)

これは、似ている数値の並びなので、なんとなくOKな方のフィルタに近いように思える。ということはこれを作って、つなげなおせばOKかな?

翌日の経過

今のところダメです。
RemoveFilterでフィルタを削除し、CoCreateInstanceでRendererフィルタを作成し、DecoderのOUTPUTピンを取得、RendererのINPUTピンを取得し、それらをConnectDirectしようとしたら、そこでエラーが出る。

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

うーん、どうしたものか。
とりあえず急ぐ仕事なので、これはあまりかまってられないので、後日の作業リストに加えておこう。(誰かDirectShowとか分かる方おられましたら教えてくださいませ。)