DirectShow视频捕获应用程序编写(SDK翻译)

标签: 视频  驱动程序  设备  捕获  参数 2007-03-30 15:15

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, &param1, &param2, 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);

转载于:https://www.cnblogs.com/zhmx/archive/2012/09/06/2672927.html

mfc 配合directshow相关推荐

  1. c语言union字节相同大小不同,C语言的struct/union字节对齐

    C语言的一大优势就是对内存空间的控制,当然,一般情况下对于开发人员来说都是透明的.看一个始终困扰初学者的问题:字节对齐! 先看四个重要的基本概念: 1.数据类型自身的对齐值: 对于char型数据,其自 ...

  2. MFC学习之路之多媒体 --(1) DirectShow

    可以说整个Windows的多媒体编程都是以DirectShow为基础,那好,来吧,我们直接看DirectShow的一段基础代码. bool Mp3::Load(LPCWSTR szFile) {Cle ...

  3. 【MFC两种视频图像採集方法】DirectShow与Opencv

    效果图: DirectShow採集核心代码: 创建线程调用该函数,採集图像通过x264解码封装rtmp协议包.推送至FMSserver,可实现视频直播 UINT __stdcall StartVide ...

  4. VC/MFC 编程技巧大总结

    1 toolbar默认位图左上角那个点的颜色是透明色,不喜欢的话可以自己改. 2 VC++中 WM_QUERYENDSESSION WM_ENDSESSION 为系统关机消息. 3 Java学习书推荐 ...

  5. C++/MFC编程技巧

    1 toolbar默认位图左上角那个点的颜色是透明色,不喜欢的话可以自己改. 2 VC++中 WM_QUERYENDSESSION WM_ENDSESSION 为系统关机消息. 3 Java学习书推荐 ...

  6. mfc 两个工具栏合并_(海外观点)MFC,微型电商仓的新玩法?

    Micro-fulfillment may be coming to a grocerystore near you. See how it works. 微型订单履约可能会来到你附近的一家杂货店.了 ...

  7. DirectShow Filter 基础与简单的示例程序

    DirectShow 是一个 Windows 平台上的流媒体框架,提供了高质量的多媒体流采集和回放功能. Filter 实质是一个 COM 组件,所以学习开发 Filter 前你应该对 COM 相关知 ...

  8. Visual C++ MFC/ATL开发-高级篇(一)

    在VC++6.0中用MFC进行COM编程首先应当明确,MFC中是通过嵌套类而不是多重继承来实现COM接口的,通过接口映射机制将接口和实现该接口的嵌套类关联起来:MFC中提供一套简明的宏来实现嵌套类的定 ...

  9. DirectX和DirectShow介绍和区别

    1.DirectX是什么 DirectX是微软推出的一套基于Windows系统的多媒体应用程式接口APIs函式.在开发中,DX分为两个部分,一个是运行库,通过DX编译出来的程式必须要有运行库的支持,另 ...

  10. windows编写第一个MFC程序

    1. 如何在对话框类中响应消息 1.1 基本方式 使用 WM_LBUTTONDOWN和WM_RBUTTONDOWN去演示如何使用消息 MFC给每一个消息都设计了一个函数 如果我们想响应WM_LBUTT ...

最新文章

  1. python查看函数参数,在python函数中获取参数名称列表
  2. java 继承和内部类_Java自学-接口与继承 内部类
  3. 成熟就是深谙世故却不世故
  4. qt4.8创建.pri_注意Java 8的[Pri​​mitive] Stream.iterate()中的递归
  5. 容器编排技术 -- Kubernetes kubectl create configmap 命令详解
  6. sftp配置导致ssh连接闪断
  7. 【填坑】Ubuntu安装vsftpd
  8. 【密码专栏】动手计算双线性对(上)
  9. 在实现视频播放器的步骤client(三)风行网络电影列表
  10. 打印机驱动兼容问题导致打印乱码
  11. IAR For ARM 7.8下载地址
  12. Ubuntu18.04安装Adobe flash player
  13. 普适计算机技术特征的事例,普适计算、物联网、云计算与未来社会信息化需求分析...
  14. 不借助第三个变量交换a,b两个变量值
  15. 【Arduino实验08 红外传感器】
  16. 神经网络是算法还是模型,神经元网络算法的思想
  17. POJ3889Fractal Streets 递归+ 坐标变换
  18. C2: 宽基窄基 场内场外
  19. 国家省份城市级联菜单
  20. PHP(10):PHP读取Excel文件的记录-方法2

热门文章

  1. 易语言版{大智慧/分析家/飞狐交易师}DLL插件接口开发模块(beta5),自定义股票软件公式扩展函数
  2. 树莓派GPIO引脚详解
  3. 简单理解:类目、SPU、SKU
  4. c语言如何输出数组最大值和最小值,C语言输出数组中最大和次大的数
  5. amazon linux ami root 密码,如何使用SecureCRT连接到亚马逊Amazon EC2 Linux AMI
  6. 细数国内外的哪些数学建模竞赛
  7. GPS测量中的数据格式——RINEX格式说明
  8. h3c交换机重启_h3c交换机常用命令
  9. itest听力答案2020_itest答案.doc
  10. 数据结构与算法 哈希表的特点