对事件作出响应

本文介绍如何响应筛选器图中发生的事件。

事件通知的工作原理

当DirectShow应用程序正在运行时,可以在筛选器图中发生事件。 例如,筛选器可能会遇到流式处理错误。 通过发送事件(包括事件代码和两个事件参数)来筛选筛选器Graph管理器发出警报。 事件代码指示事件的类型,事件参数提供其他信息。 参数的含义取决于事件代码。 有关事件代码的完整列表,请参阅 事件通知代码

筛选器Graph管理器以无提示方式处理某些事件,而不会通知应用程序。 其他事件放置在应用程序的队列中。 根据应用程序,可能需要处理各种事件。 本文重点介绍三个非常常见的事件:

  • EC_COMPLETE事件指示播放已正常完成。
  • EC_USERABORT事件指示用户已中断播放。 如果用户关闭视频窗口,视频呈现器将发送此事件。
  • EC_ERRORABORT事件指示错误导致播放停止。

使用事件通知

应用程序可以指示筛选器Graph管理器在发生新事件时向指定窗口发送Windows消息。 这使应用程序能够在窗口的消息循环内响应。 首先,定义将发送到应用程序窗口的消息。 应用程序可以使用从WM_APP到0xBFFF范围内的消息号作为专用消息:

#define WM_GRAPHNOTIFY  WM_APP + 1

接下来,查询 IMediaEventEx 接口的筛选器Graph管理器,并调用 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消息。 每当筛选器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消息,因此事件已排队。 因此,我们将超时值设置为零。

事件通知和消息循环都是异步的,因此在应用程序响应消息时,队列可能会保存多个事件。 此外,筛选器Graph管理器可能会从队列中删除某些事件(如果它们无效)。 因此,应调用 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中的事件通知