Microsoft DirectX 8.0 |
This article describes how events are raised in a Microsoft® DirectShow® filter graph, and how an application can retrieve event notifications and respond to them. It contains the following sections.
A filter notifies the Filter Graph Manager about an event by posting an event notification. The event could be something expected, such as the end of a stream, or it could represent an error, such as a failure to render a stream. The Filter Graph Manager handles some filter events by itself, and it leaves others for the application to handle. If the Filter Graph Manager does not handle a filter event, it places the event notification into a queue. The filter graph can also queue its own event notifications for the application.
An application retrieves events from the queue and responds to them based on the type of event. Event notification in DirectShow is therefore similar to the Microsoft® Windows® message queuing scheme. An application can also cancel the Filter Graph Manager's default behavior for a given event type. The Filter Graph Manager then puts those events directly into the queue for the application to handle.
This mechanism enables
The Filter Graph Manager exposes three interfaces that support event notification.
Filters post event notifications by calling the IMediaEventSink::Notify method on the Filter Graph Manager. An event notification consists of an event code, which defines the type of event, and two DWORD parameters that give additional information. Depending on the event code, the parameters might contain pointers, return codes, reference times, or other information. For a complete list of event codes and parameters, see Event Notification Codes.
To retrieve an event from the queue, the application calls the IMediaEvent::GetEvent method on the Filter Graph Manager. This method blocks until there is an event to return or until a specified time elapses. Assuming there is a queued event, the method returns with the event code and the two event parameters. After calling GetEvent, an application should always call the IMediaEvent::FreeEventParams method to release any resources associated with the event parameters. For example, a parameter might be a BSTR value that was allocated by the filter graph.
The following code example provides an outline of how to retrieve events from the queue.
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); }
To override the Filter Graph Manager's default handling for an event, call the IMediaEvent::CancelDefaultHandling method with the event code as a parameter. You can reinstate the default handling by calling the IMediaEvent::RestoreDefaultHandling method. If the filter graph performs no default handling for the specified event code, calling these methods has no effect.
In addition to retrieving event notifications from the queue, as discussed above, an application needs a way to find out when events are waiting in the queue. The Filter Graph Manager provides two ways to do this.
The following sections describe each technique.
To set up window notification, call the IMediaEventEx::SetNotifyWindow method and specify a private message. Applications can use message numbers in the range from WM_APP through 0xBFFF as private messages. Whenever the Filter Graph Manager places a new event notification in the queue, it posts this message to the designated window. The application responds to the message from within the window's message loop.
The following code example shows how to set the notification window.
#define WM_GRAPHNOTIFY WM_APP + 1 // Private message. pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);
The message is an ordinary Windows message, and is posted separately from the DirectShow event notification queue. The advantage of this approach is that most applications already implement a message loop. Therefore, you can incorporate DirectShow event handling without much additional work.
The following code example shows an outline of how to respond to the notification message. For a complete example, see Responding to Events.
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)); }
Because event notification and the message loop are both asynchronous, the queue might contain more than one event by the time your application responds to the message. Also, events can sometimes be cleared from the queue if they become invalid. Therefore, in your event handling code, call GetEvent until it returns a failure code, indicating that the queue is empty.
The filter graph keeps a manual-reset event that reflects the state of the event queue. If the queue contains pending event notifications, the filter graph signals the manual-reset event. If the queue is empty, a call to the IMediaEvent::GetEvent method resets the event. An application can use this event to determine the state of the queue.
Note The terminology can be confusing here. The manual-reset event is the type of event created by the Windows CreateEvent function; it has nothing to do with the event notifications defined by DirectShow.
The IMediaEvent::GetEventHandle method retrieves a handle to the manual-reset event. Wait for the event to be signaled by calling a function such as WaitForMultipleObjects. Once the event is signaled, call the IMediaEvent::GetEvent method to retrieve the event notification.
The following code example illustrates this approach. It retrieves the event handle, then waits in 100-millisecond intervals for the event to be signaled. If the event is signaled, it calls GetEvent and prints the event code and event parameters to the console window. The loop terminates when the EC_COMPLETE event occurs, indicating that playback has completed.
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, ¶m1, ¶m2, 0), SUCCEEDED(hr)) { printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2); hr = pEvent->FreeEventParams(evCode, param1, param2); bDone = (EC_COMPLETE == evCode); } } }
Because the filter graph automatically sets or resets the event when appropriate, your application should not do so. Also, when you release the filter graph, the filter graph closes the event handle, so do not use the event handle after that point.