DirectShow中的事件通知

37 篇文章 1 订阅

概述

    一个filter通过发送一个事件来通知filter graph manager某个事件已经发生。这些事件可以是一些预知的事件比如流结束事件,也可以是一些异常如render流时失败。一部分事件由filter graph manager自己处理,另一部分由应用程序来处理。如果filter graph manager不处理某个事件,那么这个事件会被放入到队列中。filter graph也可以通过队列将自己的事件发送给应用程序。

    应用程序从队列中接收事件并根据其类型来响应它们。DirectShow中的事件通知类似于windows的消息队列机制。应用程序可以让filter graph manager取消对指定的事件类型的默认操作,而是将它们放入事件队列由应用程序来处理它们。

   由于这样的机制,我们能做到:

   ·filter graph manager与应用程序的对话

   ·filter可以既和应用程序也和filter graph manager对话

   ·由应用程序来决定处理事件的复杂度


从队列中取事件

   Filter Graph Manager暴露3个支持事件通知的接口:

   ·IMediaEventSink 包含filter发送事件的方法

   ·IMediaEvent 包含应用程序取事件的方法

   ·IMediaEventEx 继承扩展IMediaEvent接口

   filter通过在filter graph manager上调用IMediaEventSink::Notify方法来发送事件通知,一个事件通知由一个表示事件类型的事件号,和两个DWORD类型用以放置附加信息的参数组成。按事件号的不同,这两个参数可以是指针、返回值、参考时间或其他信息。完整的事件号和参数信息,参加Event Notification codes((http://msdn.microsoft.com/library/en-us/directshow/htm/eventnotificationcodes.asp).

   要从事件队列中取事件,应用程序需要在filter graph manager上调用IMediaEvent::GetEvent事件。这个方法一直阻塞到取到事件或超时。一旦队列有了事件,这个方法就返回事件号和两个事件参数。在调用GetEvent后,应用程序应该总是调用IMediaEvent::FreeEventParams方法来释放和事件参数想关的所有资源。比如,一个参数可能是由filter graph分配的BSTR值。

   下面的代码是一个如何从队列中取事件的框架:

 long evCode, param1, param2;
  HRESULT hr;
  while (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr))
  {
      switch(evCode) 
      { 
          // Call application-defined functions for each 
          // type of event that you want to handle.
      } 
      hr = pEvent->FreeEventParams(evCode, param1, param2);
  }

    要重置filter graph manager默认的事件处理过程,调用IMediaEvent::CancelDefaultHandling方法,用事件号做参数。你可以通过调用 IMediaEvent::RestoreDefaultHandling方法来恢复某个事件的处理过程。如果filter graph对某个事件号没有默认处理过程,则调用上面两个方法不产生任何影响。


当事件发生时

   要处理DirectShow事件,应用程序需要一个方法来知道事件何时正等待在队列中。Filter Graph Manager提供两种方法: 

   *窗口通告:一旦有事件发生,Filter Graph Manager就发送一个用户自定义窗口消息来通知应用程序窗口     

   *事件信号:如果有DirectShow事件在队列中,filter graph manager就触发一个windows事件,如果队列为空,则reset这个事件。     

    应用程序可以使用任何一种方法,但通常窗口通告方法相对比较简单。


窗口通知

    要设置窗口通告,调用IMediaEventEx::SetNotifyWindow方法并指定一个私有消息,私有消息可以是从WM_APP到 0xBFFF的任一个。一旦filter graph manager把一个新的事件通告放入队列中,它便发送这个消息给指定的窗口。应用程序从窗口的消息循环中来响应这个消息。

   下面是如何设置通知窗口的例子:

#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));
  }

    因为事件通告与消息循环均为异步进行的,因此在应用程序响应事件时队列中可以会有多个事件。而当事件变为非法时,它们会从队列中被清除掉。所以在你的事件处理代码中,调用GetEvent直至返回一个表示队列已空的失败代号。

    在释放IMediaEventEx指针前,请以NULL作参数调用SetNotifyWindow方法来取消事件通告。并且在你的事件处理代码中,在调用 GetEvent前检查IMediaEventEx指针是否合法。这些步骤可以防止在释放IMediaEventEx指针后应用程序继续接收事件通告的错误。


事件信号

    Filter Graph Manager建立一个反映事件队列状态的手工重设事件(manual-reset event)。如果队列中包含有未处理的事件通告,Filter Graph Manager就会发信号给手工重设事件。如果队列是空的,则调用IMediaEvent::GetEvent方法会重设(reset)事件。应用程序可以通过这个事件来确定队列的状态。

    注意:此处的术语可能被混淆。手工重设事件是由windows的CreateEvent函数创建的一种事件类型,它与由DirectShow定义的事件无关。

    调用IMediaEvent::GetEventHandle方法得到手工重设事件的句柄,调用一个函数如WaitForMultipleObjects 来等待发送给手工重设事件的信号。一旦收到信号,就可以调用IMediaEvent::GetEvent来接收DirectShow事件了。

    下面的代码举例说明了这种方法。在取得事件句柄后,在100毫秒时间间隔内等待发送给手工重设事件的信号,如果有信号发来,它调用GetEvent然后在 windows控制台上打印出事件号和事件参数,循环在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 (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr)) 
          {
              printf("Event code: %#04x/n Params: %d, %d/n", evCode, param1, param2);
              pEvent->FreeEventParams(evCode, param1, param2);
              bDone = (EC_COMPLETE == evCode);
          }
      }
  } 

    因为Filter Graph会在适当的时候自动重设事件,因此你的应用程序应当不去作重设工作。同时,当你释放filter graph时,filter graph会关闭事件句柄,因此在这之后你就不能再使用事件句柄了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值