DSHOW与设计模式

本文详细介绍了DSHOW中的Filter Graph Manager的功能,包括协调filter状态、建立参考时钟、处理filter消息和建立filter graph的方法。同时阐述了事件处理机制,如窗口通知和事件信号,并展示了相关接口和组件类的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

1, Filter Graph Manager

A, 功能:

1>     用来协调filter之间的状态改变,从而使graph 中的所有的filter的状态的改变应该一致。即应用程序向FGM发送命令

当应用程序调用IMediaControl::Run, IMediaControl::Pause, or IMediaControl::Stop时, 过滤器图表管理器就调用Filter相应的IMediaFilter方法。停止,运行状态的切换总是要经过暂停,因此,当一个应用程序对一个停止的Graph 调用RUN命令时,过滤器图表管理器  run之前首先要暂停.

当一个Filter停止时,它拒绝发送给它的任何samples,源filter关闭他们的stream线程,其他filter也关闭他们创建的其他线程,pin  decommit他们的内存分配器。

过滤器图表管理器按照逆流的方向来切换Filter的状态,从Renderer Filter到源filter,这种方式可以防止死锁。最关键的状态切换是暂停和停止之间。

从停止到暂停,当filter暂停时,它就做好了接收sample的准备,源filter是最后一个切换到暂停的。它开始创建streaming线程,发送sample,因为下游的filter的状态都已经切换到暂停了,所以,所有的filter都可以接收sample。只有当所有的flter都接收到sample,过滤器图表管理器才算完成了状态的切换

从暂停到停止。当一个filter停止时,它要释放它拥有的所有的samples。当图表管理器试图停掉上游的一个filter时,这个filter不会阻塞在Getbufferreceive方法里,它会立即响应stop命令。上游的filter也许在执行stop命令前还会讲少量的sample传递下去,但是下游的filter会拒绝的,因为他们已经停止了。

IMediaControl:

The filter graph exposes the IMediaControl interface to allow applications to control the streaming of media through the filters in the graph. The interface provides methods for running, pausing, and stopping the streaming of data. It also provides applications with a simple method of building graphs to play back media files. This interface is implemented by the filter graph manager

Use this interface from any application that wants to control the playing of media through DirectShow filter graphs. Applications can also use it to enumerate the filters in the filter graph and all the filters in the registry, to add a source filter to the filter graph, and to instruct the filter graph manager to build a filter graph capable of rendering the media type in a file.

2>     建立一个参考时钟。

IFilterGraph::SetDefaultSyncSource函数实现:

This method is used when no clock has been given to the filter graph, and the filter graph manager consequently must find a clock to use as the synchronization source. The filter graph manager first tries all filters, starting with renderers, to see if any filter exports a clock (by providing an IReferenceClock interface). The filter graph manager will choose the first filter that it finds, providing that filter is connected to an upstream source. If no connected filters are found, the first IReferenceClock filter is used. Typically, this will be the audio rendering filter. If no filter exports a clock, the filter graph manager uses a system clock.

3>     filter 的消息返回给应用程序

当某个事件发生时,比如数据流结束,产生一个错误等,Filter就给Filter图表管理器发送一个事件通知。Filter图表管理器处理其中的一部分事件,另一部分交给应用程序处理。如果图表管理器没有处理一个filter事件,它就把事件通知放入到一个队列中,图表管理器也可以将自己的事件通知放进队列中。

应用程序可以自己处理队列中的事件,dshow中的事件通知就和windows的消息机制差不多,filter,图表管理器和应用程序通过这种机制就可以互相通信。

Filter图表管理器暴露了三个接口用来处理事件通知

IMediaEventSink Filter用这个接口来post事件,There is no need to implement this method because it is implemented by the filter graph manager.

IMediaEvent:应用程序利用这个接口来从队列中查询消息, The filter graph manager implements this interface.

IMediaEventEx:是imediaevent的扩展。

Filter都是通过调用图表管理器的 IMediaEventSink::Notify方法来通知图表管理器某种事件发生。事件通知包括一个事件code,这个code不仅仅代表了事件的类型,还包含两个DWORD类型的参数用来传递一些其他的信息。

应用程序通过调用图表管理器的IMediaEvent::GetEvent方法来从事件队列中获取事件。如果有事件发生,该函数就返回一个事件码和两个参数,如果没有事件,则一直阻塞直到有事件发生和超过某个时间。调用GetEvent函数后,应用程序必须调用IMediaEvent::FreeEventParams来释放事件码所带参数的资源。例如,某个参数可能是由filter graph分配的内存。

下面的代码演示了如何从事件队列中提取事件

long evCode, param1, param2;

HRESULT hr;

while (hr = pEvent->GetEvent(&evCode, &param1, &param2, 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。如果图表管理器对某个事件码没有缺省的处理,调用这两个函数是不起作用的。

具体见接口IMediaEvent, IMediaEventSink.

见附录:事件是如何发生的。

4>     提供方法用来建立 filter graph

接口IGraphBuilderIGraphFilter两个接口中存在方法.

 

附录:-----事件是如何发生的

为了处理事件,应用程序需要一种机制来获取正在队列中等待的事件。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, &param1, &param2, 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;

         }

}

}

B, 接口与组件类的关系

 1>CFilterGraph:  IFilterGraph,

IGraphBuilder(继承于IFilterGraph),

IMediaEventSink,

IMediaEvent(EX)

 

2>Filter Graph ManagerFilter链表的管理:

使用IFilterGraph的函数AddFilter函数实现管理.

3>应用程序对Filter Graph Manager的命令发送:

 IBaseFilter实例传入IFilterGraph.AddFIlter函数中,

 通过IBaseFilter获取IMediaFilter实例实现命令传送.IMediaFilter继承于IBaseFilter

4>时钟

 CBaseFIlter:  IBaseFilter,

              IReferenceClock,

              IMediaFilter

5>通知

 外部传入的IBaseFilter实例调用CFilterGraph内对IMediaEventSinkIMediaEvent(EX)接口函数的调用实现通知.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值