用DirectShow实现视频采集
DirectShow作为DirectX的一个子集,它为用户提供了强大、方便的多媒体开接口,并且它拥有直接操作硬件的能力,这使得它的效率远胜于用GDI等图形方式编写的多媒体程序。这里结合实践中运用DirectShow实现视频采集(WIN32) 来加深对DirectShow的理解和操作能力。
1.系统环境及开发环境
a.系统支持DirectX(Win 2K以上系统)
b.VC++ 6.0安装有DirectX SDK(最好与系统支持的DirectX版本相同)
c.视频采集设备(如USB摄像头,本文以USB PC Camera 310P为例)
2.基本思想
DirectShow的基本原理是多媒体数据在过滤器图表(Filter Graph)中流动,通过过滤器图表中各过滤器(Filter)实现在功能,最终实现多媒体数据在渲染过滤器(Vendering Filters)中的显示和回放。
前面我们已经知道,一般过滤器可分为三类:源过滤器(Source Filters)、转换过滤器(Transform Filters)、渲染过滤器(Vendering Filters)。它们分别完成数据提供、数据格式转换(压缩编码等)和数据渲染和回放功能。所以,为了实现在WIN32系统下的视频采集,我们首先要构造出一个适当的过滤器图表,然后通过应用程序对过滤器图表的管理来完成视频采集的功能。
这里我们一般需要2至3个过滤器。为什么这个数字会不准确呢?那是因为一方面系统采集设备的驱动模型是不确定的(一般有WDM和VFW两种);另一方面同一采集设备它们的Filter会由于驱动程序的差异造成Filter中引脚(Pin)的不一致;还有就 是不同总线的采集设备(PCI、USB、AGP)它们的Filter也是不一致的。比如:同为USB摄像头,有些Filter有两个输出引脚(Capture和Preview);而有些Filter则只有一个输出引脚(Capture)。这里Previe w引脚用来将做视频预览,Capture引脚用来将输入数据以供编码、保存等用处。
这几个过滤器分别是:
a.Video Capture Filter 采集设备Filter
b.Smart Tee Filter 将没有Preview引脚Filter的Capture引脚分为两支数据流(可选)
c.Video Venderer 视频渲染及回放Filter
通过上面3个过滤器,我们可以构造出一个完整的视频采集过滤器图表(如图1)
图1
我们也可以对上面的过滤器图表稍做修改,将它变为一个既可以预览视频,又可以将视频保存为媒体文件的图表(如图2)。
图2
图表构造出来后,接下来就午剩下具体的实现了,我们只需依次构造每个Filter,然后将各信Filter的Pin按序相连即可完成图表的构造。最后,我们通过应用程序向图表发送命令(通过图表管理器完成)来控制整个视频采集的流程。
3.具体实现
首先我们需要创建几个接口全局变量。
IGraphBuilder *pGraph; //过滤器图表管理器
ICaptureGraphBuilder2 *pBuild; //视频采集过滤器图表
IBaseFilter *pCap; //Video Capture Filter
IBaseFilter *pSmartTee; //Smart Tee Filter
IBaseFilter *pRender; //Video Renderer Filter
IMediaControl *pControl; //用户命令接口,用来控制过滤器图表
IMediaEvent *pEvent; //过滤器图表事件接口
1) 采集设备枚举
在构造Video Capture Filter前,我们必须列举出系统的所有采集设备,然后才能根据列举的设备名称创建Video Capture Filter。列举设备的函数实现如下
bool ListCaptureDevices()
{
ICreateDevEnum *pDevEnum = NULL; //设备枚举器Interface
IEnumMoniker *pEnum = NULL; //名称枚举Interface
// Create the System Device Enumerator.
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
reinterpret_cast<void**>(&pDevEnum)); //创建设备枚举COM对象
if (SUCCEEDED(hr))
{
// Create an enumerator for the video capture category.
hr = pDevEnum->CreateClassEnumerator(
CLSID_VideoInputDeviceCategory,
&pEnum, 0); //创建视频采集设备枚举COM对象
}
////////////////////////////////////////////////////////////
IMoniker *pMoniker = NULL;
if(pEnum == NULL)
{
return false; //如果没有设备,返回
}
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) //依次枚举,直至为空
{
IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
(void**)(&pPropBag));
if (FAILED(hr))
{
pMoniker->Release();
continue; // Skip this one, maybe the next one will work.
}
// Find the description or friendly name.
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"Description", &varName, 0);
if (FAILED(hr))
{
hr = pPropBag->Read(L"FriendlyName", &varName, 0); //设备友好名称
}
if (SUCCEEDED(hr))
{
// Add it to the application's list box.
char displayName[1024];
WideCharToMultiByte(CP_ACP,0,varName.bstrVal,-1,displayName,1024,"",NULL);
m_nList.AddString(displayName); //字符转换,枚举名称均为UNICODE码
VariantClear(&varName);
}
pPropBag->Release();
pMoniker->Release();
}
return true;
}
2)创建Video Capture Filter
根据枚举出来的设备友好名称(FriendlyName)创建Video Capture Filter。
bool CTest_capDlg::CreateHardwareFilter(const char * friendlyName)
{ //将friendlyName与所有的设备名称依次对比,如果相同,则创建Filter
ICreateDevEnum * enumHardware = NULL;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,NULL,CLSCTX_ALL
,IID_ICreateDevEnum,(void **)&enumHardware);
if( FAILED(hr) )
{
return false;
}
IEnumMoniker * enumMoniker = NULL;
hr = enumHardware->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&enumMoniker,0);
if(enumMoniker)
{
enumMoniker->Reset();
ULONG fetched = 0;
IMoniker * moniker = NULL;
char friendlyName[256];
while(!pCap && SUCCEEDED(enumMoniker->Next(1,&moniker,&fetched)) && fetched)
{
if(moniker)
{
IPropertyBag * propertyBag = NULL;
VARIANT name;
friendlyName[0]=0;
hr=moniker->BindToStorage(0,0,IID_IPropertyBag,(void **)&propertyBag);
if(SUCCEEDED(hr))
{
name.vt=VT_BSTR;
hr = propertyBag->Read(L"FriendlyName",&name,NULL);
}
else
return false;
if(SUCCEEDED(hr))
{
WideCharToMultiByte(CP_ACP,0,name.bstrVal,-1,friendlyName,256,NULL,NULL);
moniker->BindToObject(0,0,IID_IBaseFilter,(void **)&pCap);
}
else
return false;
if(propertyBag)
{
propertyBag->Release();
propertyBag=NULL;
}
moniker->Release();
}
}
enumMoniker->Release();
}
enumHardware->Release();
return true;
}
3)创建视频采集过滤器图表
DirectX较高版本中一般都为开发者提供了一个ICaptureGraphBuilder2接口,开发者可以通过它方便地创建视频采集过滤器图表,然后再将它添加到IGraphBuilder图表管理器中(如图3)。
图3
bool InitCaptureGraphBuilder()
{
HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pBuild);
if(FAILED(hr))
return false;
hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void**)&pGraph);
if(FAILED(hr))
{
pBuild->Release();
return false;
}
pBuild->SetFiltergraph(pGraph); ///////////////////// 过滤器图表添加到管理器中
pGraph->QueryInterface(IID_IMediaControl,(void **)&pControl);
pGraph->QueryInterface(IID_IMediaEvent,(void **)&pEvent);
return true;
}
4)创建剩余的Smart Tee和Video Renderer Filter并连接成完整的图表
在创建完Video Capture Filter后,我们需要将Filter添加到过滤器图表中。
pGraph->AddFilter(pCap,L"Capture Filter");
然后,我们创建剩余的Filter并相连即可,值得注意的是:ICaptureGraphBuilder2为用户提供了一个RenderStream函数,它可以自动构建Smart Tee和Video Renderer Filter并将它们连接成一个完整的图表,从而完成视频采集的功能。
pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
pCap, NULL, NULL);
为了说明整个过程,这里我们按部就搬,依次创建各个Filter。
Smart Tee
CoCreateInstance(CLSID_SmartTee,NULL,CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void **)&pSmartTee);
Video Renderer Filter
CoCreateInstance(CLSID_VideoRenderer,NULL,CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void **)&pRender);
创建好各个Filter后,我们依次取得它们的引脚(Pin),将它们按序相连即可。
IPin * GetSmartTeeInputPin() //取得Smart Tee 输入引脚
{
if(pSmartTee)
{
IPin * pPin;
HRESULT hr = pSmartTee->FindPin(L"Input",&pPin);
if(SUCCEEDED(hr))
{
pPin->Release();
return pPin;
}
}
return NULL;
}
IPin * GetSmartTeeCapturePin() //取得Smart Tee Capture引脚
{
if(pSmartTee)
{
IPin * pPin;
HRESULT hr = pSmartTee->FindPin(L"Capture",&pPin);
if(SUCCEEDED(hr))
{
pPin->Release();
return pPin;
}
}
return NULL;
}
IPin * GetSmartTeePreviewPin() //取得Smart Tee Preview引脚
{
if(pSmartTee)
{
IPin * pPin;
HRESULT hr = pSmartTee->FindPin(L"Preview",&pPin);
if(SUCCEEDED(hr))
{
pPin->Release();
return pPin;
}
}
return NULL;
}
IPin * GetRendererPin() //取得Video Renderer Filter的输入Pin
{
if(pBuild)
{
IPin * pPin;
HRESULT hr = pBuild->FindPin(pRender,PINDIR_INPUT,NULL,NULL,FALSE,0,&pPin);
if(SUCCEEDED(hr))
{
pPin->Release();
return pPin;
}
}
return NULL;
}
将各个引脚按序连接:
IPin * pOut = FindVideoPin(&PIN_CATEGORY_CAPTURE);
IPin * pIn = GetSmartTeeInputPin();
pGraph->Connect(pOut,pIn); //Video Capture Filter’ Capture Pin à Smart Tee’Input Pin
IPin * mOut = GetSmartTeePreviewPin();
IPin * mIn = GetRendererPin();
pGraph->Connect(mOut,mIn); //Smart Tee’s Preview Pin à Video Renderer Filter’s Input Pin
这样,一个完整的视频采集图表管理器就构造完成了。
5)开始视频采集
通过用户命令接口,我们可以方便的完成开始,暂停,停止视频采集。
pControl->Run();
pControl->Stop();
4.小结
通过上述视频采集过程的实现,不难发现DirectShow是一个流程清晰,开发容易的多媒体开发工具。我们在使用DirectX为我们提供的Filter构建多媒体功能的同时,也可以自己着手创建具备特定功能的Filter。总之,Direct系统还是 一个巨大的宝藏,等待着我们去发掘和开采。
分享到:
相关推荐
本文将详细讲解如何利用Directshow SDK和C++语言来实现视频采集功能。 首先,我们需要理解Directshow的基本架构。Directshow分为两大部分:Filter Graph(过滤图)和Filters(过滤器)。Filter Graph是整个流程的...
在这个主题中,我们将深入探讨如何利用DirectShow来实现视频采集与视频数据的抓取。 首先,理解DirectShow的基本结构至关重要。DirectShow由许多组件(filters)组成,每个组件负责特定的媒体处理任务,如捕获设备...
在这个"DirectShow实现视频采集编码Demo"项目中,我们将深入探讨如何利用DirectShow进行视频采集、预览,并通过ffmpeg库将采集的数据编码为MPEG文件,同时还会涉及图像抓拍功能。 1. **DirectShow视频采集**:...
它提供了丰富的API接口,使得开发者能够构建高质量的媒体应用程序,如视频播放器和摄像头视频采集应用。在这个项目中,我们将探讨如何利用DirectShow结合MFC(Microsoft Foundation Classes)库来创建一个既能够播放...
通过深入研究"DxSnap"项目提供的源码,开发者可以学习到如何在C#环境下高效地使用DirectShow库,从而实现视频采集和图片抓取功能,这对于开发多媒体应用程序或者监控系统非常有用。这个实例不仅提供了理论知识,还...
总的来说,AVCap项目是DirectShow技术在视频采集应用中的实践案例,它展示了如何利用DirectShow API实现视频数据的捕获和处理。通过研究和学习这个项目,开发者可以深入了解DirectShow的工作原理,并能够构建自己的...
在“用DirectShow实现视频采集 - VC编程”中,我们可以预期找到使用Visual C++进行Directshow编程的示例。VC++是开发Directshow应用程序的常见工具,通过COM接口可以直接操作Directshow的过滤器和图。 "DirectShow...
本项目的核心是通过DirectShow来实现视频的采集,并将采集到的帧保存为两种常见格式:BMP(位图)和YUV。 在视频采集的过程中,DirectShow首先会建立一个过滤器图形(Filter Graph)。这个图形是由多个过滤器...
第9章 基于DirectShow SDK实现视频图像的预览与采集 本章技术要点是基于DirectShow SDK实现视频.图像的预览与采集从功能实现类的分析到案例界面设计详细介绍视频采集的有关知识。以“使用经典采集技术实现视频捕获”...
一个关于使用directshow实现视频的采集、播放、回放等功能的文档
DirectShow实务精选--陆其明(源代码)第二部分(总共两部分) DirectShow实务精选--陆其明(源代码)第二部分(总共两部分)
总之,DirectShow提供了一个高效且灵活的平台,用于实现多媒体处理任务,如USB摄像头的视频采集和AVI文件的保存。通过理解和掌握DirectShow的核心概念和技术,开发者能够创建出各种定制化的多媒体应用程序,满足不同...
DirectShow实务精选--陆其明(源代码)第一部分(总共两部分)
安装DeckLink视频采集卡,使用DirectShow视频采集流程
总之,`playcap.cpp`和`playcap.h`的代码示例提供了一个简单的DirectShow视频采集起点,帮助开发者快速理解和实现视频采集功能。结合`strmbasd.lib`,你可以构建自己的视频采集应用程序,灵活应对各种多媒体需求。在...
通过熟悉GraphEdit的使用,开发者能够更有效地创建和优化多媒体处理流程,实现高效、流畅的音频和视频体验。无论是对初学者还是经验丰富的开发者,GraphEdit都是理解和调试DirectShow应用的宝贵资源。
### 基于DirectShow技术的视频采集方案的实现 #### DirectShow技术基本概念与优势 DirectShow技术作为Microsoft在1995年推出的新一代多媒体软件开发包,旨在克服原有的VFW(Video for Windows)技术的局限性,提供...
总结来说,DirectShow的视频采集技术是利用微软的多媒体框架实现从硬件设备捕获视频数据,并通过一系列处理和操作,最终实现显示或保存。NMake作为编译工具,帮助开发者高效组织和执行编译任务,使得基于DirectShow...
在"基于DirectShow的视频采集系统的设计与实现"这篇论文中,作者探讨了如何利用DirectShow来构建一个强大的视频采集系统。 视频采集是多媒体系统的基础,而基于DirectShow的系统通过其优秀的扩展性能,可以轻松添加...
在给定的场景中,"directshow多路视频采集"指的是使用DirectShow来同时获取和处理来自多个视频源的信号。 在Visual Studio 2008环境中,我们可以利用DirectShow.NET库,这是一个.NET Framework的包装器,它为...