用DirectShow实现视频采集

发布时间:2017-3-29 15:26:13 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"用DirectShow实现视频采集",主要涉及到用DirectShow实现视频采集方面的内容,对于用DirectShow实现视频采集感兴趣的同学可以参考一下。

http://blog.csdn.net/dansin/archive/2005/03/27/331854.aspx 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较高版本中一般都为开发者提供了一个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系统还是一个巨大的宝藏,等待着我们去发掘和开采。    

上一篇:网络工程师应该掌握的44个(路由器问题)知识要点
下一篇:

相关文章

相关评论

本站评论功能暂时取消,后续此功能例行通知。

一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!

二、互相尊重,对自己的言论和行为负责。

好贷网好贷款