因爲VMR沒有自己的窗口,所以當視頻需要重畫或者改變的時候你要通知它。
1、當你接到一個WM_PAINT消息,你就要調用IVMRWindowlessControl::RepaintVideo來重畫視頻
2、當你接到一個WM_DISPLAYCHANGE消息,你就要調用IVMRWindowlessControl::DisplayModeChanged.
3、當你接到一個WM_SIZE消息時,重新計算視頻的位置,然後調用SetVideoPostion。
下面的代碼演示了WM_PAINT消息的處理:
void OnPaint(HWND hwnd) { PAINTSTRUCT ps; HDC hdc; RECT rcClient; GetClientRect(hwnd, &rcClient); hdc = BeginPaint(hwnd, &ps); if (g_pWc != NULL) { // Find the region where the application can paint by subtracting // the video destination rectangle from the client area. // (Assume that g_rcDest was calculated previously.) HRGN rgnClient = CreateRectRgnIndirect(&rcClient); HRGN rgnVideo = CreateRectRgnIndirect(&g_rcDest); CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF); // Paint on window. HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE); FillRgn(hdc, rgnClient, hbr); // Clean up. DeleteObject(hbr); DeleteObject(rgnClient); DeleteObject(rgnVideo); // Request the VMR to paint the video. HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc); } else // There is no video, so paint the whole client area. { FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1)); } EndPaint(hwnd, &ps); } |
儘管我們要自己處理onpaint消息,但是已經非常簡單了。
如何處理事件通知(Event Notification)
當一個Directshow的應用程序運行的時候,在 filter Graph內部就會發生各種各樣的事件,例如,一個filter也許發生數據流錯誤。Filter通過給graph mangaer發送事件通知來和graph通信,這個事件通知包括一個事件碼和兩個事件參數。事件碼錶示發生事件的類型,兩個參數用來傳遞信息。
Filter發送的這些事件,其中的一部分可以被Manager直接處理,不通知應用程序,但有一部分事件,Manager將事件放入到一個隊列中,等待應用程序處理。這裏我們主要討論在應用程序中經常遇到的三種事件
EC_COMPLETE表明回放已經結束
EC_USERABORT表明用戶中斷了回放。用戶關閉視頻播放窗口時,視頻Render會發生這個事件
EC_ERRORABORT表明出現了一個錯誤。
應用程序可以通知filter graph manager,在某個指定的事件發生時,向指定的窗口發生一個指定的消息。這樣應用程序就可以在消息循環中對發生的事件產生反應。
首先定義消息:
#define WM_GRAPHNOTIFY WM_APP + 1 |
然後向filter graph manager請求IMediaEventEx接口,然後調用IMediaEventEx::SetNotifyWindow方法來設置消息通知窗口:
IMediaEventEx *g_pEvent = NULL; g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent); g_pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0); |
然後在WindowProc函數增加一個處理WM_GRAPHNOTIFY消息的函數:
case WM_GRAPHNOTIFY: HandleGraphEvent(); break; |
HandleGraphEvent()函數具體定義如下
void HandleGraphEvent() { // Disregard if we don't have an IMediaEventEx pointer. if (g_pEvent == NULL) { return; } // Get all the events long evCode; LONG_PTR param1, param2; HRESULT hr; while (SUCCEEDED(g_pEvent->GetEvent(&evCode, ¶m1, ¶m2,0))) { g_pEvent->FreeEventParams(evCode, param1, param2); switch (evCode) { case EC_COMPLETE: // Fall through. case EC_USERABORT: // Fall through. case EC_ERRORABORT: CleanUp(); PostQuitMessage(0); return; } } } |
在釋放IMediaEventEx指針前,要取消事件通知消息,代碼如下:
// Disable event notification before releasing the graph. g_pEvent->SetNotifyWindow(NULL, 0, 0); g_pEvent->Release(); g_pEvent = NULL; |