zoukankan      html  css  js  c++  java
  • 用DirectShow实现视频採集-流程构建

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

    1.系统环境及开发环境

      

    l       系统支持DirectXWin 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系统下的视频採集,我们首先要构造出一个适当的过滤器图表,然后通过应用程序对过滤器图表的管理来完毕视频採集的功能。 

    这里我们一般须要23个过滤器。为什么这个数字会不准确呢?那是由于一方面系统採集设备的驱动模型是不确定的(一般有WDMVFW两种);还有一方面同一採集设备它们的Filter会由于驱动程序的差异造成Filter中引脚(Pin)的不一致;还有就是不同总线的採集设备(PCIUSBAGP)它们的Filter也是不一致的。比方:同为USB摄像头,有些Filter有两个输出引脚(CapturePreview);而有些Filter则仅仅有一个输出引脚(Capture)。这里Preview引脚用来将做视频预览,Capture引脚用来将输入数据以供编码、保存等用处。

    这几个过滤器各自是:

    l       Video Capture Filter 採集设备Filter

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

    l       Video Venderer  视频渲染及回放Filter

    通过上面3个过滤器,我们能够构造出一个完整的视频採集过滤器图表(如图1 

     

                                                                        1

    我们也能够对上面的过滤器图表稍做改动,将它变为一个既能够预览视频,又能够将视频保存为媒体文件的图表(如图2)。

     

                                                                       2

    图表构造出来后,接下来就午剩下详细的实现了,我们仅仅需依次构造每一个Filter,然后将各信FilterPin按序相连就可以完毕图表的构造。最后,我们通过应用程序向图表发送命令(通过图表管理器完毕)来控制整个视频採集的流程。

    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 TeeVideo Renderer Filter并连接成完整的图表 

    在创建完Video Capture Filter后,我们须要将Filter加入到过滤器图表中。

    pGraph->AddFilter(pCap,L"Capture Filter");

    然后,我们创建剩余的Filter并相连就可以,值得注意的是:ICaptureGraphBuilder2为用户提供了一个RenderStream函数,它能够自己主动构建Smart TeeVideo 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系统还是一个巨大的宝藏,等待着我们去发掘和开採。

  • 相关阅读:
    由Kaggle竞赛wiki文章流量预测引发的pandas内存优化过程分享
    Python奇技淫巧 持续更新中....
    波士顿房价预测 最简单入门机器学习 Jupyter
    机器学习基本流程整理 这一篇就够啦
    阅读kaggle上大佬们的kernel
    wechat+项目开源分享 让你的微信账号有趣起来
    ✍36 PyQt5 模块错误排除
    ✍41 搭建wordpress&花生壳穿透
    ✍44.vs code远程编辑代码
    ✍42.图片识别相关cv2
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4022342.html
Copyright © 2011-2022 走看看