DirectShow中的事件通知机制
智慧的鱼(aoosang)
摘要:这篇文档描述DirectShow中事件产生的机制,以及应用程序是如何处理事件的。
1概述
事件是Graph图和应用程序之间互相通信的机制,类似于消息机制。当某个事件发生时,比如数据流结束,产生一个错误等,Filter就要给Filter图表管理器(Graph Manager)发送一个事件通知。Filter图表管理器处理其中的一部分事件,将其他事件要交给应用程序处理。如果图表管理器没有处理一个filter事件,它就把事件通知放入到一个队列中,图表管理器也可以将自己的事件通知放进队列中。然后应用程序可以自己处理队列中的事件。DirectShow中的事件通知就和windows的消息机制差不多。Filter,图表管理器和应用程序通过这种机制就可以互相通信。
2 如何处理事件
Filter图表管理器暴露了三个接口用来处理事件通知
IMediaEventSink Filter用这个接口来post事件。
IMediaEvent 应用程序利用这个接口来从队列中查询消息
IMediaEventEx 是imediaevent的扩展。
Filter都是通过调用图表管理器的 IMediaEventSink::Notify方法来通知图表管理器某种事件发生。事件通知包括一个事件code,这个code不仅仅代表了事件的类型,还包含两个DWORD类型的参数用来传递一些其他的信息。
关于事件code的内容,在下面的一个专题中列出,这里暂略,使用时可以参考帮助。
应用程序通过调用图表管理器的IMediaEvent::GetEvent方法来从事件队列中获取事件。如果有事件发生,该函数就返回一个事件码和两个参数,如果没有事件,则一直阻塞直到有事件发生和超过某个时间。调用GetEvent函数后,应用程序必须调用IMediaEvent::FreeEventParams来释放事件码所带参数的资源。例如,某个参数可能是由filter graph分配的内存。
下面的代码演示了如何从事件队列中提取事件
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);
}
为了重载Filter图表管理器对事件的缺省处理,你可以使用某个事件码做参数调用IMediaEvent::CancelDefaultHandling ,这样就可以屏蔽图表管理器对某个事件码的处理了。如果要恢复图表管理器对该事件码的缺省处理,可以调用 IMediaEvent::RestoreDefaultHandling。如果图表管理器对某个事件码没有缺省的处理,调用这两个函数是不起作用的。
3事件是如何发生的
为了处理事件,应用程序需要一种机制来获取正在队列中等待的事件。Filter图表管理器提供了两种方法。
1 窗口通知,图表管理器发送开发者自己定义的窗口消息
2 事件信号 如果队列中有dshow事件,就用事件信号通知应用程序,如果队列为空就重新设置事件信号。
下面的代码演示了如何利用消息通知
#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(); // Application-defined function.
break;
// Handle other Windows messages here too.
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
由于事件通知和窗口的消息循环都是异步的,因此,当你的应用程序处理消息的时候,队列中或许有N个事件等待处理。因此,在你调用GetEvent的时候,一定要循环调用,直到返回一个错误码,这表明队列是空的。
当你释放IMediaEventEx 指针时,你可以调用SetNotifyWindow来取消事件通知,记住此时要给这个函数传递一个NULL指针。在你的事件处理程序中,在调用GetEvent之前一定要检查IMediaEventEx指针是否为空,这样就可以避免错误。
下面看看采取事件信号的通知方式。
在Filter图表管理器里有一个手动设置的Event内核对象,用来反映事件队列的状态。如果队列中有等待处理的事件,event就处于通知状态,如果队列是空的,IMediaEvent::GetEvent函数调用就会重置该event对象。
应用程序可以调用IMediaEvent::GetEventHandle获得event内核对象的句柄,然后就可以调用WaitForMultipleObjects来等待事件的发生,如果event被通知了,就可以调用IMediaEvent::GetEvent来获得dshow的事件。
下面的代码演示了如何利用event内核对象来获取EC_COMPLETE事件,
HANDLE hEvent;
long evCode, param1, param2;
BOOLEAN bDone = FALSE;
HRESULT hr = S_OK;
hr = pEvent->GetEventHandle((OAEVENT*)&hEvent);
if (FAILED(hr)
{
/* Insert failure-handling code here. */
}
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);
pEvent->FreeEventParams(evCode, param1, param2);
switch (evCode)
{
case EC_COMPLETE: // Fall through.
case EC_USERABORT: // Fall through.
case EC_ERRORABORT:
CleanUp();
PostQuitMessage(0);
return;
}
}
}
事件通知码(Event Notification Codes)
常用的事件通知码如下:
Event notification code
Description
A video window is being activated or deactivated.
The graph is buffering data, or has stopped buffering data.
Send by the Video Control when a graph has been built. Not forwarded to applications.
The reference clock has changed.
The clock provider was disconnected.
Sent by an encoder to signal an encoding event.
All data from a particular stream has been rendered.
A Plug and Play device was removed or has become available again.
The display mode has changed.
The end of a segment has been reached.
An asynchronous command to run the graph has failed.
An operation was aborted because of an error.
Not supported.
The video renderer is switching out of full-screen mode.
The filter graph has changed.
The length of a source has changed.
A filter is requesting that the graph be restarted.
Notifies a filter of the video renderer's window.
A filter is passing a text string to the application.
The graph is opening a file, or has finished opening a file.
The video palette has changed.
A pause request has completed.
The graph is dropping samples, for quality control.
A video renderer requires a repaint.
A new segment has started.
The filter graph is shutting down, prior to being destroyed.
An audio device error has occurred on an input pin.
An audio device error has occurred on an output pin.
A filter is not receiving enough data.
The filter graph has changed state.
A filter performing frame stepping has stepped the specified number of frames.
A stream-control start command has taken effect.
A stream-control start command has taken effect.
An error has occurred in a stream. The stream is still playing.
A stream has stopped because of an error.
Not supported.
EC_UNBUILT
Send by the Video Control when a graph has been torn down. Not forwarded to applications.
The user has terminated playback.
The native video size has changed.
Sent when the VMR has selected its rendering mechanism.
Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented.
Sent by the VMR-7 and the VMR-9 when it was unable to accept a dynamic format change request from the upstream decoder.
The video renderer was destroyed or removed from the graph.
Sent by the Windows Media Format SDK when an application uses the ASF Reader filter to play ASF files protected by digital rights management (DRM).
Sent by the Windows Media Format SDK when an application uses the ASF Writer to index Windows Media Video files.