用DirectShow实现视频采集

 

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

系统环境及开发环境

  • 系统支持DirectX(Win 2K以上系统)
  • VC++ 6.0安装有DirectX SDK(最好与系统支持的DirectX版本相同)
  • 视频采集设备(如USB摄像头,本文以USB PC Camera 310P为例)

Contents

[hide]
[ edit]

基本思想

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引脚用来将输入数据以供编码、保存等用处。

这几个过滤器分别是:

  • Video Capture Filter 采集设备Filter
  • Smart Tee Filter 将没有Preview引脚Filter的Capture引脚分为两支数据流(可选)
  • Video Venderer 视频渲染及回放Filter

通过上面3个过滤器,我们可以构造出一个完整的视频采集过滤器图表(如图1)

o_gragh1.jpg 图1

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

o_graph2.jpg 图2

图表构造出来后,接下来就剩下具体的实现了,我们只需依次构造每个Filter,然后将各信Filter的Pin按序相连即可完成图表的构造。最后,我们通过应用程序向图表发送命令(通过图表管理器完成)来控制整个视频采集的流程。

[ edit]

具体实现

首先我们需要创建几个接口全局变量。

IGraphBuilder *pGraph; //过滤器图表管理器
ICaptureGraphBuilder2 *pBuild; //视频采集过滤器图表
IBaseFilter *pCap; //Video Capture Filter
IBaseFilter *pSmartTee; //Smart Tee Filter
IBaseFilter *pRender; //Video Renderer Filter
IMediaControl *pControl; //用户命令接口,用来控制过滤器图表
IMediaEvent *pEvent; //过滤器图表事件接口
[ edit]

采集设备枚举

在构造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;
}
[ edit]

创建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;
}
[ edit]

创建视频采集过滤器图表

DirectX较高版本中一般都为开发者提供了一个ICaptureGraphBuilder2接口,开发者可以通过它方便地创建视频采集过滤器图表,然后再将它添加到IGraphBuilder图表管理器中(如图3)。

o_graph3.jpg 图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;
}
[ edit]

创建剩余的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

这样,一个完整的视频采集图表管理器就构造完成了。

[ edit]

开始视频采集

通过用户命令接口,我们可以方便的完成开始,暂停,停止视频采集。

pControl->Run();
pControl->Stop();
[ edit]

小结

通过上述视频采集过程的实现,不难发现DirectShow是一个流程清晰,开发容易的多媒体开发工具。我们在使用DirectX为我们提供的 Filter构建多媒体功能的同时,也可以自己着手创建具备特定功能的Filter。总之,DirectX系统还是一个巨大的宝藏,等待着我们去发掘和开采。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DirectShow是一个用于处理音频和视频流的微软API。它可以用于实现视频采集,也可以用于实现视频播放、视频编辑和视频转码等多种应用。 以下是使用DirectShow实现视频采集的基本步骤: 1.创建DirectShow Filter Graph对象 在代码中创建一个Filter Graph对象,用于管理视频采集过程中的各个组件。 2.创建视频采集设备 使用DirectShow的Device Enumerator接口枚举出系统中可用的视频采集设备,并选择其中一个进行采集。 3.创建视频采集滤镜 使用DirectShow的接口创建视频采集滤镜,并将其添加到Filter Graph中。 4.设置视频采集参数 对视频采集滤镜进行设置,如设置视频格式、分辨率、帧率等参数。 5.创建视频编码滤镜(可选) 如果需要对采集到的视频进行编码,可以使用DirectShow的接口创建视频编码滤镜,并将其添加到Filter Graph中。 6.创建渲染器 使用DirectShow的接口创建一个渲染器,并将其添加到Filter Graph中。 7.连接各个组件 使用DirectShow的接口将视频采集滤镜、视频编码滤镜(可选)和渲染器连接起来,形成一个完整的视频采集流程。 8.开始采集 调用Filter Graph对象的Run()方法,开始视频采集。 9.停止采集 调用Filter Graph对象的Stop()方法,停止视频采集。 10.释放资源 释放创建的各个组件,并释放Filter Graph对象。 以上是使用DirectShow实现视频采集的基本步骤。具体实现可以参考DirectShow SDK中的示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值