DirectShow作为DirectX的一个子集,它为用户提供了强大、方便的多媒体开接口,并且它拥有直接操作硬件的能力,这使得它的效率远胜于用GDI等图形方式编写的多媒体程序。前面一篇文章已经对DirectShow作了粗略的介绍,阐述了它的原理及一些编程方法。这里结合实践中运用DirectShow实现视频采集(WIN32)来加深对DirectShow的理解和操作能力。

1.系统环境及开发环境

l       系统支持DirectX(Win 2K以上系统)

l       VC++  6.0安装有DirectX  SDK(最好与系统支持的DirectX版本相同)

l       视频采集设备(如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)。这里Preview引脚用来将做视频预览,Capture引脚用来将输入数据以供编码、保存等用处。

这几个过滤器分别是:

l       Video Capture Filter 采集设备Filter

l       Smart Tee Filter 将没有Preview引脚Filter的Capture引脚分为两支数据流(可选)

l       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较高版本中一般都为开发者提供了一个)。

图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实现视频采集-流程构建的更多相关文章

  1. 提取DirectShow中视频采集的数据

    DirectShow中,数据流(Data Flow)都是依次流过各个Filter的.它对数据的管理也有自己的方法,而且并没有向用户提供一个统一的接口,供用户操作数据流.这里以提取视频采集在的每帧为位图 ...

  2. (三)WebRTC手记之本地视频采集

    转自:http://www.cnblogs.com/fangkm/p/4374610.html 前面两篇文章介绍WebRTC的运行流程和使用框架接口,接下来就开始分析本地音视频的采集流程.由于篇幅较大 ...

  3. WebRTC手记之本地视频采集

    转载请注明出处:http://www.cnblogs.com/fangkm/p/4374610.html 前面两篇文章介绍WebRTC的运行流程和使用框架接口,接下来就开始分析本地音视频的采集流程.由 ...

  4. 用DirectShow实现视频採集-流程构建

    DirectShow作为DirectX的一个子集,它为用户提供了强大.方便的多媒体开接口,而且它拥有直接操作硬件的能力,这使得它的效率远胜于用GDI等图形方式编写的多媒体程序.前面一篇文章已经对Dir ...

  5. 监控视频采集与Web直播开发全流程分析

    内容概要: 摄像头 => FFmpeg => Nginx服务器 => 浏览器 从摄像头拉取rtsp流 转码成rtmp流向推流服务器写入 利用html5播放 1.开发流程 1.1 通过 ...

  6. DirectShow 进行视频预览和录制

    这一篇讲怎么采集摄像头图像并预览,以及录制视频到本地. 程序实现流程 这里通过使用 CaptureGraphBuilder 来简化 Graph 的创建流程. 具体流程如下: 初始化 COM 库 创建各 ...

  7. 嵌入式Linux的web视频服务器的构建

    http://blog.sina.com.cn/s/blog_53d02d550102v8bu.html随着嵌入式处理器和开源Linux 的广泛应用,各种视频服务在嵌入式系统中逐渐发展起来. 1.引言 ...

  8. 入门视频采集与处理(学会分析YUV数据)

    做视频采集与处理,自然少不了要学会分析YUV数据.因为从采集的角度来说,一般的视频采集芯片输出的码流一般都是YUV数据流的形式,而从视频处理(例如H.264.MPEG视频编解码)的角度来说,也是在原始 ...

  9. 手机Android音视频采集与直播推送,实现单兵、移动监控类应用

    从安卓智能手机.平板,到可穿戴的Android Ware.眼镜.手表.再到Android汽车.智能家居.电视,甚至最近看新闻,日本出的几款机器人都是Android系统的,再把目光放回监控行业,传统监控 ...

随机推荐

  1. Sonar项目主要指标以及代码坏味道详解

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6766994.html 众所周知Sona ...

  2. 17年年终总结——走过2017,迎来2018Flag

    引言 记得大概从大学毕业到现在都没有写作啥总结性的文章了(ps:应该是没写过啥长篇大论了),所以也不知道怎么个起头.我也闲聊的方式起头吧. 其实跨入18年就想着写篇总结,总结下自己17年的得与失,不足 ...

  3. Vue.js搭建路由报错 router.map is not a function,Cannot read property ‘component’ of undefined

    错误: 解决办法: 2.0已经没有map了,使用npm install vue-router@0.7.13 命令兼容1.0版本vue 但是安装完之后会出现一个错误: Cannot read prope ...

  4. 使用正则表达式和数组形式获取get方法传入的值

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  5. CNN网络架构演进:从LeNet到DenseNet

    卷积神经网络可谓是现在深度学习领域中大红大紫的网络框架,尤其在计算机视觉领域更是一枝独秀.CNN从90年代的LeNet开始,21世纪初沉寂了10年,直到12年AlexNet开始又再焕发第二春,从ZF ...

  6. JAVAEE——BOS物流项目04:学习计划、datagrid、分页查询、批量删除、修改功能

    1 学习计划 1.datagrid使用方法(重要) n 将静态HTML渲染为datagrid样式 n 发送ajax请求获取json数据创建datagrid n 使用easyUI提供的API创建data ...

  7. SeleniumIDE_初识

    版权声明:本文为博主原创文章,转载请注明出处. 学习Selenium,除了自己手动编写脚本,还可以使用Selenium IDE进行脚本录制. 安装Selenium IDE Selenium IDE是F ...

  8. 保存文件名至txt文件中,不含后缀

    准备深度学习的训练数据时,可能会用到将图片文件名保存到txt文件中,所以用python实现了该功能.输入参数只设了两个,图片存放路径,和输出的txt文件名. 代码里写死了只识别.jpg格式,并不进行目 ...

  9. 企业级数据库监控利器Lepus

    开篇介绍官方网站:http://www.lepus.cc开源企业级数据库监控系统简洁.直观.强大的开源数据库监控系统,MySQL/Oracle/MongoDB/Redis一站式性能监控,让数据库监控更 ...

  10. NoSQL性能测试工具YCSB-Running a Workload

    写在前面 目前,在系统设计中引入了越来越多的NoSQL产品,例如Redis/ MongoDB/ HBase等,其中性能指标往往会成为权衡不同NoSQL产品的关键因素.对这些产品在性能表现和产品选择上的 ...