Microsoft DirectX 8.0

DirectShow でのイベント通知

ここでは、Microsoft® DirectShow® のフィルタ グラフでどのようにイベントが発生するか、およびアプリケーションがどのようにイベント通知を取得し、それに応答するかについて説明する。内容は次のとおりである。

イベント通知の概要

フィルタは、イベント通知を送信することによって、フィルタ グラフ マネージャにイベントを通知する。イベントは、ストリームの終わりのように予測できる場合もあれば、ストリームのレンダリング失敗のようなエラーの場合もある。フィルタ グラフ マネージャ自身が処理するフィルタ イベントもあれば、アプリケーションが処理するフィルタ イベントもある。フィルタ グラフ マネージャは、フィルタ イベントを処理しない場合、イベント通知をキューに入れる。フィルタ グラフはアプリケーションに対する独自のイベント通知をキューに入れることもできる。

アプリケーションはキューからイベントを取得し、イベントのタイプに基づいて応答する。そのため、DirectShow でのイベント通知は Microsoft® Windows® のメッセージ キューイングの仕組みに似ている。アプリケーションは、特定のイベント タイプに対するフィルタ グラフ マネージャのデフォルト動作を取り消すこともできる。この場合、フィルタ グラフ マネージャはイベントを直接キューに入れ、アプリケーションにイベントを処理させることができる。

このメカニズムによって次のことが可能になる。

イベントの取得

フィルタ グラフ マネージャはイベント通知をサポートする 3 つのインターフェイスを公開している。

フィルタは、フィルタ グラフ マネージャに対する IMediaEventSink::Notify メソッドを呼び出すことによって、イベント通知を送信する。イベント通知は、イベントのタイプを定義するイベント コードと、付加情報を提供する 2 つの DWORD パラメータで構成される。イベント コードに応じて、パラメータにはポインタ、戻りコード、基準タイム、またはその他の情報が含まれる。すべてのイベント コードとパラメータの一覧については、「イベント通知コード」を参照すること。

キューからイベントを取得するために、アプリケーションはフィルタ グラフ マネージャに対して IMediaEvent::GetEvent メソッドを呼び出す。このメソッドは、返すイベントが発生するまで、または指定された時間が経過するまでブロックする。キュー内にイベントがあり、メソッドがイベント コードと 2 つのイベント パラメータを返したとする。アプリケーションは、GetEvent を呼び出した後必ず IMediaEvent::FreeEventParams メソッドを呼び出し、イベント パラメータに関連付けられたリソースを解放する必要がある。たとえば、パラメータは、フィルタ グラフによって割り当てられた BSTR 値である場合がある。

次のコード例は、キューからイベントを取得する方法の概要を示している。

long evCode, param1, param2;
HRESULT hr;
while (hr = pEvent->GetEvent(&evCode, &param1, &param2, 0), SUCCEEDED(hr))
{
    switch(evCode) 
    { 
        // 処理する各タイプのイベントに対して
        // アプリケーションで定義した関数を呼び出す。
    } 

hr = pEvent->FreeEventParams(evCode, param1, param2);
}

イベントに対するフィルタ グラフ マネージャのデフォルト処理をオーバーライドするには、パラメータにイベント コードを指定した IMediaEvent::CancelDefaultHandling メソッドを呼び出す。デフォルト処理は、IMediaEvent::RestoreDefaultHandling メソッドを呼び出すことによって復元できる。フィルタ グラフが指定されたイベント コードに対してデフォルト処理を実行しない場合は、これらのメソッドを呼び出しても効果はない。

イベント発生時の確認

前の「イベントの取得」で説明したようにキューからイベント通知を取得することに加えて、アプリケーションにはイベントがキューで待機している場合にそれを検出する方法が必要である。フィルタ グラフ マネージャはそのために 2 つの方法を提供している。

以下の項目で各テクニックについて説明する。

ウィンドウ通知

ウィンドウ通知をセットアップするには、IMediaEventEx::SetNotifyWindow メソッドを呼び出し、プライベート メッセージを指定する。アプリケーションでは、WM_APP から 0xBFFF までの範囲のメッセージ番号をプライベート メッセージとして使用できる。フィルタ グラフ マネージャは、新しいイベント通知をキューに入れると、指定されたウィンドウにこのメッセージを送信する。アプリケーションはウィンドウのメッセージ ループ内からメッセージに応答する。

次のコード例は、通知ウィンドウの設定方法を示している。

#define WM_GRAPHNOTIFY WM_APP + 1   // プライベート メッセージ。

pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);

メッセージは通常の Windows メッセージであり、DirectShow のイベント通知キューからは独立して送信される。この方法の利点は、ほとんどのアプリケーションが既にメッセージ ループを実装しているため、多くの追加作業を必要とせずに DirectShow のイベント処理を組み込める点である。

次のコード例は、通知メッセージに応答する方法の概要を示している。完全な例については、「イベントへの応答」を参照すること。

LRESULT CALLBACK WindowProc( HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
    switch (msg)
    {
        case WM_GRAPHNOTIFY:
            HandleEvent();  // アプリケーション定義の関数。
            break;

        // ここでほかの Windows メッセージも処理する。
    }
    return (DefWindowProc(hwnd, msg, wParam, lParam));
}

イベント通知とメッセージ ループは両方とも非同期であるため、アプリケーションがメッセージに応答するときまでに、複数のイベントがキューに入っていることがある。また、イベントが無効になったときはキューからイベントがクリアされることがある。したがって、イベント処理コードでは、キューが空であることを示すエラー コードが返されるまで GetEvent を呼び出すこと。

イベント ハンドル

フィルタ グラフは、イベント キューの状態を示す手動リセット イベントを維持する。キューにペンディング状態のイベント通知が含まれている場合、フィルタ グラフは手動リセット イベントを送信する。キューが空の場合は、IMediaEvent::GetEvent メソッドを呼び出すとイベントがリセットされる。アプリケーションはこのイベントを使用してキューの状態を確認できる。

注 :  ここで用語について混乱しないこと。手動リセット イベントは、Windows の CreateEvent 関数によって作成されるタイプのイベントであり、DirectShow で定義されているイベント通知とは無関係である。

IMediaEvent::GetEventHandle メソッドは、手動リセット イベントのハンドルを取得する。WaitForMultipleObjects などの関数を呼び出して、イベントが通知済になるまで待機すること。イベントが通知済になったら IMediaEvent::GetEvent メソッドを呼び出してイベント通知を取得する。

次のコード例は、この処理方法を示している。イベント ハンドルを取得し、イベントが通知済になるのを 100 ミリ秒間隔で待機する。イベントが通知済になると、GetEvent を呼び出してイベント コードとイベント パラメータをコンソール ウィンドウに出力する。再生が完了したことを示す EC_COMPLETE イベントが発生すると、ループが終了する。

HANDLE  hEvent; 
long evCode, param1, param2;
BOOLEAN bDone = FALSE;
HRESULT hr = S_OK;

hr = pEvent->GetEventHandle((OAEVENT*)&hEv);

while(!bDone) 
{
    if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 100))
    { 
 while (hr = pEvent->GetEvent(&evCode, &param1, &param2, 0), SUCCEEDED(hr))
        {
            printf("Event code: %#04x\n    Params: %d, %d\n", evCode, param1, param2);
hr = pEvent->FreeEventParams(evCode, param1, param2);
            bDone = (EC_COMPLETE == evCode);
        }
    }
} 

フィルタ グラフが適切なときにイベントを自動的に設定またはリセットするので、アプリケーションでイベントの設定またはリセットを行うべきではない。さらに、フィルタ グラフを解放するとフィルタ グラフはイベント ハンドルを閉じる。そのため、その後はイベント ハンドルを使用しないこと。