次の方法で共有


イベントへの応答

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

この記事では、フィルター グラフで発生するイベントに応答する方法について説明します。

イベント通知のしくみ

DirectShow アプリケーションの実行中は、フィルター グラフ内でイベントが発生する可能性があります。 たとえば、フィルターでストリーミング エラーが発生する可能性があります。 フィルターは、イベント コードと 2 つのイベント パラメーターで構成されるイベントを送信することで、Graph Manager をフィルター処理することでアラートを生成します。 イベント コードはイベントの種類を示し、イベント パラメーターは追加情報を提供します。 パラメーターの意味は、イベント コードによって異なります。 イベント コードの完全な一覧については、「 イベント通知コード」を参照してください。

一部のイベントは、アプリケーションに通知されずに、フィルター グラフ マネージャーによってサイレント処理されます。 その他のイベントは、アプリケーションのキューに配置されます。 アプリケーションによっては、さまざまなイベントを処理する必要があります。 この記事では、非常に一般的な 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 メッセージです。 Filter Graph Manager は、イベント キューに新しいイベントを配置するたびに、指定されたアプリケーション ウィンドウにWM_GRAPHNOTIFYメッセージを投稿します。 メッセージの lParam パラメーターは 、SetNotifyWindow の 3 番目のパラメーターと同じです。 このパラメーターを使用すると、メッセージと共にインスタンス データを送信できます。 ウィンドウ メッセージの wParam パラメーターは常に 0 です。

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

case WM_GRAPHNOTIFY:
    HandleGraphEvent();
    break;

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

void HandleGraphEvent()
{
    // Disregard if we don't have an IMediaEventEx pointer.
    if (g_pEvent == NULL)
    {
        return;
    }
    // Get all the events
    long evCode;
    LONG_PTR param1, param2;
    HRESULT hr;
    while (SUCCEEDED(g_pEvent->GetEvent(&evCode, &param1, &param2, 0)))
    {
        g_pEvent->FreeEventParams(evCode, param1, param2);
        switch (evCode)
        {
        case EC_COMPLETE:  // Fall through.
        case EC_USERABORT: // Fall through.
        case EC_ERRORABORT:
            CleanUp();
            PostQuitMessage(0);
            return;
        }
    } 
}

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

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

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

EC_COMPLETE イベントによってフィルター グラフが停止しないことに注意してください。 アプリケーションは、グラフを停止または一時停止できます。 グラフを停止すると、フィルターによって保持されているリソースが解放されます。 グラフを一時停止すると、フィルターは引き続きリソースを保持します。 また、ビデオ レンダラーが一時停止すると、最新のフレームの静的イメージが表示されます。

IMediaEventEx ポインターを解放する前に、NULL ウィンドウ ハンドルを使用して SetNotifyWindow を呼び出してイベント通知を取り消します。

// Disable event notification before releasing the graph.
g_pEvent->SetNotifyWindow(NULL, 0, 0);
g_pEvent->Release();
g_pEvent = NULL;

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

if (g_pEvent == NULL) return;

これにより、アプリケーションがポインターを解放した後にイベント通知を受け取った場合に発生する可能性のあるエラーが回避されます。

DirectShow の基本的なタスク

DirectShow でのイベント通知