CSDN博客

img eyes4

DirectShow编程(3.5) - 关于DirectShow - DirectShow中的事件通告

发表于2004/10/13 9:51:00  4713人阅读

3.5 DirectShow中的事件通告
    这一节主要描述在directshow filter graph中事件是怎样发生的,以及应用程序如何接收事件通告并响应它们。
3.5.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对话
     *由应用程序来决定处理事件的复杂度。

3.5.2 从队列中取事件
    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, &param1, &param2, 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对某个事件号没有默认处理过程,则调用上面两个方法不产生任何影响。

3.5.3 当事件发生时
    要处理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, &param1, &param2, 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

相关文章推荐

img
取 消
img