对事件作出响应
[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayer、 IMFMediaEngine 和 媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayer、 IMFMediaEngine 和 Media Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]
本文介绍如何响应筛选器图中发生的事件。
事件通知的工作原理
当 DirectShow 应用程序正在运行时,事件可以在筛选器图中发生。 例如,筛选器可能会遇到流式处理错误。 筛选器通过发送事件(由一个事件代码和两个事件参数组成)向筛选器关系图管理器发出警报。 事件代码指示事件的类型,事件参数提供其他信息。 参数的含义取决于事件代码。 有关事件代码的完整列表,请参阅 事件通知代码。
某些事件由 Filter Graph 管理器以无提示方式处理,而不会通知应用程序。 其他事件将放置在应用程序的队列中。 根据应用程序的不同,可能需要处理各种事件。 本文重点介绍三个非常常见的事件:
- EC_COMPLETE 事件指示播放已正常完成。
- EC_USERABORT 事件指示用户已中断播放。 如果用户关闭视频窗口,视频呈现器会发送此事件。
- EC_ERRORABORT 事件指示错误已导致播放停止。
使用事件通知
应用程序可以指示筛选器图形管理器在发生新事件时将 Windows 消息发送到指定窗口。 这使应用程序能够在窗口的消息循环内响应。 首先,定义将发送到应用程序窗口的消息。 应用程序可以使用从 WM_APP 到 0xBFFF 范围内的消息号作为私人消息:
#define WM_GRAPHNOTIFY WM_APP + 1
接下来,查询 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 管理器将新事件放入事件队列时,它都会将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消息处理程序中,在调用 GetEvent 之前检查 IMediaEventEx 指针:
if (g_pEvent == NULL) return;
这可以防止应用程序在释放指针后收到事件通知时可能发生的错误。
相关主题