DirectShow技術描述與應用(3) (轉)

amyz發表於2007-10-17
DirectShow技術描述與應用(3) (轉)[@more@] 

DirectShow中的事件通知:namespace prefix = o ns = "urn:schemas--com::office" />

 

這一部分將描述在Microsoft® DirectShow®過濾器表中,事件是如何實現的;一個應用如何才能接受到事件通知並且響應它們。

 

事件通知概述

過濾器透過投遞事件通知來向過濾器表管理器通報一個事件。事件可以是包含任何資訊,如流的結束,也可以是一個錯誤,如還原流的失敗。過濾器表管理器本身處理一些過濾器事件,其它事件則留給應用程式來進行處理。如果過濾器表管理器遇到一個不能處理的事件,它就將事件放入到一個佇列中去。同樣的,過濾器表管理器也會將它自己的事件通知放入佇列中去,以期應用程式來進行處理。

應用程式可以從佇列中接收到事件並對它們做出響應。因此DirectShow的事件通知與Microsoft® ®訊息佇列非常相似。應用程式可以取消過濾器表管理器本身可以處理事件的這種行為,過濾器表管理則直接地將這些事件放入佇列中去,由應用程式來進行處理。這種機制允許:

  • 過濾器表管理器與應用程式進行通訊。
  • 過濾器可以與應用程式和過濾器表管理進行通訊。
  • 由應用程式確定它自己都處理哪些事件。

 

接收事件

過濾器表管理器向外部暴露三個介面,來支援事件通知.

  • IMediaEventSink包含過濾器投遞事件的方法
  • IMediaEvent包含應用程式接收事件的方法
  • _cpp/htm/imediaeventexinterface.htm">IMediaEventEx inherits from and extends the IMediaEvent interface.
  • IMediaEventEx繼承於並擴充套件了IMdeiaEvent介面

過濾器透過過濾器表管理器的IMediaEventSink::Notify方法來投遞事件通知。一個事件通知由一個事件程式碼和兩個D型引數組成。根據不同的事件程式碼,引數值可能會是指標、返回碼、參考時間或者其它資訊。

為能從佇列中接收事件,應用程式應呼叫IMediaEvent::GetEvent方法來接收資料。這個方法會被阻斷直到從一個佇列中得到一個事件或者超過時限。當呼叫完GetEvent後,應用程式應該總是呼叫IMediaEvent::FreeEventParams方法來釋放在事件引數中的資源。例如引數有可能是由過濾器表提供的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);

}

為了停止某個事件的過濾器表管理器的預設處理,可以呼叫IMediaEvent::CancelDefaultHandling方法。你可以透過呼叫IMediaEvent::RestoreDefaultHandling方法來恢復事件的預設處理。對於那些沒有過濾器表管理器預設處理的事件,呼叫這些方法將不會產生任何影響。

 

事件的什麼時候發生

為了DirectShow事件,應用程式需要一種在佇列中有事件在等待時,能夠被得知的方法。過濾表管理器提供兩種途徑來實現:

  • 視窗通知:過濾器表管理器在產生一個新事件時,嚮應用程式視窗傳送一個自定義的訊息。
  • 事件訊號:如果在佇列有DirectShow事件時,則過濾器表管理器傳送一個視窗事件。並在佇列為空時重置事件。
  • An application can use either technique. Window notification is usually simpler.
  • 應用程式可以使用其它手法來實現。但視窗訊息通常會更為簡單。

 

視窗通知

可以呼叫IMediaEventEx::SetNotifyWindow方法來指定一個私有訊息,來建立起視窗通知。這個私有訊息可以使用從WM_APP到WM_APP+0xBfff之間的數。只要當過濾器表管理向佇列裡放入新的事件,它就會向指定的應用程式視窗傳送資訊。應用程式對來自Windows訊息迴圈的訊息作出響應。

下面程式碼說明了如何設定通知視窗。

#define WM_GRAPHNOTIFY WM_APP + 1  // Private message.

pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);

  下面程式碼說明了如何對視窗訊息做出響應

LRESULT CALLBACK WindowProc( HWND hwnd, UINT msg, UINT wParam, LONG lParam)

{

  switch (msg)

  {

  case WM_GRAPHNOTIFY:

  HandleEvent();  //應用程式定義的.

  break;

   //也可以在這裡編寫訊息處理。

  }

  return (DefWindowProc(hwnd, msg, wParam, lParam));

}

因為事件通知和訊息迴圈都是非同步的,所以在你的應用程式對訊息作出響應時,佇列中有可能包含多個事件訊息。也可能有時候事件會變得無效,那麼它就會被清除出佇列。因此,在你的事件處理程式碼中,應不停地的呼叫GetEvent方法,直至返回失敗碼,即訊息佇列已為空。

注意,在你釋放IMediaEventEx指標時,先要透過向SetNotifyWindow傳入一個空指標來取消事件通知。在你的事件處理程式碼中,一個定要在呼叫GetEvent方法前檢查IMeiaEventEx指標的有效性。這樣可以預防在釋放了IMediaEventEx指標後,應用程式再次事件處理程式碼所可能出現的錯誤。

 

事件的發出(signal)

過濾器表管理器維護一個手動重置(manual-reset)的事件,用來反應事件佇列的狀態。如果事件佇列包含有未解決的事件通知,那麼過濾器表管理會發出一個手動重置事件。如果事件為空,呼叫IMediaEvent::GetEvent方法會重置事件。應用程式可以使用此事件來確定佇列的狀態。

 

注意:這個語術可能會被誤解。手動重置事件是一個透過Windows CreateEvent函式建立的事件型別,它在DirectShow中並不做任何事情。

 

呼叫IMediaEvent::GetEventHandle方法可以得到手動重置事件的控制程式碼。可以透過WaitForMultiple函式來等待這個事件的發生。一旦此事件發生,呼叫IMediaEvent::GetEvent方法就可以得到DirectShow事件。

下面的程式碼示例了這樣的功能。它取得事件的控制程式碼,每100毫秒的間隔等待事件的發生。當事件發生後,透過呼叫GetEvent方法來取得事件,並將事件程式碼和事件引數顯示出來。當EC_COMPLETE事件(其表示回放已經完成)出現則迴圈結束。

 

HANDLE  hEvent;

long  evCode, param1, param2;

BOOLEAN bDone = FALSE;

HRESULT hr = S_OK;

hr = pEvent->GetEventHandle((OAEVENT*)&hEv);

while(!bDone)

{

  if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 100))

  {

  while (hr = pEvent->GetEvent(&evCode, &param1, &param2, 0), SUCCEEDED(hr))

  {

  printf("Event code: %#04xn  Params: %d, %dn", evCode, param1, param2);

  hr = pEvent->FreeEventParams(evCode, param1, param2);

  bDone = (EC_COMPLETE == evCode);

  }

  }

}

 

因為過濾器表會適當地自動設定或重置事件,你的應用程式可以免於對這些事情的處理。同樣的,當你釋放了過濾器表後,過濾器表就會釋放掉事件控制程式碼。因此在你釋放過濾器表後,就不要再去使用事件控制程式碼。

 (待續...)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-977251/,如需轉載,請註明出處,否則將追究法律責任。

相關文章