Directshow中的视频捕捉


本篇文档主要描述关于用Directshow进行视频开发的一些技术
主要包括下面内容
  • 1关于视频捕捉(About Video Capture in Dshow)
  • 2选择一个视频捕捉设备(Select capture device)
  • 3预览视频(Previewing Video)
  • 4如何捕捉视频流并保存到文件(Capture video to File)
  • 5将设备从系统中移走时的事件通知(Device remove Notify)
  • 6如何控制Capture Graph(Controlling Capture Graph)
  • 7如何配置一个视频捕捉设备
  • 8从静止图像pin中捕捉图片


1关于视频捕捉(About Video Capture in Dshow)

1视频捕捉Graph的构建
一个能够捕捉音频或者视频的graph图都称之为捕捉graph图。捕捉graph图比一般的文件回放graph图要复杂许多,dshow提供了一个Capture Graph Builder COM组件使得捕捉graph图的生成更加简单。Capture Graph Builder提供了一个ICaptureGraphBuilder2接口,这个接口提供了一些方法用来构建和控制捕捉graph。
首先创建一个Capture Graph Builder对象和一个graph manger对象,然后用filter graph manager 作参数,调用ICaptureGraphBuilder2::SetFiltergraph来初始化Capture Graph Builder。看下面的代码把
HRESULT InitCaptureGraphBuilder(
IGraphBuilder **ppGraph, // Receives the pointer.
ICaptureGraphBuilder2 **ppBuild // Receives the pointer.
)
{
if (!ppGraph || !ppBuild)
{
return E_POINTER;
}
IGraphBuilder *pGraph = NULL;
ICaptureGraphBuilder2 *pBuild = NULL;

// Create the Capture Graph Builder.
HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pGraph);
if (SUCCEEDED(hr))
{
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void**)&pGraph);
if (SUCCEEDED(hr))
{
// Initialize the Capture Graph Builder.
pBuild->SetFiltergraph(pGraph);

// Return both interface pointers to the caller.
*ppBuild = pBuild;
*ppGraph = pGraph; // The caller must release both interfaces.
return S_OK;
}
else
{
pBuild->Release();
}
}
return hr; // Failed
}

2视频捕捉的设备
现在许多新的视频捕捉设备都采用的是WDM驱动方法,在WDM机制中,微软提供了一个独立于硬件设备的驱动,称为类驱动程序。驱动程序的供应商提供的驱动程序称为minidrivers。Minidrivers提供了直接和硬件打交道的函数,在这些函数中调用了类驱动。
在directshow的filter图表中,任何一个WDM捕捉设备都是做为一个WDM Video Capture
过滤器(Filter)出现。WDM Video Capture过滤器根据驱动程序的特征构建自己的filter

下面是陆其明的一篇有关于dshow和硬件的文章,可以拿来参考一下

//陆文章开始

大家知道,为了提高系统的稳定性,Windows操作系统对硬件操作进行了隔离;应用程序一般不能直接访问硬件。DirectShow Filter工作在用户模式(User mode,操作系统特权级别为Ring 3),而硬件工作在内核模式(Kernel mode,操作系统特权级别为Ring 0),那么它们之间怎么协同工作呢?

DirectShow解决的方法是,为这些硬件设计包装Filter;这种Filter能够工作在用户模式下,外观、控制方法跟普通Filter一样,而包装Filter内部完成与硬件驱动程序的交互。这样的设计,使得编写DirectShow应用程序的开发人员,从为支持硬件而需做出的特殊处理中解脱出来。DirectShow已经集成的包装Filter,包括Audio Capture Filter(qcap.dll)、VfW Capture Filter(qcap.dll,Filter的Class Id为CLSID_VfwCapture)、TV Tuner Filter(KSTVTune.ax,Filter的Class Id为CLSID_CTVTunerFilter)、Analog Video Crossbar Filter(ksxbar.ax)、TV Audio Filter(Filter的Class Id为CLSID_TVAudioFilter)等;另外,DirectShow为采用WDM驱动程序的硬件设计了KsProxy Filter(Ksproxy.ax,)。我们来看一下结构图:

图1
从上图中,我们可以看出,Ksproxy.ax、Kstune.ax、Ksxbar.ax这些包装Filter跟其它普通的DirectShow Filter处于同一个级别,可以协同工作;用户模式下的Filter通过Stream Class控制硬件的驱动程序minidriver(由硬件厂商提供的实现对硬件控制功能的DLL);Stream Class和minidriver一起向上层提供系统底层级别的服务。值得注意的是,这里的Stream Class是一种驱动模型,它负责调用硬件的minidriver;另外,Stream Class的功能还在于协调minidriver之间的工作,使得一些数据可以直接在Kernel mode下从一个硬件传输到另一个硬件(或同一个硬件上的不同功能模块),提高了系统的工作效率。(更多的关于底层驱动程序的细节,请读者参阅Windows DDK。)

下面,我们分别来看一下几种常见的硬件。
VfW视频采集卡。这类硬件在市场上已经处于一种淘汰的趋势;新生产的视频采集卡一般采用WDM驱动模型。但是,DirectShow为了保持向后兼容,还是专门提供了一个包装Filter支持这种硬件。和其他硬件的包装Filter一样,这种包装Filter的创建不是像普通Filter一样使用CoCreateInstance,而要通过系统枚举,然后BindToObject。

音频采集卡(声卡)。声卡的采集功能也是通过包装Filter来实现的;而且现在的声卡大部分都有混音的功能。这个Filter一般有几个Input pin,每个pin都代表一个输入,如Line In、Microphone、CD、MIDI等。值得注意的是,这些pin代表的是声卡上的物理输入端子,在Filter Graph中是永远不会连接到其他Filter上的。声卡的输出功能,可以有两个Filter供选择:DirectSound Renderer Filter和Audio Renderer (WaveOut) Filter。注意,这两个Filter不是上述意义上的包装Filter,它们能够同硬件交互,是因为它们使用了API函数:前者使用了DirectSound API,后者使用了waveOut API。这两个Filter的区别,还在于后者输出音频的同时不支持混音。(顺便说明一下,Video Renderer Filter能够访问显卡,也是因为使用了GDI、DirectDraw或Direct3D API。)如果你的机器上有声卡的话,你可以通过GraphEdit,在Audio Capture Sources目录下看到这个声卡的包装Filter。

WDM驱动的硬件(包括视频捕捉卡、硬件解压卡等)。这类硬件都使用Ksproxy.ax这个包装Filter。Ksproxy.ax实现了很多功能,所以有“瑞士军刀”的美誉;它还被称作为“变色龙Filter”,因为该Filter上定义了统一的接口,而接口的实现因具体的硬件驱动程序而异。在Filter Graph中,Ksproxy Filter显示的名字为硬件的Friendly name(一般在驱动程序的.inf文件中定义)。我们可以通过GraphEdit,在WDM Streaming开头的目录中找到本机系统中安装的WDM硬件。因为KsProxy.ax能够代表各种WDM的音视频设备,所以这个包装Filter的工作流程有点复杂。这个Filter不会预先知道要代表哪种类型的设备,它必须首先访问驱动程序的属性集,然后动态配置Filter上应该实现的接口。当Ksproxy Filter上的接口方法被应用程序或其他Filter调用时,它会将调用方法以及参数传递给驱动程序,由驱动程序最终完成指定功能。除此以外,WDM硬件还支持内核流(Kernel Streaming),即内核模式下的数据传输,而无需经过到用户模式的转换。因为内核模式与用户模式之间的相互转换,需要花费很大的计算量。如果使用内核流,不仅可以避免大量的计算,还避免了内核数据与主机内存之间的拷贝过程。在这种情况下,用户模式的Filter Graph中,即使pin之间是连接的,也不会有实际的数据流动。典型的情况,如带有Video Port Pin的视频捕捉卡,Preview时显示的图像就是在内核模式下直接传送到显卡的显存的。所以,你也休想在VP Pin后面截获数据流。

讲到这里,我想大家应该对DirectShow对硬件的支持问题有了一个总体的认识。对于应用程序开发人员来说,这方面的内容不用研究得太透,而只需作为背景知识了解一下就好了。其实,大量繁琐的工作DirectShow已经帮我们做好了。
//陆其明文章结束

Direcshow中视频捕捉的Filter
Pin的种类
捕捉Filter一般都有两个或多个输出pin,他们输出的媒体类型都一样,比如预览pin和捕捉pin,因此根据媒体类型就不能很好的区别这些pin。此时就要根据pin的功能来区别每个pin了,每个pin都有一个GUID,称为pin的种类。
如果想仔细的了解pin的种类,请看后面的相关内容Working with Pin Categories。对于大多数的应用来说,ICaptureGraphBuilder2提供了一些函数可以自动确定pin的种类。
预览pin和捕捉pin
视频捕捉Filter都提供了预览和捕捉的输出pin,预览pin用来将视频流在屏幕上显示,捕捉pin用来将视频流写入文件。
预览pin和输出pin有下面的区别:
1 为了保证捕捉pin对视频桢流量,预览pin必要的时候可以停止。
2 经过捕捉pin的视频桢都有时间戳,但是预览pin的视频流没有时间戳。
预览pin的视频流之所以没有时间戳的原因在于filter图表管理器在视频流里加一个很小的latency,如果捕捉时间被认为就是render时间的话,视频renderFilter就认为视频流有一个小小的延迟,如果此时render filter试图连续播放的时候,就会丢桢。去掉时间戳就保证了视频桢来了就可以播放,不用等待,也不丢桢。
预览pin的种类GUID为PIN_CATEGORY_PREVIEW
捕捉pin的种类GUID为PIN_CA

  • 0
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
DirectShow是Windows平台上的一个多媒体框架,用于处理音频和视频流。使用DirectShow捕捉摄像头视频数据需要以下步骤: 1. 枚举可用的视频捕捉设备并选择需要使用的设备。 2. 创建一个Filter Graph Manager对象,用于管理DirectShow图形。 3. 创建一个视频捕捉设备的Filter对象,并添加到Filter Graph Manager。 4. 创建一个Sample Grabber Filter对象,用于获取视频数据。 5. 创建一个Null Renderer对象,用于显示视频数据。 6. 将Sample Grabber Filter对象和Null Renderer对象添加到Filter Graph Manager,并建立连接。 7. 设置Sample Grabber Filter对象的回调函数,用于处理视频数据。 8. 开始视频捕捉。 以下是C++代码示例: ```cpp #include <dshow.h> // 枚举可用的视频捕捉设备 HRESULT EnumerateDevices(REFGUID category, IEnumMoniker** ppEnum) { // 创建系统设备枚举器 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum); if (FAILED(hr)) { return hr; } // 枚举视频捕捉设备 hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0); if (hr == S_FALSE) { hr = VFW_E_NOT_FOUND; } pDevEnum->Release(); return hr; } // 创建Filter Graph Manager对象 IGraphBuilder* pGraphBuilder = NULL; IMediaControl* pMediaControl = NULL; IMediaEventEx* pMediaEvent = NULL; HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraphBuilder); if (FAILED(hr)) { return hr; } // 创建视频捕捉设备的Filter对象 IEnumMoniker* pEnumMoniker = NULL; hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnumMoniker); if (FAILED(hr)) { return hr; } IMoniker* pMoniker = NULL; ULONG cFetched; while (pEnumMoniker->Next(1, &pMoniker, &cFetched) == S_OK) { IBaseFilter* pFilter = NULL; hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pFilter); if (SUCCEEDED(hr)) { // 添加到Filter Graph Manager pGraphBuilder->AddFilter(pFilter, L"Video Capture"); pFilter->Release(); } pMoniker->Release(); } pEnumMoniker->Release(); // 创建Sample Grabber Filter对象 ISampleGrabber* pSampleGrabber = NULL; hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pSampleGrabber); if (FAILED(hr)) { return hr; } // 设置Sample Grabber Filter对象的回调函数 hr = pSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber); if (FAILED(hr)) { return hr; } AM_MEDIA_TYPE mt; ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE)); mt.majortype = MEDIATYPE_Video; mt.subtype = MEDIASUBTYPE_RGB24; mt.formattype = FORMAT_VideoInfo; hr = pGrabber->SetMediaType(&mt); if (FAILED(hr)) { return hr; } hr = pGrabber->SetCallback(&SampleGrabberCallback, 0); if (FAILED(hr)) { return hr; } // 创建Null Renderer对象 IBaseFilter* pNullRenderer = NULL; hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pNullRenderer); if (FAILED(hr)) { return hr; } // 将Sample Grabber Filter对象和Null Renderer对象添加到Filter Graph Manager,并建立连接 hr = pGraphBuilder->AddFilter(pSampleGrabber, L"Sample Grabber"); if (FAILED(hr)) { return hr; } hr = pGraphBuilder->AddFilter(pNullRenderer, L"Null Renderer"); if (FAILED(hr)) { return hr; } IPin* pGrabberOut = GetPin(pSampleGrabber, PINDIR_OUTPUT); IPin* pRendererIn = GetPin(pNullRenderer, PINDIR_INPUT); hr = pGraphBuilder->Connect(pGrabberOut, pRendererIn); if (FAILED(hr)) { return hr; } // 开始视频捕捉 hr = pGraphBuilder->QueryInterface(IID_IMediaControl, (void**)&pMediaControl); if (FAILED(hr)) { return hr; } hr = pMediaControl->Run(); if (FAILED(hr)) { return hr; } // 处理视频数据的回调函数 HRESULT SampleGrabberCallback::SampleCB(double Time, IMediaSample *pSample) { BYTE* pData = NULL; pSample->GetPointer(&pData); long lDataLen = pSample->GetActualDataLength(); // 处理视频数据 return S_OK; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值