Using Event Notification
A version of this page is also available for
4/8/2010
The sample application handles events from within the main window message loop. It works by having the filter graph manager post a message when a new event occurs. The application responds by retrieving the event and taking the appropriate action.
To handle events from the main window message loop, define the message that will be sent to the application window when a new event occurs. Applications can use message numbers in the range from WM_APP through 0xBFFF as private messages, as shown in the following example.
#define WM_GRAPHNOTIFY WM_APP + 1
Next, set the filter graph manager to deliver this message to the application's main window.
pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);
The IMediaEventEx::SetNotifyWindow method designates the specified window (g_hwnd
) as the recipient of the message. Call this method after you create the filter graph and specify the owner window, but before running the graph. For details, see DirectShow Sample Code. The IMediaEventEx interface is exposed by the filter graph manager.
When the application receives the WM_GRAPHNOTIFY message, the message's lParam parameter is equal to the third parameter passed to SetNotifyWindow. This parameter enables you to send instance data with the message. The sample code does not use this data, so it passes a value of zero. The message's wParam parameter is always zero.
In the window's WindowProc function, add a case statement for the WM_GRAPHNOTIFY message.
case WM_GRAPHNOTIFY:
HandleEvent();
break;
WM_GRAPHNOTIFY is an ordinary Windows message, and is posted separately from the DirectShow event notification queue. The sequence of events is as follows:
A filter sends an event notification to the filter graph manager.
If the filter graph manager does not handle the event, it places the event in the event queue.The filter graph manager posts a WM_GRAPHNOTIFY message to the application window.
The application responds to the message from within the window's message loop.
The application retrieves the event notification from the queue.
In the event handler function, call the IMediaEvent::GetEvent method to retrieve events from the queue.
long evCode, param1, param2; HRESULT hr; while (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr)) { hr = pEvent->FreeEventParams(evCode, param1, param2); if ((EC_COMPLETE == evCode) || (EC_USERABORT == evCode)) { CleanUp(); break; } }
The GetEvent method retrieves the event code and the two event parameters. The last parameter to GetEvent specifies how long the method will wait for an event. Because the application calls this method in response to a WM_GRAPHNOTIFY message, the event is already queued, so use a time-out value of zero.
Because event notification and the message loop are both asynchronous, the queue might hold more than one event by the time your application responds to the message. Also, the filter graph manager can clear events from the queue, if they become invalid. Therefore, call GetEvent until it returns a failure code, indicating that the queue is empty.
If the sample program receives an EC_COMPLETE or EC_USERABORT event, it invokes the application-defined CleanUp function, which causes the application to quit gracefully. It ignores the two event parameters (param1 and param2). After you retrieve an event, call IMediaEvent::FreeEventParams to free resources associated with the event parameters. For example, the parameter may be a BSTR, whose memory was allocated by the filter graph manager.
When an EC_COMPLETE event occurs, the filter graph does not automatically switch to a stopped state. The application must stop or pause the graph. When the graph stops, filters release any resources they hold. When it pauses, they continue to hold resources. Also, when a video renderer pauses, it displays a static image of the most recent frame. The sample program stops the graph. Stopping the graph is not instantaneous, however, and the IMediaControl::Stop method might return before the process finishes. It is a good practice to wait for the state to change before releasing interfaces or posting a quit message. Use the IMediaEvent::WaitForCompletion method to wait for the operation to complete, as shown in the following example.
pMediaControl->Stop();
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);