3.3. 构建Filter Graph
3.3.1. 用于构建Graph的组件
DirectShow提供了一系列用于构建filter graph的组件,包括:
*Filter Graph Manager。这个对象用于控制filter graph,支持IGraphBuilder、IMediaControl和IMediaEventEx等许多接口。所有的directshow应用程序都需要在某些地方用到这个对象,虽然在有些情况下,是其它的对象为应用程序创建了filter graph manager。
*Capture Graph Builder。这个对象为构建filter graph提供附加的方法。它最初是为构建提供视频采集的graph而设计的(这正是它的名字由来),但是对于构建许多另外类型的filter graph也是很有用的。它支持ICaptureGraphBuilder2接口。
*Filter Mapper和System Device Enumerator。这些对象用于查找在系统中注册的或代表硬件驱动的filter。
*DVD Graph Builder。这个对象构建用以回放和导航DVD的filter graph。它支持IDvdGraphBuilder接口。基于脚本的应用程序能够使用MSWebDVD ActiveX控件来控制DVD回放。
*Video Control。WinXP提供这个ActiveX控件,用于操纵directshow中的数据和模拟电视。
智能连接(Intelligent Connect)
智能连接这个术语覆盖了一系列Filter Graph Manager用于构建所有或部份filter graph的算法。任何时候,当Filter Graph Manager需要添加filter来完成graph时,它大致做以下几件事情:
1.如果有一个filter存在于graph中,而且这个filter有至少一个没有连接的input pin,Filter Graph Manager试着去试用这个filter。
2.否则,Filter Graph Manager在已注册的filter中寻找连接时可以接受合适的媒体类型的filter。每一个filter都注册有一个Merit值,这个值用以标记哪个filter最容易被Filter Graph Manager选中来完成graph。Filter Graph Manager按Merit值的顺序来选择filter,Merit值越大,被选中的机会越大。对于每种流类型(如音频、视频、MIDI),默认的renderer具有一个很高的Merit值,解码器同样是,专用filter具有低Merit值。
如果Filter Graph Manager因选择的filter不合适而被困,它会返回来尝试另外的filter组合。
3.3.2 Grap构建概述
创建一个filter graph,从创建一个Filter Graph Manager实例开始:
IGraphBuilder* pIGB;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph,
NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder,(void **)&pIGB);
Filter Graph Manager支持下列Graph构建方法:
*IFilterGraph::ConnectDirect,在两个pin之间进行直接连接,如果连接失败,则返回失败
*IFilterGraph::Connect,连接两个Pin,如果可能的话,直接连接它们,否则,在中间加入其它的filter来完成连接。
*IGraphBuilder::Render,从某个输出Pin处开始完成余下的graph构建。该方法会自动在输出pin后面添加必须的filter,直到renderer filter为止。
*IGraphBuilder::RenderFile,构建一个完整的文件回放graph。
*IGraphBuilder::AddFilter,将一个filter添加到graph中。它不连接filter,并且在调用此方法前,filter必须已经被创建。创建filter可以是用CoCreateInstance方法或使用Filter Mapper或系统设备枚举器(System Device Enumerator)。
这些方法提供了三种构建graph的途径:
1.filter graph manager构建整个graph
2.filter graph manager构建部分graph
3.应用程序构建整个graph
Filter Graph Manager构建整个graph
如果你仅仅是想回放一个已知格式的文件,如AVI、MPEG、WAV或MP3,使用RenderFile方法。
RenderFile方法首先寻找注册在系统中能分析源文件的filter,它使用协议名(如http://),文件扩展名或文件的头几个字节来决定选择哪一个源filter。
Filter Graph Manager使用一个迭代过程来完成余下的graph构建。在这个迭代过程中,它逐个列出filter的输出pin上支持的媒体类型,并搜索哪个已注册的filter的输入Pin接受该媒体类型。它使用一系列的规则来缩小filter的范围并排定优先顺序:
*filter类别(category)标识的filter的一般功能
*媒体类型描述filter能在接受或能输出哪种数据类型
*merit值决定filter被尝试的次序。如果两个filter具有相同的filter类别并且同时支持相同的输入类型,Filter Graph Manager选择merit值大的那一个。一些filter故意给出一个小merit值是因为它是为特殊用途设计的,仅能由应用程序来将其添加到graph。
Filter Graph Manager使用Filter Mapper对象来搜索已注册的filter。
每个filter被添加时,filter graph manager试着将其与前一个filter的输出pin连接。它们协商决定他们是否能连接,如果能,哪一种媒体类型被用来连接。如果新filter不能连接,filter graph manager丢弃它并尝试别一个,这个过程一直继续到每个流都被render为止。
Filter Graph Manager构建部分graph
如果不仅仅是播放一个文件,那么你的应用程序就必须做一些graph的构建工作。比如,一个视频采集应用程序必须先选择一个source filter并将其添加到graph中去。如果你需要将数据写入到一个AVI文件中,你必须添加一个AVI Mux和File Write filter。不过,也经常有可能让filter graph manager来完成整个graph,比如,你可以通过Render方法来render一个pin进行预览。
应用程序构建整个graph
在某些场合,你的应用程序需要添加和连接每个filter来构建graph。在这种情况下,你很可能明确地知道哪些filter需要加到graph中去。使用这种方式,应用程序通过调用AddFilter方法添加每个filter,然后枚举filter上的pin,调用Connect或ConnectDirect来连接它们。
3.3.3. 智能连接
智能连接是filter graph manager用以构建filter graph的机制。它包含了一系列相关的用以选择filter和将它们添加到graph中去的算法。作为应用程序开发者,你并不需要很具体地了解智能连接的细节。如果你在构建某个filter graph时遇到问题并希望能解决它,或者你正在编写你自己的filter并希望它能自动地被graph构建,请阅读这一节。
智能连接涉及以下IGraphBuilder方法:
*IGraphBuilder::Render
*IGraphBuilder::AddSourceFilter
*IGraphBuilder::RenderFile
*IGraphBuilder::Connect
Render方法构建一部分graph,它从一个尚未连接的输出pin开始顺着数据流的方向往下,添加必要的filter,起始的那个filter必须已被添加到了graph中。Render方法每一步都搜索一个能够连接到前一个filter的filter,如果新连接上的filter有多个输出pin,数据流能自动分流,搜索直到每个流都被renderer为止。如果Render方法搜索到的filter无法使用,它会返回去尝试另一个filter。
要连接每一个输出pin,Render方法做以下工作:
1.如果pin支持IStreamBuilder接口,Filter Graph Manager让pin的IStreamBuilder::Render方法来完成整过程。通过暴露这个接口,pin承担了构建graph剩余部分的全部工作。但是,只有很少数的filter支持此接口。
2.Filter Graph Manager尝试使用任何在缓存中的filter。在智能连接的整个过程中,filter graph manager可以在早期将filter缓存起来。
3.如果filter graph包含了任何有未连接的输入pin的filter,filter graph manager会将其当作下一个filter来尝试连接。你可以通过在调用Render之前添加特定的filter来强制让Render方法来尝试这个filter。
4.最后,filter graph manager使用IFilterMapper2::EnumMatchingFilters方法在所有注册的filter中寻找,依据已注册的媒体类型列表来逐个试着匹配输出pin的各个媒体类型(按优先级高低排列)。
每个已注册的filter都有一个merit值,这是一个用来表示filter优先级的数字,最大优先级越高,EnumMatchingFilters方法返回的filter集依据merit值来排列,直至最小的merit值MERIT_DO_NOT_USE+1,它忽略merit为MERIT_DO_NOT_USR或更小的filter。filter也通过GUID来归类,类别本身也有merit值,EnumMatchingFilters方法忽略任何merit值为MERIT_DO_NOT_USE或更小的类别,即使在那个类别中的filter有较高的merit值。
总结一下,Render方法以下列步骤尝试filter
1.使用IStreamBuilder
2.尝试被缓存的filter
3.尝试已添加在graph中的filter
4.在已注册的filter中寻找
AddSourceFilter方法添加一个能render特定文件的source filter。首先,它依据协议名(如Http://)、文件扩展名、或文件头在已注册的filter中寻找匹配的那个。如果此方法定位到了一个合适的source filter,它便立刻创建一个这个filter的实例,并将其添加到graph中,然后调用filter的IFileSourceFilter::Load方法。
RenderFile方法依据一个文件名来构建一个默认的回放graph,在其内部,RenderFile方法调用AddSourceFilter来定位source filter,并且用Render来构建Graph的余下部分。
Connect方法将输出pin连接到输入pin上去,这个方法自动添加必要的中间filter到graph中去,使用在Render方法中描述的那一系列算法:
1.使用IStreamBuilder
2.尝试被缓存的filter
3.尝试已添加在graph中的filter
4.在已注册的filter中寻找