DirectShow DirectShow是一种windows平台上的流媒体体系结构。DirectShow提供了多媒体流的高质量捕获和回放机制。它支持多种格式,包括ASF(Advanced System Format),MPEG(Motion Picture Expert Group),AVI(Audio-Video Interleaved),MP3(MPEG Audio Layer-3)和WAV声音文件。它支持基于WDM(Windows Driver Model)和VFW(Video for Windows)数字和模拟设备的捕获。DirectShow与其他DirectX技术集成在一起。如果可能,它将自动检测并使用视频和音频硬件加速,当然它同时支持没有硬件加速的系统。 DirectShow简化了媒体回放,格式转换和捕获任务。同时,它为应用程序提供了对下层流控制体系的访问。你也可以创建你自己的DirectShow组件来支持新的数据格式或者用户功能。 可以使用DirectShow编程实现文件播放器,TV和DVD播放器,视频编辑程序,文件格式转换程序,音频视频捕获程序,编码器和解码器,数字信号处理程序等等。 DirectShow基于COM(Component Object Model)。要编写DirectShow程序或者组件,必须理解COM客户编程。对于大多数程序,不需要实现你自己的COM对象,DirectShow提供你需要的组件。
DirectShow系统概览 多媒体的挑战 开发多媒体需要面临一下挑战 多媒体流包含大量需要快速处理的数据 音频和视频必须同步以便它们同时开始,同时停止,以同样的速率播放。 数据可以有很多来源,包括本地文件,计算机网络,广播电视和视频相机。 数据有很多格式,不如AVI,ASF,MPEG和DV。 程序员不知道将会有什么样的硬件出现在系统中。
DirectShow解决方案 设计DirectShow就是为了解决以上挑战的。它的主要设计目标是简化在windows平台上创建数字媒体应用程序的任务,把应用程序从数据传输的复杂,硬件的差异和同步中解脱出来。 要达到需要的吞吐量来传输音频和视频,任何可能的情况下,DirectShow使用DirectDraw和DirectSound。这些技术可以高效的把数据展现在用户的声卡和显卡上。DirectShow通过把媒体数据封装在带有时间戳的Sample中来同步回放。要处理可能的多种源,格式和硬件设备,DirectShow使用模块化体系结构,在其中应用程序把一种叫做filter的组件组合起来。 DirectShow提供了支持基于WDM(Windows Driver Model)的捕获和调谐设备的filter,基于旧的VFW(Video for Windows)的捕获卡的filter和音频压缩管理器编解码器和视频压缩管理接口。 下图展示了应用程序,DirectShow组件和一些DirectShow支持的硬件和软件组件之间的关系。 正如上图所示,DirectShow filter与各种设备通讯并控制它们,这些设备包括本地文件系统,TV调谐设备和视频捕获卡,VFW编解码器,视频显示器和声卡。因此,DirectShow隔离了应用程序和复杂的设备。DirectShow同时为某种文件格式提供了本地压缩解压filter。
DirectShow中的视频捕获 术语视频捕获描述了任何从硬件接收视频的应用程序。视频捕获设备不仅包括数码相机,也包括TV调谐卡,视频录像带等等。捕获的视频可以存储在磁盘上或者实时预览。
关于捕获Graph Builder 一个执行视频和音频不过的filter graph叫做捕获graph。捕获graph通常要比文件回放graph更复杂。为了让应用程序更简单的创建捕获graph,DirectShow提供一个叫做捕获Graph Builder的帮助对象。该捕获Graph Builder导出IcaptureGraphBuilder2接口,该接口包含创建和控制捕获graph的方法。 以调用CoCreateInstance创建一个新的捕获Graph Builder和Filter Graph Manager开始。随后通过给定Filter Graph Manager的IgraphBuilder接口指针作为参数调用IcaptureGraphBuilder2::SetFiltergraph函数初始化捕获Graph Builder。 关于视频捕获设备 大多数新的视频捕获设备使用WDM(Windows Driver Model)驱动程序。在WDM体系结构中,微软提供了一套独立于硬件的驱动程序,叫做类驱动程序,硬件供应商提供硬件相关的迷你驱动程序。迷你驱动程序实现任何设备相关的功能。对于多数功能,迷你驱动程序只是调用微软的类驱动程序。 在一个DirectShow filter graph中,任何WDM捕获设备以WDM视频捕获filter的形式出现。该WDM捕获filter基于驱动程序的特性配置自身。 一些老的捕获设备仍然使用VFW(Video for Windows)驱动程序。虽然这些驱动程序现在已经过时了,但是DirectShow仍然通过VFW捕获filter来支持它们。
DirectShow视频捕获filter DirectShow中的捕获filter含有一些有别于其他类型filter的特性。虽然捕获Graph Builder隐藏了许多细节,但阅读这部分会让你对DirectShow捕获graph有个大体上的理解。 Pin的种类 一个捕获filter通常含有两个或者更多的输出pin用来传输相同种类的数据,例如,预览pin和捕获pin。因此,媒体类型不是区分它们的好的办法。取而代之,使用它们的功能来驱动它们,它们的ID使用GUID,叫做pin的种类。 要讨论如何使用种类来请求pin,参看Work with pin categories。可是,对于多数应用程序,你不需要直接请求pin。取而代之,许多IcaptureGraphBuilder的方法操作的时候需要指定pin的种类。捕获Graph Builder自动定位正确的pin。 预览pin和捕获pin 一些视频捕获设备拥有独立的预览和捕获pin。预览pin用来展现视频到屏幕,同时捕获pin用来把视频数据写入文件。 一个预览pin和一个捕获pin有一下不同。 为了保持捕获pin上的吞吐量需要的时候预览pin可以丢弃数据帧。 当数据帧被捕获时,捕获pin上的数据帧都盖有流时间的时间戳。预览pin没有加盖时间戳。 预览帧没有时间戳的原因是filter graph在流媒体上会导致一小部分延迟。如果捕获时间使用当前的时间,视频展示器认为每个sample都有一点迟到。这个可以引起视频展示器丢掉数据帧同时试图捕获它们。移除时间戳确定当每个sample到达时即时展示,不会丢掉数据帧。 预览pin的种类为PIN_CATEGORY_PREVIEW。捕获pin的种类为PIN_CATEGORY_CAPTURE. 视频端口pin 视频端口是一个视频设备(比如模拟TV调谐器)和视频卡之间的硬件链接。视频端口使能该设备直接发送视频数据到显卡。该视频使用硬件覆盖显示在显示器上。视频端口可以是两个独立板卡上的两个设备的真实链接,也可以是一个板卡上的硬件链接。 视频端口的优点是视频直接传向显存,不需要任何CPU的工作。可是,视频端口也有一些缺点。 在捕获时,视频端口总使用覆盖表面而不考虑你是否需要预览视频。 数据帧之间的翻转是自动发生的,而这种翻转使翻转与其他的视频之间的操作很难同步。 如果捕获设备使用视频端口,捕获filter拥有一个视频端口pin而不是预览pin。该pin的种类是PIN_CATEGORY_VIDEOPORT. 每个捕获filter拥有至少一个捕获pin。另外它可能含有一个预览pin或者视频端口pin,但从来不同时包含预览和视频端口pin。Filter可以拥有多个捕获pin和预览pin,每个传输分立的媒体类型。因此,单个filter可以拥有一个视频捕获pin,一个视频预览pin,一个音频捕获pin,一个音频预览pin。 Upstream WDM Filter WDM(Windows Driver Model)设备可以请求一些另外的filter作为捕获filter的upstream。这些filters包括以下filter: TV调谐器filter。控制模拟TV调谐的调谐器。 TV音频filter。控制模拟TV调谐器的音频设置。 模拟视频Crossbar filter。通过硬件设备路由视频和音频信号。比如,一个设备可以拥有多个输入,比如S-Video和复合视频信号。该Crossbar filter使能应用程序选择输入。 虽然在DirectShow中这些是一些分立的filter,但是代表典型的硬件设备。每个filter控制设备的不同功能。这些filter使用pin链接在一起,但是在这些链接中没有媒体数据一同。因此,这些filter上的pin不是通过创建媒体类型链接的。而是使用叫做媒体(mediums)的GUID值链接。媒体GUID是由给定设备的迷你驱动程序唯一定义的。例如,TV调谐filter和相同TV卡的视频捕获filter将都支持相同媒体,该媒体使应用程序正确的创建graph。 在实践中,只要你使用IcaptureGraphBuilder2来创建你的捕获graph,这些filter将自动的添加到graph中。
选择捕获设备 要选择捕获设备,使用系统设备枚举器。该对象返回一个设备的别名,通过filter类型选择。 预览视频 创建一个预览graph,调用IcaptureGraphBuilder2::RenderStream方法
上例做了以下假设。 pBuild初始化 pCap初始化,通过创建一个捕获filter的实例并添加它到filter graph。 RenderStream的第一个参数制定pin种类;对于预览graph,使用PIN_CATEGORY_PREVIEW。地二个参数制定媒体类型,对于视频为MEDIATYPE_Video。DV设备传输interleaved音频和视频媒体类型为MEDIATYPE_Interleaved。 第三个参数是一个指向捕获filterIBaseFilter接口的指针。接下来了两个参数在本例中不需要。它们用来制定另外的filter以备展示媒体流。设置最后一个参数为NULL导致捕获Graph Builder为媒体流选择默认的展示器。对于视频,捕获Graph Builder总是使用Video Render filter作为默认展示器。 虽然该pin的类型给定为PIN_CATEGORY_PREVIEW,但是filter实际是否真的存在预览pin是无关紧要的。它可以是一个视频端口pin或者就是一个捕获pin。在任何一种情况下,捕获Graph Builder自动创建正确的graph。 下图显示了可能的最简单的预览视频graph 在该图中,捕获filter有一个预览pin,该pin直接链接到展示器。 如果捕获filter仅仅拥有一个捕获pin,捕获Graph Builder插入一个Smart Tree filter,该filter把媒体流分割成捕获流和预览流。 在某些情况下,视频流必须通过Overlay Mixer filter。如果这样,RenderStream方法自动添加它奥graph。
Capturing Video to a File 下图展示了可能的最简单的捕获视频到文件的graph。
AVI Mux filter从捕获pin得到视频流,然后打包成AVI媒体流。音频流也可以连接到AVI Mux filter,在这种情况下mux可以把两种流交叉在一起。File Write filter把AVI流写入磁盘。 要创建这种graph,调用IcaptureGraphBuilder2::SetOutputFileName方法开始,如下
第一个参数指定文件类型(在这种情况下为AVI)。第二个参数指定文件名称。对于AVI,SetOutputFileName方法创建AVI Mux filter和File Writer filter并添加它们到graph。通过调用IfileSinkFilter::SetFileName方法,它同时设置File Writer filter文件名称。然后链接两个filter。该方法在第三个参数返回指向AVI Mux的指针。可选的,可以在第四个参数返回IfileSinkFilter接口的指针。如果你不需要该接口,你可以设置该参数为NULL。 接下来,调用IcaptureGraphBuilder2::RenderStream方法来链接捕获filter到AVI Mux:
第一个参数给出pin的类型。对于捕获类型为PIN_CATEGORY_CAPTURE。第二个参数给定媒体类型。第三个参数为指向捕获filter IbaseFilter接口的指针。第四个参数为可选。他可以让你路由视频流通过一个中间媒体filter,比如在传递给mux filter之前通过编码器。否则,设置该参数为NULL。第五个参数是一个指向mux filter的指针。该指针通过调用SetOutputFileName方法获得。 要捕获音频,调用以类型MEDIATYPE_Audio调用RenderStream。如果你从连个设备捕获音频和视频,最好让音频流作为主流。这将帮助你阻止两个媒体流之间的漂移。因为AVI Mux filter会调整视频的回放率来匹配音频流。要设置主流,在AVI Mux filter上调用IconfigAviMux::SetMasterStream方法。
SetMasterStream的参数是流序号,该序号由调用RenderStream的顺序决定。例如,如果你首先为视频调用RenderStream,然后为音频调用,那么视频的流序号为0,音频的为1. 你可能同时需要设置AVI Mux filter如何交叉音频和视频流,通过调用IconfigInterleaving::PutMode方法可以达到目的。
使用INTERLEAVE_CAPTURE标志,AVI Mux以适合视频捕获的速率执行交叉。你也可以使用INTERLEAVE_NONE,它的意思是不相交-AVI Mux将把数据按照他们到达的顺序写入文件。INTERLEAVE_FULL标志意思是AVI Mux执行完全交叉;但是,该模式不是特别适合视频捕获,因为需要最多的overheard。 编码视频流 你可以通过在捕获filter和AVI Mux filter之间插入一个编码filter来实现对视频流的编码。使用系统设备枚举器或者Filter Mapper来选择一个编码filter。 在调用RenderStream的第四个参数中,指定编码filter,如下所示:
编码filter可能支持IAMVideoCompression或者其他设置编码参数的接口。 捕获视频到windows Media文件 要捕获视频并把它编码成windows媒体文件(WMV),链接捕获pin到WM ASF Writer filter。 创建这种graph的一种简单方法是在调用IcaptureGraphBuilder2::SetOutputFileName方法时指定MEDIASUBTYPE_Asf。
该值告诉捕获Graph Builder使用WM ASF Writer filter作为文件接收器。该捕获Graph Builder创建该filter,添加到graph并调用IfileSinkFilter::SetFileName来设置输出文件的名称。它返回一个该filter的指针。 之用IconfigAsfWriter接口来设置Windows媒体轮廓。你必须在你链接任何WM ASF Writer之前做这些。
调用IcaptureGraphBuilder2::RenderStream来链接捕获filter到ASF Writer。
自定义文件格式 如果你拥有一个支持你自己文件格式的自定义的mux或者文件写入filter,你可以指定CLSID作为SetOutputFileName方法的第一个参数。
文件捕获中的视频端口pin 如果捕获设备拥有一个视频端口,该视频端口必须链接到视频展示器,即使你只想捕获到文件。 如果你使用参数PIN_CATEGORY_CAPTURE来调用RenderStream而且设备存在一个视频端口pin,该捕获Graph Builder自动链接视频端口pin到Overlay Mixerfilter并链接Overlay Mixer到视频展示器。该捕获通过使用参数OAFALSE调用IvideoWindow::put_AutoShow方法,Graph Builder将隐藏视频窗口。如果应用程序稍后使用参数PIN_CATEGORY_PREVIEW调用RenderStream方法,该Graph Builder将使用参数OATURE调用put_AutoShow以便显示视频窗口。 在你使用参数PIN_CATEGORY_CAPTURE调用RenderStream后,你可以通过请求Filter Graph Manager的IvideoWindow接口来检查它是否添加了视频展示器。 捕获到多个文件 在你将一些视频数据捕获到文件后,你可以通过停止graph并设置File Writer filter的文件名来变换到一个新的文件。调用File Writer上的IfileSinkFilter::SetFileName方法。当你创建graph时,可以通过在SetOutputFileName方法的pSink中,你可以得到一个IfileSinkFilter接口的指针。下面的代码展示了实现。
把视频预览和捕获链接在一起。 以前个描述了如何捕获视频到多挣文件格式。预览视频部分描述如何创建一个实时graph。可是许多应用程序必须同时做两件事情。要创建预览和捕获链接在一起的graph,简单的调用IcaptureGraphBuild2::RenderStream两次:
在上面这段代码中,捕获Graph Builder隐藏了一些细节: 如果捕获filter存在预览pin或者视频端口pin,在加上一个捕获pin,RenderStream简单的展示两个pin,如下图所示。 如果该filter只有一个捕获pin,该捕获Graph Builder使用Smart Tee filter来分割捕获流。下图展示了使用Smart Tee的graph。
Smart Tee filter拥有一个捕获pin和一个预览pin。它从单独的捕获filter中得到视频流,然后把该视频流分割成两条视频流,一条送给捕获pin,一条送给预览pin。为了保持捕获pin上的吞吐量,如果需要预览pin将丢弃数据帧。 虽然Smart Tee分割视频流,它其实不是物理上复制该视频数据。它使用自定义的媒体sample对象(该对象分享缓冲区)。该sample被标记为只读确定下层流filter不能在其上写入数据。 控制捕获Grah Filter Graph Manager的ImediaControl接口拥有控制整个graph运行,停止和暂停的方法。如果filter graph存在捕获和预览流,但是你或许想分别控制两个流。例如,你可能想在没有捕获的情况下预览视频。你可以通过IcaptureGraphBuilder2::ControlStream方法来这样做。 注意当捕获ASF文件时,该方法不能工作。
第一个参数指定控制哪个流,作为pin类型。第二个参数给出媒体类型。第三个参数为指向捕获filter的指针。要控制graph中的所有的捕获流,设置第二个参数和第三个参数为空。 接下来两个参数定义了流开始和停止的时间。调用ImediaControlRun来运行graph。知道你运行graph,ControlStream方法才有作用。如果该graph已经运行,该设置会马上起作用。 上两个参数是为了在流开始和停止时得到事件通知。对于每个使用该方法控制的流。当流开始时,Filter graph发送一组事件EC_STREAM_CONTROL_STARTED,当流停止时graph发送EC_STREAM_CONTROL_STOPPED。wStartCookie和wStopCookie的值将会作为事件的第二个参数。因此lparam2在开始时间时等于wStartCookie。下面代码展示了如何得到这些事件。
ControlStream方法为开始和停止时间定义了一些特殊的值。 比如,下面代码马上停止捕获:
虽然你可以停止捕获流,稍后再重新开始,这将产生一个时间戳间隙。在回放时,视频会在间隙处出现停滞。 控制预览流 要控制预览pin,也是调用ControlStream但第一个参数设置为PIN_CATEGORY_PREVIEW。这和捕获是一样了,出国你不能使用引用时间来指定开始和停止,因为预览帧没有时间戳。因此,你必须使用NULL或者MAXLONGLONG。使用NULL开始预览流: pBuild->ControlStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, // Start now. 0, // (Don't care.) wStartCookie, wStopCookie); 使用MAXLONGLONG停止预览流: pBuild->ControlStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, 0, // (Don't care.) MAXLONGLONG, // Stop now. wStartCookie, wStopCookie); 预览流来自预览pin,捕获pin或者Smart Tee filter都无所谓。ControlStream方法在这集中情况下都可以工作。 可是,对于视频端口pin,该方法就会失败。在那种情况下,另外的方法会隐藏视频窗口。需要使用IvideoWindow::put_Visible方法来显示或者隐藏窗口。
关于流控制的备注。 默认情况下,当graph运行后pin开始传送sample。比如,假设你使用PIN_CATEGORY_CAPTURE而不是PIN_CATEGORY_PREVIEW调用ControlStream方法。当你运行graph时,预览流将会马上运行,但是捕获流将在你指定的时间开始运行。 如果你捕获多于一条流,并发送他们到mux filter-比如,如果你捕获音频和视频到AVI文件-你应该一前一后控制两个流。否则,mux filter可能为交叉两个流而等待另一个流最终阻塞。请在开始运行graph之前设置所有流有相同的开始和停止时间。
|
|
来自: haodafeng_org > 《DSHOW》