Responding to Events
[The feature associated with this page, DirectShow, is a legacy feature. It has been superseded by MediaPlayer, IMFMediaEngine, and Audio/Video Capture in Media Foundation. Those features have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use MediaPlayer, IMFMediaEngine and Audio/Video Capture in Media Foundation instead of DirectShow, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]
This article describes how to respond to events that occur in a filter graph.
How Event Notification Works
While a DirectShow application is running, events can occur within the filter graph. For example, a filter might encounter a streaming error. Filters alert the Filter Graph Manager by sending events, which consist of an event code and two event parameters. The event code indicates the type of event, and the event parameters supply additional information. The meaning of the parameters depends on the event code. For a complete list of event codes, see Event Notification Codes.
Some events are handled silently by the Filter Graph Manager, without the application being notified. Other events are placed on a queue for the application. Depending on the application, there are various events that you might need to handle. This article focuses on three events that are very common:
- The EC_COMPLETE event indicates that playback has completed normally.
- The EC_USERABORT event indicates that the user has interrupted playback. Video renderers send this event if the user closes the video window.
- The EC_ERRORABORT event indicates that an error has caused playback to halt.
Using Event Notification
An application can instruct the Filter Graph Manager to send a Windows message to a designated window whenever a new event occurs. This enables the application to respond inside the window's message loop. First, define the message that will be sent to the application window. Applications can use message numbers in the range from WM_APP through 0xBFFF as private messages:
#define WM_GRAPHNOTIFY WM_APP + 1
Next, query the Filter Graph Manager for the IMediaEventEx interface and call the IMediaEventEx::SetNotifyWindow method:
IMediaEventEx *g_pEvent = NULL;
g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent);
g_pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);
This method designates the specified window (g_hwnd) as the recipient of the message. Call the method after you create the filter graph, but before running the graph.
WM_GRAPHNOTIFY is an ordinary Windows message. Whenever the Filter Graph Manager puts a new event on the event queue, it posts a WM_GRAPHNOTIFY message to the designated application window. The message's lParam parameter is equal to the third parameter in SetNotifyWindow. This parameter enables you to send instance data with the message. The window message's wParam parameter is always zero.
In your application's WindowProc function, add a case statement for the WM_GRAPHNOTIFY message:
case WM_GRAPHNOTIFY:
HandleGraphEvent();
break;
In the event handler function, call the IMediaEvent::GetEvent method to retrieve events from the queue:
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;
}
}
}
The GetEvent method retrieves the event code and the two event parameters. The fourth GetEvent parameter specifies the length of time to wait for an event, in milliseconds. Because the application calls this method in response to a WM_GRAPHNOTIFY message, the event is already queued. Therefore, we set the time-out value to zero.
Event notification and the message loop are both asynchronous, so the queue might hold more than one event by the time your application responds to the message. Also, the Filter Graph Manager can remove certain events from the queue, if they become invalid. Therefore, you should call GetEvent until it returns a failure code, indicating the queue is empty.
In this example, the application responds to EC_COMPLETE, EC_USERABORT, and EC_ERRORABORT by invoking the application-defined CleanUp function, which causes the application to quit gracefully. The example ignores the two event parameters. After you retrieve an event, call IMediaEvent::FreeEventParams to any free resources associated with the event parameters.
Note that an EC_COMPLETE event does not cause the filter graph to stop. The application can either stop or pause the graph. If you stop the graph, filters release any resources they are holding. If you pause the graph, filters continue to hold resources. Also, when a video renderer pauses, it displays a static image of the most recent frame.
Before you release the IMediaEventEx pointer, cancel event notification by calling SetNotifyWindow with a NULL window handle:
// Disable event notification before releasing the graph.
g_pEvent->SetNotifyWindow(NULL, 0, 0);
g_pEvent->Release();
g_pEvent = NULL;
In the WM_GRAPHNOTIFY message handler, check the IMediaEventEx pointer before calling GetEvent:
if (g_pEvent == NULL) return;
This prevents a possible error that can occur if the application receives the event notification after releasing the pointer.
Related topics