|
在DirectShow系统概述中介绍了DirectShow的总体系统框架。本文我们从程序员的角度,进一步深入探讨一下DirectShow的应用以及Filter的开发。
在这之前,笔者首先要特别提一下微软提供的一个Filter测试工具——GraphEdit,它的路径在DXSDK\bin\DXUtils\GraphEdit.exe。(如果您还没有安装DirectX SDK,请到微软的网站上去下载。)通过这个工具,我们可以很直观地看到Filter Graph的运行及处理流程,方便我们进行程序调试。(如果您手边就有电脑,还等什么,马上体验一下吧:运行GraphEdit,执行File->Render Media File…选择一个媒体文件;当Filter Graph构建成功后,按下工具栏的运行按钮;您就能看到刚才选择的媒体文件被回放出来了!看到了吧,写一个媒体播放器也就这么回事!)
接下去,我们开讲Filter的开发。
学习DirectShow Filter的开发,不外乎以下几种方法:看帮助文档、看示例代码和看SDK基类源代码。看帮助文档,应着重于总体概念上的理解;看示例代码应与基类源代码的研究同步进行,因为自己写Filter,关键的第一步是选择一个合适的Filter基类和Pin的基类。对于Filter的把握,一般认为要掌握以下三方面的内容:Filter之间Pin的连接、Filter之间的数据传输以及流媒体的随机访问(或者说流的定位)。下面就开始分别进行阐述。
所谓的Filter Pin之间的连接,实际上是Pin之间Media Type(媒体类型)的一个协商过程。连接总是从输出Pin指向输入Pin的。要想深入了解具体的连接过程,就必须认真研读SDK的基类源代码(位于DXSDK\samples\Multimedia\DirectShow\BaseClasses\amfilter.cpp,类CBasePin的Connect方法)。连接的大致过程为,枚举欲连接的输入Pin上所有的媒体类型,逐一用这些媒体类型与输出Pin进行连接,如果输出Pin也接受这种媒体类型,则Pin之间的连接宣告成功;如果所有输入Pin上枚举的媒体类型输出Pin都不支持,则枚举输出Pin上的所有媒体类型,并逐一用这些媒体类型与输入Pin进行连接。如果输入Pin接受其中的一种媒体类型,则Pin之间的连接到此也宣告成功;如果输出Pin上的所有媒体类型,输入Pin都不支持,则这两个Pin之间的连接过程宣告失败。
有一点需要注意的是,上述的输入Pin与输出Pin一般不属于同一个Filter,典型的是上一级Filter(也叫Upstream Filter)的输出Pin连向下一级Filter(也叫Downstream Filter)的输入Pin。如下图所示:

当Filter的Pin之间连接完成,也就是说,连接双方通过协商取得了一种大家都支持的媒体类型之后,即开始为数据传输做准备。这些准备工作中,最重要的是Pin上的内存分配器的协商,一般也是由输出Pin发起。在DirectShow Filter之间,数据是通过一个一个数据包传送的,这个数据包叫做Sample。Sample本身是一个COM对象,拥有一段内存用以装载数据,Sample就由内存分配器(Allocator)来统一管理。已成功连接的一对输出、输入Pin使用同一个内存分配器,所以数据从输出Pin传送到输入Pin上是无需内存拷贝的。而典型的数据拷贝,一般发生在Filter内部,从Filter的输入Pin上读取数据后,进行一定意图的处理,然后在Filter的输出Pin上填充数据,然后继续往下传输。下面,我们就具体阐述一下Filter之间的数据传送。
首先,大家要区分一下Filter的两种主要的数据传输模式:推模式(Push Model)和拉模式(Pull Model)。参考图如下:


所谓推模式,即源Filter(Source Filter)自己能够产生数据,并且一般在它的输出Pin上有独立的子线程负责将数据发送出去,常见的情况如代表WDM模型的采集卡的Live Source Filter;而所谓拉模式,即源Filter不具有把自己的数据送出去的能力,这种情况下,一般源Filter后紧跟着接一个Parser Filter或Splitter Filter,这种Filter一般在输入Pin上有个独立的子线程,负责不断地从源Filter索取数据,然后经过处理后将数据传送下去,常见的情况如文件源。推模式下,源Filter是主动的;拉模式下,源Filter是被动的。而事实上,如果将上图拉模式中的源Filter和Splitter Filter看成另一个虚拟的源Filter,则后面的Filter之间的数据传输也与推模式完全相同。
上一篇:成长的烦恼:进一步理解设计模式
下一篇:正则表达式从入门到精通
|