迎风向前,是唯一的方向
移动平台开发:iPhone,Brew等。多媒体方面开发,涉及DirectShow,Direct3D,ffmpeg等

导航

统计
  • 随笔 - 16
  • 文章 - 6
  • 评论 - 2
  • 引用 - 0
公告
 
DirectShow中用好智能连接

    前段时间,在做一个播放器时,内存泄漏很严重。为了达到要求,Filter Graph要按照事先已确定的一种链路来建立,所以刚开始时我在程序中将Filter逐个进行连接,即创建一个,再连接一个,这样虽然能构建出事先确定的链路,但很麻烦,代码冗长,而且在每打开一个媒体文件时,因为上一次得到的接口难以释放彻底,会造成严重的内存泄漏。后来,我将程序改为智能连接,就好多了。大体过程为:先在Graph中加入少部分Filter,再智能连接,最后对部分地方进行修改,增减Filter,这样一来,程序简单好理解,并且也避免了内存泄漏问题!当初为了解决内存泄漏问题,花了很大劲,所以将我的一些经验感受写下来,以下的都是以典型的打开硬盘媒体文件的情况为例。希望对自己有一些收获,对大家能有一些价值,也希望借此抛砖引玉!

一、 逐个Filter进行连接
      在GraphEdit中测试好Filter的连接方式后,就在程序中按GraphEdit里的方式,逐个加入并连接这个Filter,直到连上Renderer为止!刚开始我采用的也是这种方法,写了一个函数:
      HRESULT AppendFilter(IBaseFilter *pPrior, GUID FilterGUID,IBaseFilter **ppLast)。
      这个函数的作用是,在pPrior后面连上指定GUID的Filter,新Filter的值通过ppLast保存。这样要连上后面的Filter,只要指定不同的GUID,调用这个函数就可以了。要按指定链路连好一个Filter Graph,就要多次调用这函数。这样做的好处是,其思路便于理解,对照GraphEdit中测试好的链路,一目了然。但缺点是连接方式是固定死了的,如果媒体文件适合用不同的连接方式,则使用这种方法就失败返回;要涉及到指向每个Filter的指针,多次由自己处理COM接口的获取、释放,稍有不慎就会带来错误,易造成内存泄露或程序崩溃(这两个问题都是我很头疼的);程序改动麻烦,因为要连接哪个Filter都是指定了的,而连接方式发动改变,往往涉及到多个Filter,程序中作相应的改动,就要把涉及到的AppendFilter的Filter参数都改过。

二、采用智能连接
      采用智能连接的好处是显而易见的,程序较简单、较易于控制,也能有效避免内存泄露,程序更稳键等。但问题在于如何让智能连接按照要求的链路构造出Filter Graph。刚开始我对智能连接的理解,是很简单的。后来发现,通过适当的方式,可以达到这个目的。就以下几个方面来说明:

★ 加入一、两个Filter的简单情况
      举个例子,要加入一个视频叠加Filter。首先获取IGraphBuilder指针,再获取这个Filter的指针和用于处理视频的接口指针,然后就调用IgraphBuilder::AddFilter()加入这个Filter,最后智能连接(调用RenderFile()),因为它是视频叠加Filter,所以在构造出的Graph中这个Filter就连在了正确的视频一路,在视频解码Filter之后、视频渲染Filter之前。这样只需要涉及到Filter指针和处理视频叠加的接口指针就可以了,比逐个连接涉及到所有Filter的指针轻松多了。如果是加入一个音频处理Filter或同时加入一个音频处理Filter、一个视频处理Filter,这两个Filter也能连在正确的位置。
      还是以加入一个视频叠加Filter来说明,如果源Filter需要用指定的Filter,如读取CD光盘的源Filter,那就在加入源Filter和视频处理Filter后,以源Filter的输出Pin为参数,调用RenderPin(),也可以连在正确的位置。如果是用到指定的分离Filter或视频解码Filter,那么在加入分离Filter或视频解码Filter后,再调用RenderPin(),也能构建出按照要求的Graph。

★ 加入多个相同类型的Filter(针对转换Filter而言)
      按我的理解,智能连接是这样的,首先采用Graph中已有的Filter试连,如果不行,再从系统Filter里选取匹配的、最合适的Filter进行试连,直到连接成功或找不到合适的Filter失败返回。所以只要Graph中有不同类型的、可接受同一媒体类型的Filter,则采用智能连接,每个Filter都可连在正确的位置。但如果是连入两个或两个以上相同类型的Filter,又如何保证按照指定的顺序呢?
Merit值可以影响到智能连接中Filter被使用的可能性,Merit值越大,被使用可能性越大。但并不能保证一定会使用这个Filter,而且也不是Merit值越大越好,有可能会扰乱正常的智能连接。加入多个相同类型的Filter,这是属于比较复杂的情况,毕竟碰到的时候不多,我采用的方式是,先加入一个Filter,智能连接成功后再插入第二个Filter,这样程序也比较简洁,也能按照要求构建出Filter Graph。
如果是自己写的Filter,需要完成多个功能,尽量把多个功能集成在一个Filterf中完成,这样也省去了连接多个Filter的麻烦,也可减少程序开销。我曾写过一个音频处理Filter,具有声道切换、音量平衡、音频均衡器的功能,这三个功能可以单独在在三个Filter实现,但也可以在同一个Filter中实现(只需要做一些不复杂的处理),为什么不在一个Filter中实现呢?

★ 得到程序功能所需的接口指针
      做一个媒体播放的程序,一般来讲都需要这些功能和接口:视频控制(IVideoWindow)、音频控制(IBasicAudio)、媒体播放控制(IMediaControl、IMediaSeeking)等。上述接口可以通过IGraphBuilder获取,前两个也可以通过Filter获取。IVideoWindow可通过Video Renderer获取,IBsicAudio可通过Audio Renderer获取。通过IGraphBuilder可以得到IVideoWindow,但如果程序中有两个视频播放的窗口,又怎么分别得到呢,就从Video Renderer上分别获取,先加入两个Video Renderer,并分别得到IVideoWindow,再智能连接,这样智能连接时就会采用Graph中已有的Video Renderer。(在实际中,情况还复杂一点,可能还需要明确这两个Video Renderer是连在哪一路上的,那就根据实际情况来解决,在这里就不赘述了)。

三、总结
      为什么在智能连接中不同类型的Filter会连在各自的位置上呢?这是因为这些Filter能接受的媒体类型不同。一般来讲,从源Filter出来的数据流,只简单的带有属于哪种编码格式信息,其它信息都是未知的,所以音/视频解码Filter不能用,音/视频处理Filter也不能用,只有分离Filter可能匹配这种媒体类型,所以只会试连分离Filter。而从分离Filter出来的数据分为音、视频两路,每路的数据流都加上了是音频还是视频的信息,但没有标明这是何种音频或何种视频的信息,所以可以被音/视频解码器Filter接受,但不会被音/视频处理Filter接受。而从解码Filter出来的数据流带上了是何种格式的音/视频媒体类型的信息,所以能被音/视频处理Filter接受,再往后就是Renderer了。自己写的Filter,在CheckInputType()中进行媒体类型判断,在这里判断能接受何种媒体类型,使自己连在适合的位置。
      在不同的Windows系统中,运行相同的智能连接程序,得到的结果不一定相同,因为不同的系统中,已有的、注册了的Filter很可能不同。所以要保证,在不同的系统中运行程序效果相同,也要关注系统的状况。

呵呵,过年了,恭祝大家新年快乐,工作顺利!



Cyrys  
2008-2-6

posted on 2008-03-18 15:36 cyrys 阅读(...) 评论(...)  编辑 收藏