Share via


回應事件

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。

本文說明如何回應篩選圖表中發生的事件。

事件通知的運作方式

當 DirectShow 應用程式正在執行時,事件可能會發生在篩選圖形內。 例如,篩選可能會發生串流錯誤。 傳送事件來篩選篩選 Graph 管理員,其中包含事件程式碼和兩個事件參數。 事件程式碼會指出事件的類型,而事件參數會提供其他資訊。 參數的意義取決於事件程式碼。 如需事件代碼的完整清單,請參閱 事件通知碼

篩選圖形管理員會以無訊息方式處理某些事件,而不會通知應用程式。 其他事件會放在應用程式的佇列上。 視應用程式而定,您可能需要處理各種事件。 本文著重于非常常見的三個事件:

  • EC_COMPLETE事件表示播放已正常完成。
  • EC_USERABORT事件表示使用者已中斷播放。 如果使用者關閉視訊視窗,影片轉譯器就會傳送此事件。
  • EC_ERRORABORT事件表示錯誤導致播放停止。

使用事件通知

應用程式可以指示 Filter Graph 管理員在發生新事件時,將 Windows 訊息傳送至指定的視窗。 這可讓應用程式在視窗的訊息迴圈內回應。 首先,定義將傳送至應用程式視窗的訊息。 應用程式可以使用範圍中的訊息號碼,從WM_APP到0xBFFF作為私人訊息:

#define WM_GRAPHNOTIFY  WM_APP + 1

接下來,查詢 IMediaEventEx 介面的 Filter Graph 管理員,並呼叫 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中的第三個參數。 此參數可讓您使用訊息傳送實例資料。 視窗訊息的 wParam 參數一律為零。

在應用程式的 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方法會擷取事件程式碼和兩個事件參數。 第四個 GetEvent 參數會指定等候事件的時間長度,以毫秒為單位。 因為應用程式會呼叫此方法以回應WM_GRAPHNOTIFY訊息,所以事件已排入佇列。 因此,我們會將逾時值設定為零。

事件通知和訊息迴圈都是非同步,因此佇列可能會在應用程式回應訊息時保留多個事件。 此外,如果篩選圖形管理員變得無效,就可以從佇列中移除特定事件。 因此,您應該呼叫 GetEvent ,直到傳回失敗碼為止,指出佇列是空的。

在此範例中,應用程式會叫用應用程式定義的 CleanUp 函式,以回應 EC_COMPLETEEC_USERABORTEC_ERRORABORT ,這會導致應用程式正常結束。 此範例會忽略兩個事件參數。 擷取事件之後,請將 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訊息處理常式中,請先檢查 IMediaEventEx 指標,再呼叫 GetEvent

if (g_pEvent == NULL) return;

這可防止應用程式在釋放指標之後收到事件通知時可能發生的錯誤。

基本 DirectShow 工作

DirectShow 中的事件通知