DirectShow 事件产生
要处理DirectShow事件,应用程序需要一种方法来确定事件何时在队列中等待。过滤器图形管理器提供了两种方法:
- 窗口通知:每当出现新事件时,过滤器图形管理器就会向应用程序窗口发送用户定义的窗口消息。
- 事件信号:如果队列中有DirectShow事件,Filter Graph Manager将向Windows事件发送信号,如果队列为空,则重置事件。
窗口通知
要设置窗口通知,调用IMediaEventEx::SetNotifyWindow方法并指定私有消息。应用程序可以使用从WM_APP到0xBFFF 范围内的消息编号作为私有消息。每当过滤器图形管理器在队列中放置新的事件通知时,它都会将此消息发布到指定的窗口。应用程序从窗口的消息循环中响应消息。
下面的代码示例演示如何设置通知窗口。
#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来取消事件通知。在事件处理代码中,在调用GetEvent之前检查IMediaEventEx指针是否有效。这些步骤可以防止可能的错误,即应用程序在释放IMediaEventEx指针后接收事件通知。
事件信号
过滤图形管理器保留一个反映事件队列状态的手动重置事件。如果队列包含挂起的事件通知,则过滤器图形管理器会向手动重置事件发出信号。如果队列为空,则调用IMediaEvent::GetEvent方法重置事件。应用程序可以使用此事件来确定队列的状态。
调用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);
}
}
}
因为过滤器图会在适当的时候自动设置或重置事件,所以应用程序不应该这样做。另外,当释放过滤器图时,过滤器图会关闭事件句柄,因此不要在该点之后使用事件句柄。