發生事件時學習
[與此頁面相關聯的功能,DirectShow是舊版功能。 它已被 MediaPlayer、IMFMediaEngine和媒體基礎中的 音訊/視訊擷取所取代。 這些功能已針對 Windows 10 和 Windows 11 進行優化。 Microsoft強烈建議新程式代碼盡可能在媒體 基礎中使用 MediaPlayer、IMFMediaEngine 和 音訊/視訊擷取,而不是 DirectShow。 Microsoft建議使用舊版 API 的現有程式代碼,盡可能改寫成使用新的 API。]
若要處理 DirectShow 事件,應用程式需要一種方式來找出佇列中正在等候的事件。 Filter Graph 管理員提供兩種方式來執行此動作:
- 視窗通知: 每當有新事件時,Filter Graph 管理員就會將使用者定義的 Windows 訊息傳送至應用程式視窗。
- 事件訊號: Filter Graph Manager 會在佇列中有 DirectShow 事件時發出 Windows 事件的訊號,如果佇列是空的,則重設事件。
應用程式可以使用任一技術。 視窗通知通常更簡單。
視窗通知
若要設定視窗通知,請呼叫 IMediaEventEx::SetNotifyWindow 方法並指定私人訊息。 應用程式可以在WM_APP到0xBFFF的範圍內使用訊息號碼作為私人訊息。 每當 Filter Graph Manager 將新的事件通知放在佇列中時,就會將此訊息張貼至指定的視窗。 應用程式會從視窗的訊息循環內回應訊息。
下列程式代碼範例示範如何設定通知視窗。
#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 指標之前,請呼叫 SetNotifyWindow,並使用 NULL 指標來取消事件通知。 在事件處理程式碼中,請先檢查您的 IMediaEventEx 指標是否有效,再呼叫 getEvent。 這些步驟可防止發生一個可能的錯誤,即應用程式在釋放 IMediaEventEx 指標後收到事件通知。
事件訊號
Filter Graph Manager 會保留手動重設事件,以反映事件佇列的狀態。 如果佇列包含擱置的事件通知,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, ¶m1, ¶m2, 0))
{
printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);
pEvent->FreeEventParams(evCode, param1, param2);
bDone = (EC_COMPLETE == evCode);
}
}
}
因為篩選圖表會在適當時自動設定或重設事件,所以您的應用程式不應該這麼做。 此外,當您釋放篩選圖形時,篩選圖表會關閉事件句柄,因此請勿在該點之後使用事件句柄。