一、 总览:
Graph、Filter、Pin、Simple
Graph:可以理解为媒体处理的流程图。
Filter:可以理解为媒体处理流程图中的一个步骤。
Pin:可以理解为媒体处理各个步骤之间的数据流节点。
Simple:可以理解为各个形态的数据。
Filter的分类:
· source filter:将数据从源(比如媒体文件)引入Graph。
· transform filter:数据流入、改变、流出。
· Renderer filters:把最终结果展现给用户。
· splitter filter:比如把一个媒体流分解为视频和音频。
· mux filter:和splitter filter相反。
1. Graph Manager
主要对处于同一个Graph的Filter(s)进行统一的管理。
比如:
· 各个Filter的状态切换
· 建立同步时钟
· 事件回发
· 创建
为什么要统一管理状态切换?
因为处于同一Graph中的各个Filter的状态切换往往需要遵循严格的先后顺序。所以一般通过发送命令给Graph Manager的方式进行各Filter的状态变更。
为什么要建立同步时钟?
比如声像需要同步。
2. Media Types
结构体AM_MEDIA_TYPE定义了媒体类型。
主要包含如下结构:
· Major type: 由一个GUID来表示。通常包含音视频、未知流、MIDI等等。
· Subtype: 由一个GUID来表示。Major type为视频,则子类型可以包括RGB-24, RGB-32, UYVY
· Format block: 说明图形尺寸、帧率等信息。如果Major type为视频,sub type为RGB-24,则Format block的信息会被自动辨识。
3. Sample和Allocator
需要注意的是,各个filter之间并不直接传送它们各自进行处理的数据的指针。它们通过一个暴露IMemAllocator接口的Com组件来分配内存。填充了数据的内存被封装到Sample里面。各个Filter真正使用的是Sample。Sample通常包含:
· 内存指针
· 时间戳
· 标识
· 媒体类型(可选)
这里当一个Filter使用Sample的时候,它同时掌握这个Sample的引用计数,这样就有效杜绝了资源争用现象的发生。
4. Filter Graph中的硬件
硬件被封装到Filter中,任何与硬件的交互都转化为与Filter的交互。
二、 Graph-Building 组件
Filter Graph Manager.
Capture Graph Builder:设计的初衷是视频捕获,但是可以衍生很多用途。
Filter Mapper and System Device Enumerator:枚举可用的filter.
DVD Graph Builder
Video Control
1. 智能拼接
1) 如果在Graph里面有一个没有输入的Filter,那么Graph在完成自己的时候,就会考虑这个Filter。如果有一个已有的Filter的流出恰好与这个没有输入的Filter的流入相匹配,则将这两个Filter连接。
2) Graph在完成自己的过程中也会查找所有注册过的Filter与当前非终点Filter的流出进行匹配。注册的Filter会有一个权值,作为Graph进行连接尝试的优先级依据。
步骤:
1) 使用IStreamBuilder(如果pin实现了这个接口,但大多数情况没有)。(否则2)
2) 查找被缓存的Filter。(否则3)
3) 查找Graph现有的Filter。(否则4)
4) 查找所有注册了的Filter。
2. 关键方法
IFilterGraph::ConnectDirect:直接连接两个Filter,如不成功,返回失败。
IGraphBuilder::Connect:连接两个Filter,如果可能,直接连接,否则通过中间Filter(s)进行连接。
IGraphBuilder::Render:你自己建立了一系列从源开始的Filter,基于这些Filter(s)完成Graph。
IGraphBuilder::RenderFile:从一个文件开始完成Graph.
IFilterGraph::AddFilter:向Graph中添加一个Filter
通过这些方法,你可以:
1、 由Graph Manager建立整个Graph。(通过RenderFile)
2、 由Graph Manager建立部分Graph。(比如你想自己写一个AVI文件,当然也可以通过Render来生成预览)
3、 完全手动建立整个Graph。(需要自己AddFilter,还需要自己Connect)。
三、 Direct Show 数据流概述
1. 关键接口(方法)
IMediaSample:对Filter之间使用的内存的封装。
IMemAllocator::GetBuffer:从allocator获取Buffer(即ImediaSimple的实现对象)
摘要:
由于Render会按照时间戳来Render数据,所以它会一直占用它的上一级Filter流入的Simple,直到时间戳所标识的时间到达。所以当上一级Filter用完了allocator的Simple池中的Simple时,会阻塞而不处理,进而反向影响到更上一级的Filters,从而使它们变为等待的状态。同时由于时间戳对于Render的时间上的要求,所有上级Render都必须在Simple的时间戳标识的时间到达之前处理完自己对于该Simple的动作。
2. Transport(传送)
· Push Model(推送模型):上层filter(pin-out)将处理好的数据推送给下层filter(pin-in)。下层filter在需要数据的时候通过IMemInputPin::Receive来获取数据。
· Pull Model(抓取模型):下层filter(pin-in)在需要数据的时候,通过IAsyncReader异步向上层filter请求数据。(通常用于视频文件的回放)
3. Samples and Allocators
1) 引用计数
上层Filter(pin-out)通过IMemAllocator::GetBuffer向Allocator申请Simple,如果此时没有Simple的引用计数为0,则说明Allocator的Simple池中没有可用的Simple,则GetBuffer的调用会被阻塞。一旦Simple池中出现可用的Simple,则先前阻塞的GetBuffer放行,并获取一个引用计数变为1的Simple。此Simple处理后,传递给下层Filter(pin-in),下层Filter如果在Receive方法中处理Simple,则它与上层Filter的处理处于同一线程中,Simple的引用计数不会变化,如果下层Filter需要通过创建线程异步使用上层推入的Simple进行处理,则该Simple的引用计数会加1,变为2.之后如果上层Filter的推送线程结束,则Simple的引用计数减1,变为1.
2) 提交和撤销Allocator
IMemAllocator::Commit,在被调用之前,所有的GetBuffer无效,
调用IMemAllocator::Decommit之后,所有的GetBuffer调用无效.
4. Filter 状态变化
Filter状态的变化由Graph Manager进行控制。
所有的状态变化都是自低(Render Filter)向上(Source Filter)进行的。比如暂停的时候,Render Filter会首先暂停,这时候Render之上的Filter中都会存有未能推送的Simple,此时相当于在各个Filter之前都堆积了一些等待处理的数据。沿着Render向上的Filter逐个暂停,直到Source。当状态从暂停变化为开始的时候,Render会首先变化为开始状态,处理在它之前堆积的数据,并释放那些被占用的Simple。然后逐步向上直到Source,Source在能够获取Simple之后,数据继续流入Graph,整个Graph进入开始状态。