基本名词:filter、pin、filter Graph
DirectShow 使用模块化体系结构,其中每个处理阶段都由名为filter
的 COM 对象完成。 DirectShow 提供了一组标准filter
供应用程序使用,开发人员可以编写自己的自定义filter
来扩展 DirectShow 的功能。每个filter
都连接到一个或多个其他filter
。 连接点也是 COM 对象,称为 pin
。 filter
使用pin
把数据从一个filter
移动到下一个filter
中。在 DirectShow 中,一组filter
称为 filter Graph
。
filter的状态
filter
有三种可能的状态:运行、停止和暂停。 filter
运行时,它会处理媒体数据。 停止时,它将停止处理数据。 暂停状态用于在运行前提示数据。
filter
关系图管理器按上游顺序执行所有状态转换,从呈现器开始,然后向后工作到源filter
。 必须进行此排序,以防止删除MediaSample
并防止图形死锁。 最重要的状态转换是在暂停和停止之间转换:
已停止-->暂停:当每个filter
暂停时,它便准备好从下一个filter
接收MediaSample
。 源filter
是最后一个暂停的filter
。 它创建流式处理线程并开始传送MediaSample
。 由于所有下游filter
都已暂停,因此没有filter
会拒绝任何MediaSample
。 filter
图形管理器不会完成转换,直到图形中的每个呈现器都收到MediaSample
((实时源除外),如前面) 所述。
暂停-->停止:当filter
停止时,它会释放它保存的任何MediaSample
,从而取消阻塞在 GetBuffer 中等待的任何上游filter
。 如果filter
正在等待 Receive 方法中的资源,它将停止等待并从 Receive 返回,这会取消阻塞调用filter
。 因此,当 Filter Graph 管理器停止下一个上游filter
时,该filter
不会在 GetBuffer 或 Receive 中被阻塞,并且可以响应 stop 命令。 上游filter
可能会在获取停止命令之前提供一些额外的MediaSample
,但下游filter
只是拒绝它们,因为它已经停止。
filter的分类
filter
可以分为多个大类:
- 源
filter
将数据引入。 数据可能来自文件、网络、相机或其他任何位置。 每个源filter
处理不同类型的数据源。 - 转换
filter
采用输入流、处理数据并创建输出流。 编码器和解码器是转换filter
的示例。 - 呈现器
filter
位于链的末尾。 他们接收数据并将其呈现给用户。 例如,视频呈现器在显示器上绘制视频帧、音频呈现器将音频数据发送到声音卡、文件编写器将数据写入文件。 - 拆分器
filter
将输入流拆分为两个或多个输出,通常在此过程中分析输入流。 例如,AVI 拆分器将字节流分析为单独的视频流和音频流。 - 复用
filter
采用多个输入,并将其合并到单个流中。 例如,AVI 复用器执行 AVI 拆分器反运算。 它采用音频和视频流,并生成 AVI 格式的字节流。
所有 DirectShowfilter
都公开 IBaseFilter 接口,所有pin
都公开 IPin 接口。
pin的作用
filter
跨pin
连接提供数据。 数据从一个filter
的输出pin
移动到另一个filter
的输入pin
。 输出pin
传递数据的最常见方法是对输入调用 IMemInputPin::Receive
方法,但也有一些其他机制。
根据filter
,媒体数据的内存可以通过各种方式分配:在堆上、在 DirectDraw中、使用共享 GDI 内存或使用某种其他分配机制。 负责分配内存的对象称为 分配器,该分配器是公开 IMemAllocator 接口的 COM 对象。
当两个pin
连接时,其中一个pin
必须提供分配器。 DirectShow 定义一系列方法调用,用于建立哪个pin
提供分配器。 pin
还就分配器将创建的缓冲区数和缓冲区大小达成一致。
在流式处理开始之前,分配器会创建缓冲区池。 在流式处理期间,上游filter
使用数据填充缓冲区,并将其传递到下游filter
。 但是,上游filter
不会为下游filter
提供指向缓冲区的原始指针。 相反,它使用称为 MediaSample
的 COM 对象,分配器创建这些对象来管理缓冲区。 MediaSample
公开 IMediaSample 接口。 MediaSample
包含:
- 指向基础缓冲区的指针
- 时间戳
- 各种标志
- (可选)媒体类型
时间戳定义呈现器filter
用于计划呈现的呈现时间。
标志指示自上一个示例以来数据中是否存在中断之类的情况。
媒体类型为filter
提供了一种在流中更改格式的方法。 通常,该示例没有媒体类型,这表示格式自上一个示例以来没有更改。
当filter
使用缓冲区时,它会保留样本的引用计数。 分配器使用引用计数来确定何时可以重复使用缓冲区。 这可以防止filter
覆盖另一个filter
仍在使用的缓冲区。 样本不会返回到分配器的可用样本池,直到每个filter
释放它。
filter Graph
Filter Graph 管理器是一个 COM 对象,用于控制filter Graph
中的一组filter
。 它执行许多功能,包括以下内容:
- 协调
filter
之间的状态更改。 - 建立引用时钟。
- 将事件传达回应用程序。
- 为应用程序提供生成
filter Graph
的方法。
此处简要介绍了其中每个函数。 可在文档的其他地方找到详细信息。
状态更改。filter
中的状态更改必须按特定顺序发生。 因此,应用程序不会直接向filter
发出状态更改命令。 相反,它会向 Filter Graph 管理器提供单个命令,后者将命令分发给每个filter
。 查找的工作方式与此类似:应用程序向 Filter Graph 管理器提供一个 seek 命令,后者将其分发到filter
。
参考时钟。 图中的所有filter
都使用相同的时钟,称为 引用时钟。 引用时钟可确保同步所有流。 应呈现视频帧或音频示例的时间称为 演示时间。 表示时间相对于参考时钟进行测量。 Filter Graph 管理器选择引用时钟,通常是声音卡上的时钟或系统时钟。
图形事件。 Filter Graph 管理器使用事件队列通知应用程序filter Graph
中发生的事件。 此机制类似于 Windows 消息循环。
图形生成方法。 Filter Graph 管理器为应用程序提供了向图形添加filter
、将filter
连接到其他filter
以及断开filter
连接的方法。
Filter Graph 管理器不处理的一个函数是将数据从一个filter
移动到下一个filter
。 这由filter
本身通过其pin
连接完成。 处理始终在单独的线程上进行。
filter是如何与音频和视频硬件交互的
所有 DirectShow filter
都是用户模式软件组件。 为了使内核模式硬件设备(如视频捕获卡)加入 DirectShow filter
图,设备必须表示为用户模式filter
。 此函数由 DirectShow 提供的专用“包装器”filter
执行。 这些filter
包括 音频捕获 filter
、 VFW 捕获 filter
、 电视调谐器 filter
、 电视音频 filter
和 模拟视频横杠 filter
。 DirectShow 还提供名为 KsProxy 的filter
,该filter
可以表示任何类型的 Windows 驱动程序模型 (WDM) 流式处理设备。 硬件供应商可以通过提供 Ksproxy 插件(由 KsProxy 聚合的 COM 对象)来扩展 KsProxy 以支持自定义功能。
包装器filter
公开表示设备功能的 COM 接口。 应用程序使用这些接口向filter
传递和从filter
传递信息。 filter
将 COM 方法调用转换为设备驱动程序调用,在内核模式下将该信息传递给驱动程序,然后将结果转换回应用程序。 电视调谐器、电视音频、模拟视频交叉栏和 KsProxy filter
通过 IKsPropertySet 接口支持自定义驱动程序属性。 VFW 捕获filter
和音频捕获filter
不是以这种方式扩展的。
对于应用程序开发人员,包装器filter
使应用程序能够像控制任何其他 DirectShow filter
一样控制设备。 无需特殊编程;与内核模式设备通信的详细信息封装在filter
中。
Windows 设备视频
VFW 捕获filter
支持 Windows (VfW) 捕获卡的早期视频。 当目标系统上存在 VfW 卡时,可以使用 DirectShow 系统设备枚举器发现它并将其添加到filter
图中。 有关详细信息,请参阅 枚举设备和filter
。
音频捕获和混音设备 (声卡)
较新的声卡具有用于麦克风和其他类型的设备的输入插孔。 通常,这些卡还具有板载混合功能,用于控制每个输入的音量、高音和低音。 在 DirectShow 中,声音卡的输入和混音器由音频捕获filter
包装。 可以使用系统设备枚举器发现每个声音卡。 若要查看系统中的声卡,请运行 GraphEdit 并从“音频捕获源”类别中进行选择。 该类别中的每个filter
都是音频捕获filter
的单独实例。 (请参阅 使用 GraphEdit.)
WDM 流式处理设备
较新的硬件解码器和捕获卡符合 Windows 驱动程序模型 (WDM) 规范。 这些设备比 VfW 设备具有更大的功能。 WDM 视频捕获卡可以支持 VfW 下不可用的功能,包括捕获格式的枚举、视频参数(如色调和亮度)的编程控制、编程输入选择和电视调谐器支持。
为了支持 WDM 流式处理设备,DirectShow 提供了 KsProxy filter
(ksproxy.ax) 。 KsProxy被称为“瑞士军刀过滤器”,因为它做许多不同的事情。 filter
上的pin
数以及filter
公开的 COM 接口数取决于基础驱动程序的功能。 KsProxy 不会显示在名称“KsProxy”下的filter
图中。它始终采用在注册表中找到的设备的友好名称。 若要查看系统上的 WDM 设备,请运行 GraphEdit 并从 WDM 流式处理类别中进行选择。 即使系统上只有一个 WDM 卡,该卡也可能包含多个设备。 每个设备都表示为单独的filter
,其中每个filter
实际上都是 KsProxy。
应用程序使用系统设备枚举器查找系统上的 WDM 设备名字对象。 KsProxy 通过在名字对象上调用 BindToObject 来实例化。 由于 KsProxy 可以表示所有类型的 WDM 设备,因此它必须查询驱动程序以确定驱动程序支持的属性集。 属性集是 WDM 驱动程序以及某些用户模式filter
(如 MPEG-2 软件解码器)使用的数据结构的集合。 KsProxy 将自身配置为公开与这些属性集对应的 COM 接口。 KsProxy 将 COM 方法调用转换为属性集,并将其发送到驱动程序。 硬件供应商可以通过提供插件来扩展 KsProxy,插件是供应商特定的接口,用于公开设备的特殊功能。 所有这些详细信息都对应用程序隐藏。 应用程序通过 KsProxy 控制设备,其方式与任何其他 DirectShow filter
的方式相同。
内核流式处理
WDM 设备支持内核流式处理,其中数据完全在内核模式下流式传输,而无需切换到用户模式。 在内核模式和用户模式之间切换计算成本高昂;内核流式处理允许高比特率,而不会给主机 CPU 带来负担。 基于 WDM 的filter
可以使用内核流式处理将多媒体数据从一个硬件设备直接传递到另一个硬件设备,无论是在同一个卡上还是在不同的卡,而无需将数据复制到系统的main内存中。
从应用程序的角度来看,数据似乎从一个用户模式filter
移动到下一个用户模式filter
。 实际上,数据可能根本不会进入用户模式,而是可以直接从一个内核模式设备流式传输到另一个内核模式设备,直到在视频图形上呈现卡。 某些方案(如捕获到文件)要求数据在某个时间点从内核模式传递到用户模式。 但是,此开关不一定要求将数据复制到内存中的新位置。