您的位置:首页 > 编程语言

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

2012-04-15 11:24 274 查看
http://blog.csdn.net/believefym/article/details/1779742

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, ¶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对某个事件号没有默认处理过程,则调用上面两个方法不产生任何影响。

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的CreateEve
4000
nt函数创建的一种事件类型,它与由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会关闭事件句柄,因此在这之后你就不能再使用事件句柄了。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息