事件的消息隊(duì)列機(jī)制
filter
通過(guò)發(fā)布事件通知來(lái)通知 Filter Graph 管理器有關(guān)事件。 該事件可能是預(yù)期事件(例如流的末尾)卧斟,也可能表示錯(cuò)誤囚聚,例如無(wú)法呈現(xiàn)流跨晴。 Filter Graph 管理器自行處理某些filter
事件,而其他filter
事件則留給應(yīng)用程序處理根吁。 如果 Filter Graph 管理器不處理filter
事件员淫,則會(huì)將事件通知放入隊(duì)列中。 filter
圖還可以為應(yīng)用程序?qū)⒆约旱氖录ㄖ抨?duì)击敌。
應(yīng)用程序從隊(duì)列中檢索事件介返,并根據(jù)事件類型對(duì)其進(jìn)行響應(yīng)。 因此愚争,DirectShow 中的事件通知類似于 Microsoft Windows 消息隊(duì)列方案映皆。 應(yīng)用程序還可以取消filter
關(guān)系圖管理器對(duì)給定事件類型的默認(rèn)行為挤聘。 然后轰枝,F(xiàn)ilter Graph 管理器將這些事件直接放入隊(duì)列中供應(yīng)用程序處理。
- 用于與應(yīng)用程序通信的 Filter Graph 管理器组去。
- 用于與應(yīng)用程序和
filter
關(guān)系圖管理器通信的filter
鞍陨。 - 應(yīng)用程序,以確定其在處理事件中的參與程度从隆。
Filter Graph 管理器公開(kāi)三個(gè)支持事件通知的接口诚撵。
- IMediaEventSink 包含用于發(fā)布事件的
filter
的方法。 - IMediaEvent 包含應(yīng)用程序用于檢索事件的方法键闺。
- IMediaEventEx 繼承自 并擴(kuò)展 IMediaEvent 接口寿烟。
通過(guò)在 Filter Graph 管理器上調(diào)用 IMediaEventSink::Notify 方法篩選事件通知。 事件通知由一個(gè)定義事件類型的事件代碼和兩個(gè)提供其他信息的參數(shù)組成辛燥。 根據(jù)事件代碼筛武,參數(shù)可能包含指針、返回代碼挎塌、引用時(shí)間或其他信息徘六。 有關(guān)事件代碼和參數(shù)的完整列表,請(qǐng)參閱 事件通知代碼榴都。
若要從隊(duì)列中檢索事件待锈,應(yīng)用程序在 Filter Graph Manager 上調(diào)用 IMediaEvent::GetEvent 方法。 此方法將阻塞嘴高,直到有要返回的事件竿音,或直到指定的時(shí)間過(guò)去。 假設(shè)存在排隊(duì)事件拴驮,該方法返回事件代碼和兩個(gè)事件參數(shù)春瞬。 調(diào)用 GetEvent 后,應(yīng)用程序應(yīng)始終調(diào)用 IMediaEvent::FreeEventParams 方法莹汤,以釋放與事件參數(shù)關(guān)聯(lián)的任何資源快鱼。 例如,參數(shù)可能是由filter
圖分配的 BSTR 值。
下面的代碼示例概述了如何從隊(duì)列中檢索事件抹竹。
long evCode;
LONG_PTR param1, param2;
HRESULT hr;
while (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr))
{
switch(evCode)
{
// Call application-defined functions for each
// type of event that you want to handle.
}
hr = pEvent->FreeEventParams(evCode, param1, param2);
}
若要替代 Filter Graph 管理器對(duì)事件的默認(rèn)處理线罕,請(qǐng)使用事件代碼作為參數(shù)調(diào)用 IMediaEvent::CancelDefaultHandling 方法。 可以通過(guò)調(diào)用 IMediaEvent::RestoreDefaultHandling 方法來(lái)恢復(fù)默認(rèn)處理窃判。 如果filter
圖未對(duì)指定的事件代碼執(zhí)行默認(rèn)處理钞楼,則調(diào)用這些方法不起作用。
事件的兩種響應(yīng)機(jī)制
若要處理 DirectShow 事件袄琳,應(yīng)用程序需要一種方法來(lái)查明事件何時(shí)在隊(duì)列中等待询件。 Filter Graph 管理器提供了兩種執(zhí)行此操作的方法:
- 窗口通知: 每當(dāng)有新事件時(shí),F(xiàn)ilter Graph 管理器會(huì)將用戶定義的 Windows 消息發(fā)送到應(yīng)用程序窗口唆樊。
- 事件信號(hào): 如果隊(duì)列中有 DirectShow 事件宛琅,F(xiàn)ilter Graph 管理器會(huì)發(fā)出 Windows 事件信號(hào),如果隊(duì)列為空逗旁,則重置事件嘿辟。
應(yīng)用程序可以使用任一技術(shù)。 窗口通知通常更簡(jiǎn)單片效。
窗口通知
若要設(shè)置窗口通知红伦,請(qǐng)調(diào)用 IMediaEventEx::SetNotifyWindow 方法并指定私人消息。 應(yīng)用程序可以使用從WM_APP到0xBFFF范圍內(nèi)的消息號(hào)作為私人消息淀衣。 每當(dāng) Filter Graph 管理器在隊(duì)列中放置新事件通知時(shí)昙读,它會(huì)將此消息發(fā)布到指定的窗口。 應(yīng)用程序從窗口的消息循環(huán)中響應(yīng)消息膨桥。
下面的代碼示例演示如何設(shè)置通知窗口蛮浑。
#define WM_GRAPHNOTIFY WM_APP + 1 // Private message.
pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);
該消息是普通的 Windows 消息,獨(dú)立于 DirectShow 事件通知隊(duì)列發(fā)布国撵。 此方法的優(yōu)點(diǎn)是大多數(shù)應(yīng)用程序已實(shí)現(xiàn)消息循環(huán)陵吸。 因此,無(wú)需執(zhí)行大量額外工作即可合并 DirectShow 事件處理介牙。
下面的代碼示例演示了如何響應(yīng)通知消息的概述壮虫。 有關(guān)完整示例,請(qǐng)參閱 響應(yīng)事件环础。
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));
}
由于事件通知和消息循環(huán)都是異步的囚似,因此在應(yīng)用程序響應(yīng)消息時(shí),隊(duì)列可能包含多個(gè)事件线得。 此外饶唤,如果事件變得無(wú)效,有時(shí)可以從隊(duì)列中清除這些事件贯钩。 因此募狂,在事件處理代碼中办素,調(diào)用 IAMMediaEvent::GetEvent ,直到返回失敗代碼祸穷,指示隊(duì)列為空性穿。
在釋放 IMediaEventEx 指針之前,請(qǐng)使用 NULL 指針調(diào)用 SetNotifyWindow 來(lái)取消事件通知雷滚。 在事件處理代碼中需曾,檢查 IMediaEventEx 指針在調(diào)用 GetEvent 之前是否有效。 這些步驟可防止可能出現(xiàn)的錯(cuò)誤祈远,即應(yīng)用程序在釋放 IMediaEventEx 指針后接收事件通知呆万。
事件信號(hào)
Filter Graph 管理器保留一個(gè)反映事件隊(duì)列狀態(tài)的手動(dòng)重置事件。 如果隊(duì)列包含掛起的事件通知车份,F(xiàn)ilter Graph 管理器會(huì)發(fā)出手動(dòng)重置事件的信號(hào)谋减。 如果隊(duì)列為空,則對(duì) IMediaEvent::GetEvent 方法的調(diào)用將重置事件躬充。 應(yīng)用程序可以使用此事件來(lái)確定隊(duì)列的狀態(tài)逃顶。
備注
此處的術(shù)語(yǔ)可能會(huì)令人困惑。 手動(dòng)重置事件是由 Windows CreateEvent 函數(shù)創(chuàng)建的事件類型, 它與 DirectShow 定義的事件無(wú)關(guān)充甚。
調(diào)用 IMediaEvent::GetEventHandle 方法以獲取手動(dòng)重置事件的句柄。 等待通過(guò)調(diào)用 WaitForMultipleObjects 等函數(shù)發(fā)出事件信號(hào)霸褒。 發(fā)出事件信號(hào)后伴找,調(diào)用 IMediaEvent::GetEvent 以獲取 DirectShow 事件。
以下代碼示例演示了此方法废菱。 它獲取事件句柄技矮,然后以 100 毫秒的間隔等待事件發(fā)出信號(hào)。 如果事件收到信號(hào)殊轴,它將調(diào)用 GetEvent 并將事件代碼和事件參數(shù)輸出到控制臺(tái)窗口衰倦。 循環(huán)在 發(fā)生EC_COMPLETE 事件時(shí)終止,指示播放已完成旁理。
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);
}
}
}
由于filter
圖會(huì)在適當(dāng)時(shí)自動(dòng)設(shè)置或重置事件樊零,因此應(yīng)用程序不應(yīng)這樣做。 此外孽文,釋放filter
圖時(shí)驻襟,filter
圖將關(guān)閉事件句柄,因此不要在該點(diǎn)之后使用事件句柄芋哭。