DirectShowを使う(1)

アクセス解析を見ていると、ときどきVMR9の項目を見に来ている人がいるのがわかるのですが・・・。何が目的なのかちょっと知りたいです。

ま、その話題までがんばって持って行きたいと思います。

とりあえず、DirectShowを使って再生、というのをやってみたいと思います。この辺はサンプル(SDKのサンプルだとPlayWndとか)を見た方が早いのですが、

実際に再生の手順をやってみましょう。

先に説明しておきますが、hrはHRESULT型の変数で、代入しているところはエラー処理を省いていますので必要に応じてエラーチェックをしてください。

また、コード開始時は以下の仮定があるものとします。

  • COMのライブラリは初期化済み(CoInitializeExを呼び出している)
  • ウィンドウハンドルはhWndで記述され、メインウィンドウのハンドルを持っている
  • 内部文字列はすべてUNICODEで扱われている(ASCIIで扱っているときはUNICODEへの変換を行うこと)

というわけで、コードです。

IGraphBuilder *m_lpGraphBuilder;
IMediaControl *m_lpMediaControl;
IMediaEventEx *m_lpMediaEventEx;
IMediaSeeking *m_lpMediaSeeking;
IVideoWindow  *m_lpVideoWindow;
IBasicAudio *m_lpBasicAudio;
IBasicVideo *m_lpBasicVideo;
const WCHAR *lpwFileName;
//lpwFileNameには有効なファイル名が代入されているものとする
//IGraphBuliderを生成する
hr = CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void **)&m_lpGraphBuilder);
//ファイル名を指定してグラフを生成する
hr = m_lpGraphBuilder->RenderFile(lpwFileName,NULL);
//コントロールに必要なインターフェイスを取得する
hr = m_lpGraphBuilder->QueryInterface(IID_IMediaControl,(void **)&m_lpMediaControl);
hr = m_lpGraphBuilder->QueryInterface(IID_IMediaEventEx,(void **)&m_lpMediaEventEx);
hr = m_lpGraphBuilder->QueryInterface(IID_IMediaSeeking,(void **)&m_lpMediaSeeking);
hr = m_lpGraphBuilder->QueryInterface(IID_IBasicVideo,(void **)&m_lpBasicVideo);
hr = m_lpGraphBuilder->QueryInterface(IID_IBasicAudio,(void **)&m_lpBasicAudio);
hr = m_lpGraphBuilder->QueryInterface(IID_IVideoWindow,(void **)&m_lpVideoWindow);
//ビデオのウィンドウをメインウィンドウに設定する
hr = m_lpVideoWindow->put_Owner((OAHWND)hWnd);
hr = m_lpVideoWindow->put_MessageDrain((OAHWND)hWnd);
hr = m_lpVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
//再生位置の変更を行うタイムフォーマットをメディアタイム(100nsを1とする単位系)にする
hr = m_lpMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
//メディアを再生する
hr = m_lpMediaControl->Run();
//ここでいろいろな動作をする
...
//再生を停止する
hr = m_lpMediaControl->Stop();
//ビデオウィンドウを解放する
m_lpVideoWindow->put_Visible(OAFALSE);
m_lpVideoWindow->put_MessageDrain(NULL);
m_lpVideoWindow->put_Owner(NULL);
//各オブジェクトを解放する
m_lpVideoWindow->Release();
m_lpBasicVideo->Release();
m_lpBasicAudio->Release();
m_lpMediaSeeking->Release();
m_lpMediaEventEx->Release();
m_lpMediaControl->Release();
m_lpGraphBuilder->Release();

そのまんまです。コメントも何もありません。ちょっとわかりづらいのがQueryInterfaceの部分で、「なぜこのインターフェイスが取得できるのか?」という疑問がつきまとうわけですが、

これはこうなるので覚えてくれ、としかいいようがありません。特にRenderFileを行った後でIGraphBuilderから取得できるインターフェイスにはいろいろな種類があるので

この場では説明できません。というかありすぎて私もよく使うものしか覚えていません。なんてこった。

それぞれのインターフェイスの説明はMSDNあたりで調べてみてください。大まかには以下のような感じです。

IGraphBuilder グラフ(情報工学上のグラフのこと。この場合は各処理のつながり)を管理するオブジェクト。DirectShowでは親となるインターフェイス
IMediaControl グラフの動作状態を管理するオブジェクト。再生や停止の指示はここで行う
IMediaEventEx グラフから与えられるイベント(再生が完了したといった情報)を管理する。ウィンドウプロシージャに通知することも
IMediaSeeking メディアの再生位置を操作するオブジェクト。シーク処理やメディアの時間管理がメイン
IBasicAudio メディアがオーディオを持つときに使用できる音量管理オブジェクト。オーディオラインを持たないときは取得できないこともある
IBasicVideo メディアがビデオを持つときのそのビデオの情報を管理するオブジェクト。ビデオラインを持たないときは取得できないこともある
IVideoWindow メディアがビデオウィンドウ(ビデオ表示窓)を持つときに使用できるウィンドウを管理するオブジェクト。自分で別にレンダリングするとき(VMR9など)には使用されない

ちなみに、解放するときは初期化したものから逆順に解放するのが作法です。完全に正確に逆順でなくてもいいですが、IGraphBuilderは必ず最後に解放してください。

でないと状態の管理がよくわからない状態となりますので。

あと、COMの概念がよく出ているコードだと思います。つまり、

  • CoCreateInstanceによって親となるCOMオブジェクトを生成している
  • QueryInterfaceによるインターフェイス要求を行うことで必要な機能を使用できるようにする
  • 戻り値はほとんどの処理でHRESULTとなる

というものです。以降フィルタを作成するときにもよく出てくると思いますので覚えておきましょう。

簡単に説明してみました。省きすぎて文章として成立していないような・・・。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

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