About DirectShow [2]

       2.5    How Hardware Devices Participate in the Filter Graph
       本节描述 DirectShow 是如何与音频、视频设备进行交互。
 
       2.5.1 Wrapper Filters
       所有的 DirectShow Filters 都是用户组件模式的软件组件。为了把内核模式的硬件设备,比如视频捕捉卡等加入倒 Filter Graph ,设备必须被表示为用户模式 Filter 。这项功能是通过 DirectShow 提供的包装 Filters 来实现的。这些 Filters 包括 Audio Capture Filters, VFW Capture Filters, TV Tuner Filter, TV Audio Filter Analog Video Crossbar Filter. DirectShow 也提供了称为 KsProxy Filter 用来描述任何类型的 WDM 流设备。硬件供应商可以提供聚集了 KsProxy COM 对象 KsProxy Plug-in, 以此扩展 KsProxy 来支持自定义功能,
       这些包装 Filters 暴露了描述设备功能的接口。应用程序使用这些接口与 Filter 交互数据。 Filter COM 函数调用翻译为设备驱动调用,把信息传递到内核的驱动,然后再把结果转化给应用程序。 TV Tuner, TV Audio, Analog Video Crossbar KsProxy 通过 IKsPropertySet 接口支持自定义驱动属性。 VFW Capture Filter Audio Capture Filter 并没有这种扩展。
       对应用程序开发人员,包装 Filter 允许应用程序控制设备像控制其他 DirectShow Filter 一样。我们不需要特殊的编程,这些与内核模式的通信细节都在 Filter 中封装起来。
 
       2.5.2 Video for Windows Devices
       VFW Capture Filter 支持早期的 VFW 捕捉卡。当目标系统上存在 VFW 卡时,它可以被发现,然后通过 DirectShow System Device Enumerator 添加到 Filter Graph 。细节可参考 Enumerating Devices and Filters.
 
 
       2.5.3 Audio Capture and Mixing Devices (Sound Cards)
       新一代的声卡都有麦克风和其他类型设备的输入娱乐工具。通常这些声卡还有控制每个输入音量、立体音和重音的实时混合功能。在 DirectShow 中,声卡的输入和混合器都被 Audio Capture Filter 包装。每个声卡都能被 System Device Enumerator 发现。查看系统的声卡,运行 GraphEdit 并选择 Audio Capture Source 种类。属于此类的每个 Filter 都是 Audio Capture Filter 的一个单独实例。 ( 参考 Using GraphEdit).
 
       2.5.4 WDM Streaming Devices
     新一代硬件解码器和捕捉卡都与 WDM 规范一致。这些设备的功能比 VFW 设备强,可以在 Windows NT/2K Windows 98/ME 移植。 WDM 视频捕捉卡可以支持 VFW 不能支持的特征,包括枚举捕捉格式,编程控制视频参数,比如色调和亮度,编程选择输入端和 TV Tuner 支持。
     为了支持 WDM 流设备, DirectShow 提供了 KsProxy Filter(ksproxy.ax) KsProxy 也称为“ Swiss Army Knife Filter ”因为它的实现的功能很多。此 Filter PINS 数量、暴露的 COM 接口数量,都取决于内在驱动的性能。 KsProxy Filter Graph 中不以名称 KsProxy 出现。而总是采用设备的 Friendly Name, 可以通过注册表读取。查看系统的 WDM 设备,运行 GraphEdit 选择 WDM Streaming 种类。即使系统只有一个 WDM 卡,它也可能包括多个设备。每个设备是以不同的 Filter 表示。每个 Filter 实际上都是 KsProxy.
       应用程序使用 System Device Enumerator 查找系统的 WDM 设备 Monikers KsProxy 被函数 BindToObject 实例化。因为 KsProxy 可以表示所有的 WDM 设备。它必须查询驱动来决定驱动支持那种属性集。属性集是 WDM 驱动使用的数据结构集合,也被一些用户模式的 Filter 使用。 KsProxy COM 调用转译为属性集并发送给驱动。硬件供应商可以提供 Plug-in 扩展 KsProxy Plug-in 是硬件供应商指定暴露、实现特殊设备功能的接口。所有这些都从应用程序隐藏。应用程序通过 KsProxy 控制设备像控制其他 DirectShow Filter 一样。
 
       2.5.5 Kernel Streaming
       WDM 设备支持内核流,这当中所有的数据都是在内核模式流动。永远也不需要转换到用户模式。在内核模式和用户模式切换的计算代价很高。内核模式允许高位率的流而不加重 CPU 负担。基于 WDM Filter 可使用内核流把多媒体数据从一个设备传递到另一个设备,可以是相同卡也可是不同的设备卡,不需要把数据拷贝到系统内存。
       从应用程序角度看,数据好像是从一个用户模式 Filter 移动到下一个。而实际上,数据可能根本没有进入用户模式,而是从一个内核设备直接流向另一个设备,直到被提交到图形卡。对于某些情况,比如捕捉到文件,某些时候要求数据从内核模式传递到用户模式。但是这样的交换并不必要求数据被拷贝到内存的新位置。应用程序开发人员一般不需要关心内核流的细节,除了作为背景信息。参考 Microsoft DDK 的关于 WDM, 内核流和 KsProxy 的相关主题。
 
3.     Building the Filter Graph
       The Filter Graph and Its Component 一节描述了建立 DirectShow Filter Graph 的基本组件。本节检查这些组件是如何创建、连接在一起开始处理数据的。
 
       3.1    Graph-Building Components
       DirectShow 提供了组件用来建立 Filter Graph 。包括:
       · Filter Graph Manager. 它控制 Filter Graph. 支持 IGraphBuiler, IMediaControl, IMediaEventEx 及其他接口。所有的 DirectShow 应用程序在某些时候都使用此对象,尽管在某些情况下 Filter Graph Manager 是由其他对象创建的。
       · Capture Graph Builder. 提供建立 Filter Graphs 的额外方法。它最开始是设计来创建执行视频捕捉的 Graph( 从名称看也是 ) ,但是对一些其他类型的自定义 Filter Graph 也有用。它支持接口 ICaptureGraphBuilder2.
       · Filter Mapper System Device Enumerator. 它们定位注册在用户系统上的 Filters. 或者表示硬件设备。
       · DVD Graph Builder. 建立 DVD 回放和导航的 Filter Graph. 支持 IDvdGraphBuilder 接口。基于脚本的应用程序可使用 MSWebDVD ActiveX 控制进行 DVD 回放。
       ·视频控制。此 ActiveX 控制在 Windows XP 上可用。它在 DirectShow 中处理数字和模拟电视。更多信息可参考 Using the Video Control.
 
       3.1.1 Intelligent Connect
       术语 “Intelligent Connect” 覆盖了 Filter Graph Manager 用来建立全部、部分 Filter Graph 的一个算法集。在 Filter Graph Manager 需要其他 Filters 来完成 Graph 的任何时候,粗略按照如下步骤进行:
       (1) 、如果 Filter Graph 中,并且至少有一个未连接的 PIN Filter Graph Manager 就尝试使用这个 Filter
       (2) 、否则 Filter Graph Manager 在注册表查询能接收媒体类型连接的 Filter 。每个 Filter 在注册表有一个 Merit 值。它能粗略说明在完成 Graph Filter 可能有多大的用处。 Filter Graph Manager 根据 Merit 值的顺序进行尝试。每种流类型(比如音频、视频或者 MIDI ),默认的 Renderer 都有一个较高的 Merit 值。解码器的 Merit 值也比较高。特殊目的 Filter 的值比较低。
     如果 Filter Graph Manager 遇到困难,就返回尝试不同 Filters 的组合。我们可从 Intelligent Gonnect 主题找到具体细节。
 
       3.2    Overview of Graph Building
       创建 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 直接在两个 PINS 上尝试连接。若 PIN 不能连接,函数失败。
       · IFilterGraph::Connect 连接两个 PINS ,如果可能先进行直接连接。否则使用中介 Filter 完成连接。
       · IGraphBuilder::Render 从某输出 PIN 开,建立 Graph 剩余部分。此函数在下一级添加必要的 Filter 直到到达 Renderer Filter.
       · IFilterGraph::AddFilter Filter 添加到 Graph. 并不连接 Filter. 必须在调用函数前创建 Filter
       这些方法提供三种基本的方法建立 Graph:
       (1) Filter Graph Manager 建立整个 Graph
       (2) Filter Graph Manager 建立部分 Graph
       (3) 、应用程序建立整个 Graph
 
       3.2.1 The Filter Graph Manager builds the entire graph
       如果我们想简单播放的已经格式创建的文件,比如 AVI, MPEG, WAV MP3, 使用 RenderFile 函数。 How To Play a File 一节说明了具体如何操作。
       RendererFile 开始在注册表搜索能解析文件的 Source Filter 。它使用协议 ( 比如文件名称中的 http://), 文件扩展名,或者文件中的预定义字节模式来决定使用哪个 Source Filter 。详细信息可参考 Registering a Custom File Type.
       在建立 Graph 剩余部分时, Filter Graph Manager 使用一种交互过程。交互时用 Filter 输出 PIN 支持的媒体类型,然后在注册表查找可以用此类型做输入的 Filter 。它使用几个标准来加快搜索和决定 Filters 的使用次序:
       · Filter Category 标示了 Filter 的基本功能
       ·媒体类型描述了 Filter 可接受的输入或者发送的输出格式
       · Merit 值决定了 Filter 被尝试使用的顺序。如果两个 Filter 属于相同的 Category ,支持相同的输入类型, Filter Graph Manager 就选择 Merit 值高的一个。某些 Filter Merit 设置得比较低,因为它们通常用于特殊用途,应该由应用程序把它们加入到 Graph
       Filter Graph Manager 使用 Filter Mapper 来查找注册表。
       只要添加了每个 Filter 后, Filter Graph Manager 开始把它与前一个 Filter 的输出 PIN 连接。 Filter 进行协商来决定它们是否连接。如果是,再决定连接采用的媒体类型。如果新 Filter 不能连接。 Filter Graph Manager 丢弃它并尝试其他 Filter 。这个过程会一直持续下去直到每个流都被提交。
 
       3.2.2 The Filter Graph Manager builds part of the graph
       当我们不止想简单播放文件时,应用程序必须至少执行一些 Graph 的建立工作。比如视频捕捉应用程序必须选择捕捉 Source Filter 并加入 Graph 。如果把数据写入到 AVI 文件,必须添加 AVI Mux File Writer Filter Graph 中。但是,让 Filter Graph Manager 来完成 Graph 也是可能的。比如我们可以调用 Render 函数提交 PIN 进行预览。
 
       3.2.3 The Application builds the entire graph
       在某些时候,我们的应用程序需要通过添加或者连接每个 Filter 来建立 Graph 。这时,我们应该知道哪个 Filter 应该被加入到 Graph 。使用这种方法,应用程序调用 AddFilter 来添加 Filter 。枚举 Filter PIN ,调用 Connect ConnectDirect 来连接它们。
 
       3.3    Intelligent Connect
       智能连接是 Filter Graph Manager 建立 Graph 的机制。它包含几个选择 Filter 、把 Filter 添加到 Filter Graph 的算法。对于应用程序编程,我们很少需要知道智能连接的细节。如果你建立某些 Filter 有困难,并且想解决问题时,或者是编写自己的 Filter 并且允许支持自动 Graph 建立,可阅读此节。
       智能连接主要涉及到如下几个 IGraphBuilder 的函数:
       · IGraphBuilder::Render
       · IGraphBuilder::AddSourceFiler
       · IGraphBuilder::RenderFile
       · IGraphBuilder::Connect
       Render 函数建立部分 Graph. 它从一个未连接的输出 PIN 开始,并向下工作,必要时添加 Filter. 开始的 Filter 必须已经在 Graph 中。每一步, Render 函数都查找可以连接上一级 Filter Filter 。数据流可以分支,如果连接 Filter 有多个输出 PIN 。直到每个流都有提交查找才停止。如果 Render 遇到困难,它可能返回用不同的 Filter 进行再次的尝试。
       为了连接每个输出 PIN Render 函数执行如下工作:
       (1) 、如果 PIN 支持 ISteamBuilder 接口, Filter Graph Manager 就委派给 IStreamBuilder Render 函数。通过暴露此接口, PIN 就被假定承担建立 Graph 剩余部分,一直到 Renderer. 但是很少有 PIN 支持这个接口。
       (2) Filter Graph Manager 尝试使用缓存在内存的 Filter 。如果有,贯穿整个智能连接过程, Filter Graph Manager 可能缓存这个 Filter 开始处理的每一步的 Filter.( 参考 Dynamic Graph Building)
       (3) 、如果 Filter Graph 中某个 Filter 有未连接输出 PIN Filter Graph Manager 下一步就尝试它们。我们可以在调用 Render 之前,把某个特殊 Filter 添加到 Graph ,以此来强制 Render 函数尝试使用它。
       (4) 、最后, Filter Graph Manager 调用 IFilterMapper2::EnumMatchingFitlers 查找注册表。它尝试用输出 PIN 的首先媒体类型来匹配注册表中列举的媒体类型。
       每个 Filter 都注册了一个 Merit 数值来区别与其他 Filter 的优先级。 EnumMatchingFitlers 函数返回以 Merit 排序的 Filters, 最小的 Merit MERIT_DO_NOT_USE+1. 它会忽略比这个最小值还小的 Filter. Filter 还会以 GUID 值的种类排序。种类本身也有 Merit EnumMatchingFitlers 也忽略 Merit 比最小值小的种类。即使种类中存在 Merit 比较高的 Filter
       总结起来, Render 函数按下面的顺序尝试 Filter:
       ·使用 IStramBuilder
       ·尝试缓存 Filter
       ·尝试 Graph 中的 Filter
       ·查询注册表的 Filter
     AddSourceFilter 函数添加一个可以提交指定文件的 Source Filter 。首先查询注册表匹配协议(比如 http:// ),文件扩展名,预定义的检测字节集(通常是匹配一定模式的在文件中特殊偏移位置的字节)。细节可参考 Registering a Custom File Type. 如果函数找到合适的 Source Filter, 就创建 Filter 的实例并添加到 Graph. 然后用文件名调用 Filter IFileSourceFilter::Load 函数。
       RenderFile 函数从文件名建立一个默认的回放 Graph 。内部它使用 AddSourceFilter 来查找正确的 Source Filter ,然后用 Render 建立 Graph 的余下部分。
       Connect 函数连接输入、输出 PIN 。此函数会根据 Render 中描述的各种算法添加中介 Filter ,如果需要:
       (1) 、尝试不用中介 Filter 直接连接两个 Filter
       (2) 、尝试缓存 Filter
       (3) 、尝试 Graph 中的 Filter
       (4) 。查询注册表的 Filter.
 
4.     Data Flow in the Filter Graph
       本节描述媒体数据如何在 Filter Graph 中移动。通常,编写 DirectShow 应用程序不需要知道这些细节,尽管有时候会感觉到有帮助。如果是编写 DirectShow Filter ,就需要理解这些知识。
     4.1    Transports
       为了在 Filter Graph 中传送媒体数据, DirectShow Filter 需要支持一些协议,称之为传输协议( transport )。相连的 Filter 必须支持同样的传输协议,否则不能交换媒体数据。通常,一个传输协议要求 PIN 支持一个特殊的接口。当 Filter 连接时,一个 PIN 就向另一个查询此接口。
大多数的 DirectShow Filter 把媒体数据保存在主存储器中,并通过 Pin 连接把数据递送给其它的 Filter ,这种传输称为本地内存传输。虽然本地内存传输在 DirectShow 中最常用,但并不是所有的 Filter 都使用它。例如,有些 Filter 通过硬件传送媒体数据, Pin 只是用来提交控制信息,如 IOverlay 接口。
   DirectShow 为本地内存器传输定义了两种机制:推模式和拉模式。在推模式中, Source Filter 生成数据并递送给下一级 Filter 。下一级 Filter 被动接收数据,完成处理后再传送给再下一级 Filter 。在拉模式中, Source Filter 与一个 Parse Filter 相连。 Parse Filter Source Filter 请求数据后, Source Filter 才递送数据以响应请求。推模式用 IMemInputPin 接口,拉模式用 IAsyncReader 接口。
   推模式比拉模式要更常用。因为后续的文章都假定使用推模式。本节的最后一部分, Pull Mode 说明了 IAsyncReader 接口与 IMemInputPin 接口的区别。
 
4.2 Samples and Allocators
       当一个 Pin 向另一个 Pin 传递数据的时候,它并不是直接将内存块的指针传递下一个 Pin ,实际上传递的是一个管理内存的 COM 对象指针。这个 COM 对象称 Media Sample 暴露了 IMediaSample 接口。接收 Pin 通过调用 IMediaSample 的方法来对内存进行操作,比如 GetSize GetActualDataLength 以及 GetPointer 方法。
   Sample 总是从上到下递送的,从输出 Pin 到输入 Pin ,输出 Pin 通过调用输入 Pin IMemInputPin Receive 方法传递 Sample ,输入 Pin 可以在 Receive 函数同步处理数据,或者另外采用一个工作线程进行异步处理。输入 PIN 也允许阻塞在 Receive 方法中,如果需要等待资源。
       另外一个 COM 对象,叫做 Allocator ,用来创建和管理 Sample 的。暴露了 IMemAllocator 接口。当一个 Filter 需要一个空的 Buffer 的时候,就可以调用 IMemAllocator::GetBuffer ,该方法返回一个指向 Sample 的指针。每一个 Pin 连接都共享一个 Allocator ,当两个 Pin 连接的时候,他们会决定由哪个 Filter 来提供 Allocator ,通过 Pin 还可以设置 Allocator 的属性,例如, Buffer 的数量和大小。具体细节可参考 How Filters Connect, Negotiating Allocators.
       下面的图表显示了 Allocator Sample Filter 之间的关系。

       4.2.1  Media Sample Reference Counts
       Allocator 创建了一个有限的 Sample 池。在任何时候,某些 Sample 可能在使用,而其他的可以响应 GetBuffer 调用。 Allocator 用引用计数保持跟踪 Samples GetBuffer 返回的 Sample 的引用计数是 1 。当 Sample 的引用计数为 0 时, Sample 就返回 Allocator 池,成为空闲 Sample ,可以再次响应 GetBuffer 的调用。如果所有的 Sample 都处于繁忙状态, GetBuffer 就会阻塞,直到有一个 Sample 空闲。
       例如,假设一个输入 Pin 接到一个 Sample ,如果它在 Receive 方法里同步的处理这个 Sample ,没有增加该 Sample 的引用计数,在 Receive 返回后,输出 Pin 释放这个 Sample ,引用计数为 0 Sample 就返回到 Allocator 池。另一方面,如果输入 Pin 在工作线程中处理该 Sample ,引用计数增加 1 ,成为 2 ,输出 Pin 返回,释放 Sample ,计数成 1 。在工作线程结束处理后,调用 Release 释放 Sample. 现在 Sample 返回到池中。
       当一个 Pin 接收到一个 Sample 时,它可以将数据复制到另一个 Sample 中,也可以将这个 Sample 传递到下一个 Filter 。一个 Sample 可以流遍整个 Filter graph 。每个 Filter 依次调用 AddRef Release (译注:相当于使引用计数要保持大于 0 )。因此,输出 Pin 调用 Receive 后就决不能再使用这个 Sample 。因为也许下游还有 Filter 正在使用该 Sample 。输出 Pin 必须调用 GetBuffer 获取新的 Sample
       这种机制减少了内存分配的,因为 Buffer 可以重用。也防止了 Filter 在没有处理的 Sample 重新写入,因为 Allocator 维护了一个可用 Sample 列表。
       Filter 可以给输入、输出使用分别的 Allocator. Filter 可以在扩展输入数据时这么做(比如解压数据)。如果输出并不比输入大, Filter 可以就地处理数据,而不需拷贝到新 Sample 。那时,两个或者更多的 PIN 连接就可以共享 Allocator.
 
       4.2.2 Committing and Decommitting Allocators
       Filter 创建一个 Allocator 的时候, Allocator 还没有保留任何的内存,如果这个时候有人 GetBuffer ,就会失败。只有当数据流开始的时候,输出 Pin 调用 IMemAllocator::Commit ,提交 Allocator ,现在才能分配内存。然后才可以调用 GetBuffer.

       当数据流停止时,Pin就调用IMemAllocator::Decommit,来销毁Allocator。在Allocator再次Commit之前,所有的GetBuffer调用都会失败。同样,即使是GetBuffer正在阻塞等待Sample,也会立即返回一个错误码。Decommit能不能释放内存取决于它的实现。比如CMemAllocator类会等到它的析构函数来释放内存。 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值