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是一个流程清晰,开发easy的多媒体开发工具。我们在使用DirectX为我们提供的Filter构建多媒体功能的同一时候,也能够自己着手创建具备特定功能的Filter。总之,Direct系统还是一个巨大的宝藏,等待着我们去发掘和开採。

用DirectShow实现视频採集-流程构建的更多相关文章

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

    最新手机採集推送直播监控以及EasyDarwin开源流媒体平台的版本号及代码: EasyDarwin 开源流媒体云平台:https://github.com/easydarwin EasyClient ...

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

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

  3. 用DirectShow实现视频采集-流程构建

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

  4. XP下採用DirectShow採集摄像头

    转载请标明是引用于 http://blog.csdn.net/chenyujing1234 欢迎大家提出意见,一起讨论! 须要演示样例源代码的请独自联系我. 前提: 摄像头能正常工作.摄像头有创建di ...

  5. Android摄像头採集的视频数据流怎样通过Socket实时发送到目标服务端

    分两块: 1.取得摄像头採集的视频流 2.发送到server端 protected MediaRecorder mMediaRecorder; private LocalServerSocket mL ...

  6. 【视频合集】极客时间 react实战进阶45讲 【更新中】

    https://up2.v.sharedaka.com/video/ochvq0AVfpa71A24bmugS5EewhFM1553702519936.mp4 01 React出现的历史背景及特性介绍 ...

  7. 精彩看点 | GIAC大会PPT+视频合集全量放送!

    GIAC是中国互联网技术领域的行业盛事,每年从互联网架构最热门的系统架构设计.人工智能.机器学习.工程效率.区块链.分布式架构等领域甄选前沿有典型代表的技术创新及研发实践的架构案例,分享他们在本年度最 ...

  8. 开源 java CMS - FreeCMS2.3 Web页面信息採集

    原文地址:http://javaz.cn/site/javaz/site_study/info/2015/23312.html 项目地址:http://www.freeteam.cn/ Web页面信息 ...

  9. python爬虫之採集——360联想词W2版本号

    http://blog.csdn.net/recsysml/article/details/30541197,我的这个博文介绍了对应的简单的方法做一个联想词的爬虫,并且还承诺了下面优化: 下一版本号的 ...

随机推荐

  1. ASP.NET CORE Web浏览器和Web服务器

    //web浏览器 //浏览器本质的原理:浏览器向服务器发请求,服务器把请求的内容返回给浏览器,然后浏览器把返回的内容绘制成一个图形化的界面 //Socket一种通讯交流的技术 //qq用户把信息通过s ...

  2. 本博客迁移到Github,之后停止更新

    本博客之后停止更新或者更新不是很及时,关注博客请移步   http://waylife.github.io 或者 http://blog.13kbook.com   谢谢支持. Update Time ...

  3. git学习一

    一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以 ...

  4. 3. 使用绘图API自定义视图 --- 旋转的方块

    import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; impor ...

  5. POJ 2728 Desert King 01分数规划,最优比率生成树

    一个完全图,每两个点之间的cost是海拔差距的绝对值,长度是平面欧式距离, 让你找到一棵生成树,使得树边的的cost的和/距离的和,比例最小 然后就是最优比例生成树,也就是01规划裸题 看这一发:ht ...

  6. hadoop1.2.1 伪分布式配置

    主要配置 core-site.xml hdfs-site.xml mapred-site.xml

  7. 利用flashback query 恢复表数据

    flashback query可以查询过去某个时间点对象的状态,从而可以利用此来进行恢复数据 1 准备测试数据 用普通用户创建一个表,表中插入部分数据: SQL> show user USER ...

  8. lighttpd mod_status模块

    用过nginx的status可以查看服务器的状态,之后就想lighttpd有没有这样的模块呢 之后看下配置文件,真的有,然后就试下 第一步, "mod_auth" 把这个前面的#号 ...

  9. SendMessage()、WPARAM、LPARAM函数使用例子(转)

      http://chujiaba.blog.163.com/blog/static/18991813720106209350592/ 2010-07-20 21:35:00|  分类: C |  标 ...

  10. [Hive - LanguageManual] Create/Drop/Alter -View、 Index 、 Function

    Create/Drop/Alter View Create View Drop View Alter View Properties Alter View As Select Version info ...