对事件作出响应

[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayerIMFMediaEngine媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayerIMFMediaEngineMedia 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, &param1, &param2, 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_COMPLETEEC_USERABORTEC_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;

这可以防止应用程序在释放指针后收到事件通知时可能发生的错误。

基本 DirectShow 任务

DirectShow 中的事件通知