激战2永久采集工具:DirectShow视频捕获应用程序编写(SDK翻译)

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 01:16:21

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方法

ICaptureGraphBuilder2 *pBuild; // Capture Graph Builder
// Initialize pBuild (not shown).
 
IBaseFilter *pCap; // Video capture filter.
/* Initialize pCap and add it to the filter graph (not shown). */
 
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, 
    pCap, NULL, NULL);

上例做了以下假设。

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方法开始,如下

IBaseFilter *pMux;
hr = pBuild->SetOutputFileName(
    &MEDIASUBTYPE_Avi,  // Specifies AVI for the target file.
    L"C:\\Example.avi", // File name.
    &pMux,              // Receives a pointer to the mux.
    NULL);              // (Optional) Receives a pointer to the file sink.

第一个参数指定文件类型(在这种情况下为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:

hr = pBuild->RenderStream(
    &PIN_CATEGORY_CAPTURE, // Pin category.
    &MEDIATYPE_Video,      // Media type.
    pCap,                  // Capture filter.
    NULL,                  // Intermediate filter (optional).
    pMux);                 // Mux or file sink filter.
 
// Release the mux filter.
pMux->Release();

第一个参数给出pin的类型。对于捕获类型为PIN_CATEGORY_CAPTURE。第二个参数给定媒体类型。第三个参数为指向捕获filter IbaseFilter接口的指针。第四个参数为可选。他可以让你路由视频流通过一个中间媒体filter,比如在传递给mux filter之前通过编码器。否则,设置该参数为NULL。第五个参数是一个指向mux filter的指针。该指针通过调用SetOutputFileName方法获得。

要捕获音频,调用以类型MEDIATYPE_Audio调用RenderStream。如果你从连个设备捕获音频和视频,最好让音频流作为主流。这将帮助你阻止两个媒体流之间的漂移。因为AVI Mux filter会调整视频的回放率来匹配音频流。要设置主流,在AVI Mux filter上调用IconfigAviMux::SetMasterStream方法。

IConfigAviMux *pConfigMux = NULL;
hr = pMux->QueryInterface(IID_IConfigAviMux, (void**)&pConfigMux);
if (SUCCEEDED(hr))
{
    pConfigMux->SetMasterStream(1);
    pConfigMux->Release();
}

SetMasterStream的参数是流序号,该序号由调用RenderStream的顺序决定。例如,如果你首先为视频调用RenderStream,然后为音频调用,那么视频的流序号为0,音频的为1.

你可能同时需要设置AVI Mux filter如何交叉音频和视频流,通过调用IconfigInterleaving::PutMode方法可以达到目的。

IConfigInterleaving *pInterleave = NULL;
hr = pMux->QueryInterface(IID_IConfigInterleaving, (void**)&pInterleave);
if (SUCCEEDED(hr))
{
    pInterleave->put_Mode(INTERLEAVE_CAPTURE);
    pInterleave->Release();
}

使用INTERLEAVE_CAPTURE标志,AVI Mux以适合视频捕获的速率执行交叉。你也可以使用INTERLEAVE_NONE,它的意思是不相交-AVI Mux将把数据按照他们到达的顺序写入文件。INTERLEAVE_FULL标志意思是AVI Mux执行完全交叉;但是,该模式不是特别适合视频捕获,因为需要最多的overheard。

编码视频流

你可以通过在捕获filter和AVI Mux filter之间插入一个编码filter来实现对视频流的编码。使用系统设备枚举器或者Filter Mapper来选择一个编码filter。

在调用RenderStream的第四个参数中,指定编码filter,如下所示:

IBaseFilter *pEncoder;
/* Create the encoder filter (not shown). */
// Add it to the filter graph.
pGraph->AddFilter(pEncoder, L"Encoder);
 
/* Call SetOutputFileName as shown previously. */
 
// Render the stream.
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, 
    pCap, pEncoder, pMux);
pEncoder->Release();

编码filter可能支持IAMVideoCompression或者其他设置编码参数的接口。

捕获视频到windows Media文件

要捕获视频并把它编码成windows媒体文件(WMV),链接捕获pin到WM ASF Writer filter。

创建这种graph的一种简单方法是在调用IcaptureGraphBuilder2::SetOutputFileName方法时指定MEDIASUBTYPE_Asf。

IBaseFilter* pASFWriter = 0;
hr = pBuild->SetOutputFileName(
    &MEDIASUBTYPE_Asf,   // Create a Windows Media file.
    L"C:\\VidCap.wmv",   // File name.
    &pASFWriter,         // Receives a pointer to the filter.
    NULL);  // Receives an IFileSinkFilter interface pointer (optional).

该值告诉捕获Graph Builder使用WM ASF Writer filter作为文件接收器。该捕获Graph Builder创建该filter,添加到graph并调用IfileSinkFilter::SetFileName来设置输出文件的名称。它返回一个该filter的指针。

之用IconfigAsfWriter接口来设置Windows媒体轮廓。你必须在你链接任何WM ASF Writer之前做这些。

IConfigAsfWriter *pConfig = 0;
hr = pASFWriter->QueryInterface(IID_IConfigAsfWriter, (void**)&pConfig);
if (SUCCEEDED(hr))
{
     // Configure the ASF Writer filter.
    pConfig->Release();
}

调用IcaptureGraphBuilder2::RenderStream来链接捕获filter到ASF Writer。

hr = pBuild->RenderStream(
    &PIN_CATEGORY_CAPTURE,   // Capture pin.
    &MEDIATYPE_Video,        // Video. Use MEDIATYPE_Audio for audio.
    pCap,                    // Pointer to the capture filter. 
    0, 
    pASFWriter);             // Pointer to the sink filter (ASF Writer).

自定义文件格式

如果你拥有一个支持你自己文件格式的自定义的mux或者文件写入filter,你可以指定CLSID作为SetOutputFileName方法的第一个参数。

IBaseFilter *pMux = 0;
IFileSinkFilter *pSink = 0;
hr = pBuild->SetOutputFileName(&CLSID_MyCustomMuxFilter, 
    L"C:\\VidCap.avi", &pMux, &pSink);

文件捕获中的视频端口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接口的指针。下面的代码展示了实现。

IBaseFilter *pMux;
IFileSinkFilter *pSink
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\YourFileName.avi", 
    &pMux, &pSink);
if (SUCCEEDED(hr))
{
    hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, 
        pCap, NULL, pMux);
 
    if (SUCCEEDED(hr))
    {
        pControl->Run();
        /* Wait awhile, then stop the graph. */
        pControl->Stop();
        // Change the file name and run the graph again.
        pSink->SetFileName(L"YourFileName02.avi", 0);
        pControl->Run();
    }
    pMux->Release();
    pSink->Release();
}

把视频预览和捕获链接在一起。

以前个描述了如何捕获视频到多挣文件格式。预览视频部分描述如何创建一个实时graph。可是许多应用程序必须同时做两件事情。要创建预览和捕获链接在一起的graph,简单的调用IcaptureGraphBuild2::RenderStream两次:

// Render the preview stream to the video renderer.
hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, 
    NULL, NULL);
 
// Render the capture stream to the mux.
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, 
    NULL, pMux);

在上面这段代码中,捕获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文件时,该方法不能工作。

// Control the video capture stream. 
REFERENCE_TIME rtStart = 10000000, rtStop = 50000000;
const WORD wStartCookie = 1, wStopCookie = 2;  // Arbitrary values.
hr = pBuild->ControlStream(
    &PIN_CATEGORY_CAPTURE, // Pin category.
    &MEDIATYPE_Video,      // Media type.
    pCap,                 // Capture filter.
    &rtStart, &rtStop,     // Start and stop times.
    wStartCookie, wStopCookie  // Values for the start and stop events.
);
pControl->Run();

第一个参数指定控制哪个流,作为pin类型。第二个参数给出媒体类型。第三个参数为指向捕获filter的指针。要控制graph中的所有的捕获流,设置第二个参数和第三个参数为空。

接下来两个参数定义了流开始和停止的时间。调用ImediaControlRun来运行graph。知道你运行graph,ControlStream方法才有作用。如果该graph已经运行,该设置会马上起作用。

上两个参数是为了在流开始和停止时得到事件通知。对于每个使用该方法控制的流。当流开始时,Filter graph发送一组事件EC_STREAM_CONTROL_STARTED,当流停止时graph发送EC_STREAM_CONTROL_STOPPED。wStartCookie和wStopCookie的值将会作为事件的第二个参数。因此lparam2在开始时间时等于wStartCookie。下面代码展示了如何得到这些事件。

while (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr))
{
    switch (evCode)
    {
    case EC_STREAM_CONTROL_STARTED: 
    // param2 == wStartCookie
    break;
 
    case EC_STREAM_CONTROL_STOPPED: 
    // param2 == wStopCookie
    break;
    
    } 
    pEvent->FreeEventParams(evCode, param1, param2);
}

ControlStream方法为开始和停止时间定义了一些特殊的值。

比如,下面代码马上停止捕获:

pBuild->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap,
    0, 0,     // Start and stop times.
    wStartCookie, wStopCookie); 

虽然你可以停止捕获流,稍后再重新开始,这将产生一个时间戳间隙。在回放时,视频会在间隙处出现停滞。

控制预览流

要控制预览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方法来显示或者隐藏窗口。

// Hide the video window.
IVideoWindow *pVidWin = 0;
hr = pGraph->QueryInterface(IID_IVideoWindow, (void**)&pVidWin);
if (SUCCEEDED(hr))
{
    pVidWin->put_Visible(OAFALSE);
    pVidWin->Release();
}

关于流控制的备注。

默认情况下,当graph运行后pin开始传送sample。比如,假设你使用PIN_CATEGORY_CAPTURE而不是PIN_CATEGORY_PREVIEW调用ControlStream方法。当你运行graph时,预览流将会马上运行,但是捕获流将在你指定的时间开始运行。

如果你捕获多于一条流,并发送他们到mux filter-比如,如果你捕获音频和视频到AVI文件-你应该一前一后控制两个流。否则,mux filter可能为交叉两个流而等待另一个流最终阻塞。请在开始运行graph之前设置所有流有相同的开始和停止时间。

pBuild->ControlStream(&PIN_CATEGORY_CAPTURE, 
    NULL, NULL,       // All capture streams.
    &rtStart, rtStop, 
    wStartCookie, wStopCookie);