回應事件
[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayer、 IMFMediaEngine和 Media Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayer、 IMFMediaEngine 和 音訊/視訊擷取 ,而不是 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, ¶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;
這可防止應用程式在釋放指標之後收到事件通知時可能發生的錯誤。
相關主題