DirectShow 事件通知

发布于 2021-11-29


DirectShow 可与它的宿主程序进行事件通知。当 DirectShow 内部运行时产生了什么事件,filter 可通过 IMediaEventSink::Notify 向上层发送事件通知。

DirectShow 有两个接口接收通知。一是 IMediaEvent,之前播放视频文件的例子就用到它来接收视频播放结束的通知事件:

  CComPtr<IMediaEvent> media_event;
  hr = graph_buiulder->QueryInterface(IID_IMediaEvent, (void **)&media_event);
  if (FAILED(hr)) {
    return 0;
  }

  // 等待视频播放完成
  long event_code = 0;
  media_event->WaitForCompletion(INFINITE, &event_code);

一般通过 IMediaEvent::GetEvent 来从消息队列里接收事件通知。

此外还可以使用 IMediaEventEx,它继承自 IMediaEvent。IMediaEventEx::SetNotifyWindow 可以设置一个窗口句柄,当有消息发生时,就会往这个窗口发送通知。

    #define WM_GRAPHNOTIFY WM_APP + 1

    CComPtr<IMediaEventEx> media_event;
    hr = graph_buiulder_->QueryInterface(IID_IMediaEventEx,
      (void**)&media_event_);
    if (FAILED(hr)) {
      return false;
    }
    media_event_->SetNotifyWindow((OAHWND)parent_, WM_GRAPHNOTIFY, 0);

    void HandleEvent() {
    long event_code;
    LONG_PTR param1, param2;
    while (
      SUCCEEDED(media_event_->GetEvent(&event_code, &param1, &param2, 0))) {
      media_event_->FreeEventParams(event_code, param1, param2);
      CString str;
      str.Format(L"HandleEvent:%u, %u %u\n", event_code, param1, param2);
      OutputDebugString(str);
    }
  }

输出结果:
HandleEvent:9, 0 0
HandleEvent:10, 31457920 0
HandleEvent:13, 0 0
HandleEvent:14, 0 0
HandleEvent:31, 12807516 0
HandleEvent:31, 12807516 1

event_code 值 31 是 EC_DEVICE_LOST,param2 为 0 表示设备拔开,param2 为 1 表示设备拔开后又重新接入。

当我们不再需要处理事件时,SetNotifyWindow 置空:

    if (media_event_) {
      media_event_->SetNotifyWindow((OAHWND)NULL, 0, 0);
    }

此外也可以响应 WM_DEVICECHANGE 消息来处理设备的插拔。

参考