次の方法で共有


イベントへの応答

ここでは、フィルタ グラフで発生するイベントに応答する方法を解説する。

イベント通知の動作

DirectShow アプリケーションの実行中にフィルタ グラフでイベントが発生することがある。たとえば、フィルタにはストリーミング エラーが発生することがある。フィルタはイベントを送信してフィルタ グラフ マネージャにエラーを知らせる。イベントはイベント コードと 2 つのイベント パラメータから構成される。イベント コードはイベントのタイプを示す。イベント パラメータは追加情報を示す。パラメータの意味は、イベント コードによって異なる。すべてのイベント コードの一覧については、「イベント通知コード」を参照すること。

一部のイベントについては、フィルタ グラフ マネージャがアプリケーションに通知せずに暗黙のうちに処理する。他のイベントはアプリケーションのキューに入る。処理が必要なイベントはアプリケーションによって異なる。ここでは、一般的な 3 つのイベントを取り上げる。

  • EC_COMPLETE イベントは、再生が正常に完了したことを知らせる。
  • EC_USERABORT イベントは、ユーザーが再生を中断したことを知らせる。ユーザーがビデオ ウィンドウを閉じると、ビデオ レンダラがこのイベントを送信する。
  • EC_ERRORABORT イベントは、エラーのため再生が停止したことを知らせる。

イベント通知の使い方

新しいイベントが発生するごとに、フィルタ グラフ マネージャが特定のウィンドウに Windows メッセージを送信するよう、アプリケーションで指定できる。したがって、アプリケーションはウィンドウのメッセージ ループ内で応答できる。まず、アプリケーション ウィンドウに送信されるメッセージを定義する。アプリケーションでは、WM_APP から 0xBFFF までの範囲のメッセージ番号をプライベート メッセージとして使える。

#define WM_GRAPHNOTIFY  WM_APP + 1

次に、フィルタ グラフ マネージャに IMediaEventEx インターフェイスを問い合わせ、IMediaEventEx::SetNotifyWindow メソッドを呼び出す。

IMediaEventEx *g_pEvent = NULL;
g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent);
g_pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);

このメソッドは、指定されたウィンドウ (g_hwnd) をメッセージの受信者として指定する。フィルタ グラフの作成後、グラフを実行する前にメソッドを呼び出す。

WM_GRAPHNOTIFY は通常の Windows メッセージである。フィルタ グラフ マネージャは、新しいイベントをイベント キューに入れると、指定されたアプリケーション ウィンドウに WM_GRAPHNOTIFY メッセージを送信する。メッセージの lParam パラメータは SetNotifyWindow の 3 番目のパラメータに等しい。このパラメータを使えば、メッセージと共にインスタンス データを送信できる。ウィンドウ メッセージの wParam パラメータは常に 0 である。

アプリケーションの WindowProc 関数に、WM_GRAPHNOTIFY メッセージ用の case ステートメントを追加する。

case WM_GRAPHNOTIFY:
    HandleGraphEvent();
    break;

イベント ハンドラ関数の中で IMediaEvent::GetEvent メソッドを呼び出して、キューからイベントを取り出す。

void HandleGraphEvent()
{
    // IMediaEventEx ポインタがない場合は、無視する。
    if (g_pEvent == NULL)
    {
        return;
    }
    // すべてのイベントを取得する。
    long evCode, param1, param2;
    HRESULT hr;
    while (SUCCEEDED(g_pEvent->GetEvent(&evCode, &param1, &param2, 0)))
    {
        g_pEvent->FreeEventParams(evCode, param1, param2);
        switch (evCode)
        {
        case EC_COMPLETE:  // 失敗に終わる。
        case EC_USERABORT: // 失敗に終わる。
        case EC_ERRORABORT:
            CleanUp();
            PostQuitMessage(0);
            return;
        }
    } 
}

GetEvent メソッドは、イベント コードと 2 つのイベント パラメータを取り出す。GetEvent の 4 番目のパラメータはイベント発生までの待機時間をミリ秒単位で指定する。アプリケーションでは WM_GRAPHNOTIFY メッセージに応答してこのメソッドを呼び出すため、イベントは既にキューに入っている。したがって、タイムアウト値は 0 に設定する。

イベント通知とメッセージ ループはどちらも非同期で行われるため、アプリケーションがメッセージに応答するときまでに、キューには複数のイベントが入っている可能性がある。また、フィルタ グラフ マネージャは、イベントが無効になった場合にキューからイベントを削除できる。そのため、キューが空であることを示すエラー コードが返されるまで GetEvent を呼び出すこと。

この例で、アプリケーションは EC_COMPLETE、EC_USERABORT、および EC_ERRORABORT に応答してアプリケーション定義の CleanUp 関数を呼び出す。この関数によりアプリケーションは正常に終了する。例では、2 つのイベント パラメータを無視している。イベントを取り出したら、イベント パラメータに関連する空のリソースに IMediaEvent::FreeEventParams を呼び出す。

EC_COMPLETE イベントが発生してもフィルタ グラフは停止しない点に注意すること。アプリケーションはグラフを停止するかポーズすることができる。グラフを停止すると、フィルタは保持しているリソースを解放する。グラフをポーズすると、フィルタはリソースを保持し続ける。なお、ビデオ レンダラはポーズしたとき、最新のフレームの静止画像を表示する。

IMediaEventEx ポインタを解放する前に、NULL ポインタを使って SetNotifyWindow を呼び出し、イベント通知を取り消す。

// グラフを解放する前にイベント通知を無効にする。
g_pEvent->SetNotifyWindow(NULL, 0, 0);
g_pEvent->Release();
g_pEvent = NULL;

WM_GRAPHNOTIFY メッセージ ハンドラで、GetEvent を呼び出す前に IMediaEventEx ポインタをチェックする。

if (g_pEvent == NULL) return;

このチェックにより、アプリケーションがポインタ解放後にイベント通知を受け取る場合に発生するエラーを回避できる。

参照