回應事件
[與此頁面相關聯的功能,DirectShow是舊版功能。 它已被 MediaPlayer、IMFMediaEngine和媒體基金會中的 音訊/視訊擷取取代。 這些功能已針對 Windows 10 和 Windows 11 進行優化。 Microsoft強烈建議新程式代碼盡可能在媒體 基礎中使用 MediaPlayer、IMFMediaEngine 和 音訊/視訊擷取,而不是 DirectShow。 Microsoft建議使用舊版 API 的現有程式代碼,盡可能改寫成使用新的 API。]
本文說明如何響應篩選圖表中發生的事件。
事件通知的運作方式
當 DirectShow 應用程式正在執行時,事件可能會發生在篩選圖形內。 例如,過濾器可能會遇到串流錯誤。 篩選器透過傳送包含事件代碼和兩個事件參數的事件來通知篩選圖管理員。 事件程式代碼會指出事件的類型,而事件參數則提供其他資訊。 參數的意義取決於事件程序代碼。 如需事件代碼的完整清單,請參閱 事件通知碼。
篩選圖形管理員會以無訊息方式處理某些事件,而不會通知應用程式。 其他事件會放在應用程式的佇列上。 視應用程式而定,您可能需要處理各種事件。 本文著重在十分常見的三個事件:
- EC_COMPLETE 事件表示播放已正常完成。
- EC_USERABORT 事件表示使用者已中斷播放。 如果使用者關閉視訊視窗,影片轉譯器就會傳送此事件。
- EC_ERRORABORT 事件表示錯誤已造成播放停止。
使用事件通知
應用程式可以指示 Filter Graph Manager 在發生新事件時,將 Windows 訊息傳送至指定的視窗。 這可讓應用程式在視窗的訊息循環內回應。 首先,定義將傳送至應用程式視窗的訊息。 應用程式可以使用WM_APP到0xBFFF範圍中的訊息號碼作為私人訊息:
#define WM_GRAPHNOTIFY WM_APP + 1
接下來,查詢 Filter Graph Manager 中的 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 中的第三個參數。 此參數可讓您使用 訊息傳送實例數據。 視窗訊息的 wParam 參數一律為零。
在您的應用程式的 WindowProc 函式中,新增處理 WM_GRAPHNOTIFY 訊息的情況陳述:
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, ¶m1, ¶m2, 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_COMPLETE、EC_USERABORT和 EC_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 中的 事件通知