イベントが発生した場合の学習

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/Video Capture を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存のコードを、可能であれば新しい API を使用するように書き換えるよう提案しています。]

DirectShow イベントを処理するには、アプリケーションでイベントがキュー内で待機しているタイミングを確認する方法が必要です。 フィルター グラフ マネージャーには、次の 2 つの方法があります。

  • ウィンドウ通知: フィルター グラフ マネージャーは、新しいイベントが発生するたびに、ユーザー定義の Windows メッセージをアプリケーション ウィンドウに送信します。
  • イベント シグナリング: フィルター グラフ マネージャーは、キューに DirectShow イベントがある場合は Windows イベントを通知し、キューが空の場合はイベントをリセットします。

アプリケーションでは、どちらの手法も使用できます。 通常、ウィンドウ通知の方が簡単です。

ウィンドウ通知

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

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

#define WM_GRAPHNOTIFY WM_APP + 1   // Private message.
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();  // Application-defined function.
            break;
        // Handle other Windows messages here too.
    }
    return (DefWindowProc(hwnd, msg, wParam, lParam));
}

イベント通知とメッセージ ループはどちらも非同期であるため、アプリケーションがメッセージに応答する時点までにキューに複数のイベントが含まれる場合があります。 また、イベントが無効になった場合は、キューからクリアされることがあります。 そのため、イベント処理コードでは、キューが空であることを示すエラー コードが返されるまで IAMMediaEvent::GetEvent を呼び出します。

IMediaEventEx ポインターを解放する前に、NULL ポインターで SetNotifyWindow を呼び出してイベント通知を取り消します。 イベント処理コードで、GetEvent を呼び出す前に IMediaEventEx ポインターが有効かどうかをチェックします。 これらの手順では、 IMediaEventEx ポインターを解放した後にアプリケーションがイベント通知を受け取る可能性のあるエラーを防ぎます。

イベント シグナリング

Filter Graph Manager は、イベント キューの状態を反映する手動リセット イベントを保持します。 キューに保留中のイベント通知が含まれている場合、フィルター グラフ マネージャーは手動リセット イベントを通知します。 キューが空の場合、 IMediaEvent::GetEvent メソッドを呼び出すとイベントがリセットされます。 アプリケーションは、このイベントを使用してキューの状態を判断できます。

注意

この用語は、ここでは混乱を招く可能性があります。 手動リセット イベントは、Windows CreateEvent 関数によって作成されたイベントの種類です。DirectShow によって定義されたイベントとは関係ありません。

 

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

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

HANDLE  hEvent; 
long    evCode, param1, param2;
BOOLEAN bDone = FALSE;
HRESULT hr = S_OK;
hr = pEvent->GetEventHandle((OAEVENT*)&hEvent);
if (FAILED(hr))
{
    /* Insert failure-handling code here. */
}

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

フィルター グラフでは、必要に応じてイベントが自動的に設定またはリセットされるため、アプリケーションでは設定しないでください。 また、フィルター グラフを解放すると、フィルター グラフはイベント ハンドルを閉じるので、その後にイベント ハンドルを使用しないでください。