原文:http://blog.csdn.net/nemojiang/article/details/653033?locationNum=7&fps=1

前言

  DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布。DirectShow为多媒体流的捕捉和回放提供了强有力的支持。用DirectShow开发应用程序,我们可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。

  DirectShow是基于COM的,为了编写DirectShow应用程序,需要了解COM客户程序编写的基础知识。DirectShow提供了大量的接口,但在编程中发现还是不够方便,如果能构建一个视频捕捉类把常用的一些动作封装起来,那么就更方便了。

  编程思路

  为了更加容易建立视频捕捉应用程序,DirectShow提供了一个叫做Capture Graph Builder的对象,Capture Graph Builder提供IcaptureGraphBuilder2接口,该接口可以建立和控制Capture Graph。

  建立视频捕捉程序,必须首先获取并初始化IcaptureGraphBuilder2接口,然后选择一个适当的视频捕捉设备。选择好设备后,为该设备创建Capture filter,然后调用AddFilter把Capture filter添加到Filter Graph。

  如果仅仅希望用摄像头来进行实时监控的话,只需要在上面的基础上调用ICaptureGraphBuilder2::RenderStream就可以了:

ICaptureGraphBuilder2 *pBuild; // Capture Graph Builder
//省略初始化部分代码
IBaseFilter *pCap; // Video capture filter.
//省略初始化和添加到Filter Graph部分代码
pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, NULL);

  DirectShow提供了一个捕捉静态图像的方法:使用Sample Grabber filter。依次按照以下三个步骤就可以了:

  第一步, 定义一个类实现Sample Grabber的回调接口IsampleGrabberCB:

class CSampleGrabberCB : public ISampleGrabberCB

 //在后面提供的类中具体完成
}
CSampleGrabberCB mCB;

  第二步、调用RenderStream依次把Still pin、Sample Grabber和系统默认Renderer Filter连接起来。

  第三步、配置Sample Grabber以捕获数据。

  视频捕捉类CCaptureVideo的具体实现

// CCaptureVideo视频捕捉类头文件
/////////////////////////////////////////////////////////////////////
#if !defined(AFX_CAPTUREVIDEO_H__F5345AA4_A39F_4B07_B843_3D87C4287AA0__INCLUDED_)
#define AFX_CAPTUREVIDEO_H__F5345AA4_A39F_4B07_B843_3D87C4287AA0__INCLUDED_
/////////////////////////////////////////////////////////////////////
// CaptureVideo.h : header file
/////////////////////////////////////////////////////////////////////
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <atlbase.h>
#include <windows.h>
#include <dshow.h>
#ifndef SAFE_RELEASE
#define SAFE_RELEASE( x ) /
if ( NULL != x ) /
{ /
 x->Release( ); /
 x = NULL; /
}
#endif
class CSampleGrabberCB;
class CCaptureVideo : public CWnd 
{
 friend class CSampleGrabberCB;
 public:
  void GrabOneFrame(BOOL bGrab);
  HRESULT Init(int iDeviceID,HWND hWnd);
  int EnumDevices(HWND hList);
  CCaptureVideo();
  virtual ~CCaptureVideo();
 private:
  HWND m_hWnd;
  IGraphBuilder *m_pGB;
  ICaptureGraphBuilder2* m_pCapture;
  IBaseFilter* m_pBF;
  IMediaControl* m_pMC;
  IVideoWindow* m_pVW;
  CComPtr<ISampleGrabber> m_pGrabber;
 protected:
  void FreeMediaType(AM_MEDIA_TYPE& mt);
  bool BindFilter(int deviceId, IBaseFilter **pFilter);
  void ResizeVideoWindow();
  HRESULT SetupVideoWindow();
  HRESULT InitCaptureGraphBuilder();
};
#endif // !defined(AFX_CAPTUREVIDEO_H__F5345AA4_A39F_4B07_B843_3D87C4287AA0__INCLUDED_)
//-------------------------------------------------------------------
// CCaptureVideo视频捕捉类实现文件CaptureVideo.cpp
//-------------------------------------------------------------------
// CaptureVideo.cpp: implementation of the CCaptureVideo class.
//
/////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "CaptureVideo.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
BOOL bOneShot=FALSE;//全局变量
class CSampleGrabberCB : public ISampleGrabberCB 
{
 public:
  long lWidth;
  long lHeight;
  TCHAR m_szFileName[MAX_PATH];// 位图文件名称
  CSampleGrabberCB( ){
   strcpy(m_szFileName, "c://donaldo.bmp");
  } 
  STDMETHODIMP_(ULONG) AddRef() { return 2; }
  STDMETHODIMP_(ULONG) Release() { return 1; }
  STDMETHODIMP QueryInterface(REFIID riid, void ** ppv){
  if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown ){ 
   *ppv = (void *) static_cast<ISampleGrabberCB*> ( this );
   return NOERROR;
  } 
  return E_NOINTERFACE;
 }
 STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample ){
 return 0;
}
STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize ){
 if( !bOneShot )return 0;
 
 if (!pBuffer)return E_POINTER;
 SaveBitmap(pBuffer, lBufferSize);
 bOneShot = FALSE;
 return 0;
}
//创建位图文件
BOOL SaveBitmap(BYTE * pBuffer, long lBufferSize )
{
 HANDLE hf = CreateFile(
 m_szFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL,
 CREATE_ALWAYS, NULL, NULL );
 if( hf == INVALID_HANDLE_VALUE )return 0;
  // 写文件头 
  BITMAPFILEHEADER bfh;
  memset( &bfh, 0, sizeof( bfh ) );
  bfh.bfType = ’MB’;
  bfh.bfSize = sizeof( bfh ) + lBufferSize + sizeof( BITMAPINFOHEADER );
  bfh.bfOffBits = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
  DWORD dwWritten = 0;
  WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
  // 写位图格式
  BITMAPINFOHEADER bih;
  memset( &bih, 0, sizeof( bih ) );
  bih.biSize = sizeof( bih );
  bih.biWidth = lWidth;
  bih.biHeight = lHeight;
  bih.biPlanes = 1;
  bih.biBitCount = 24;
  WriteFile( hf, &bih, sizeof( bih ), &dwWritten, NULL );
  // 写位图数据
  WriteFile( hf, pBuffer, lBufferSize, &dwWritten, NULL );
  CloseHandle( hf );
  return 0;
 }
};
CSampleGrabberCB mCB;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCaptureVideo::CCaptureVideo()
{
 //COM Library Intialization
 if(FAILED(CoInitialize(NULL))) /*, COINIT_APARTMENTTHREADED)))*/
 {
  AfxMessageBox("CoInitialize Failed!/r/n"); 
  return;
 }
 m_hWnd = NULL;
 m_pVW = NULL;
 m_pMC = NULL;
 m_pGB = NULL;
 m_pCapture = NULL; 
}
CCaptureVideo::~CCaptureVideo()
{
 // Stop media playback
 if(m_pMC)m_pMC->Stop();
 if(m_pVW){
  m_pVW->put_Visible(OAFALSE);
  m_pVW->put_Owner(NULL);
 }
 SAFE_RELEASE(m_pCapture);
 SAFE_RELEASE(m_pMC);
 SAFE_RELEASE(m_pGB);
 SAFE_RELEASE(m_pBF);
 CoUninitialize( );
}
int CCaptureVideo::EnumDevices(HWND hList)
{
 if (!hList)
  return -1;
 int id = 0;

 //枚举视频扑捉设备
 ICreateDevEnum *pCreateDevEnum;
 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);

 if (hr != NOERROR)return -1;
 CComPtr<IEnumMoniker> pEm;
 hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);

 if (hr != NOERROR)return -1;
 pEm->Reset();
 ULONG cFetched;
 IMoniker *pM;
 while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
 {
  IPropertyBag *pBag;
  hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
  if(SUCCEEDED(hr)) 
  {
   VARIANT var;
   var.vt = VT_BSTR;
   hr = pBag->Read(L"FriendlyName", &var, NULL);
   if (hr == NOERROR) 
   {
    TCHAR str[2048]; 
    id++;
    WideCharToMultiByte(CP_ACP,0,var.bstrVal, -1, str, 2048, NULL, NULL);
    ::SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)str);
    SysFreeString(var.bstrVal);
   }
   pBag->Release();
  }
  pM->Release();
 }
 return id;
}
HRESULT CCaptureVideo::Init(int iDeviceID, HWND hWnd)
{
 HRESULT hr;
 hr = InitCaptureGraphBuilder();
 if (FAILED(hr)){
  AfxMessageBox("Failed to get video interfaces!");
  return hr;
 }
 // Bind Device Filter. We know the device because the id was passed in
 if(!BindFilter(iDeviceID, &m_pBF))return S_FALSE;
 hr = m_pGB->AddFilter(m_pBF, L"Capture Filter");
 // hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, 
 // m_pBF, NULL, NULL);
 // create a sample grabber
 hr = m_pGrabber.CoCreateInstance( CLSID_SampleGrabber );
 if( !m_pGrabber ){
  AfxMessageBox("Fail to create SampleGrabber, maybe qedit.dll is not registered?");
  return hr;
 }
 CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );

 //设置视频格式
 AM_MEDIA_TYPE mt; 
 ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
 mt.majortype = MEDIATYPE_Video;
 mt.subtype = MEDIASUBTYPE_RGB24;
 hr = m_pGrabber->SetMediaType(&mt);

 if( FAILED( hr ) ){
  AfxMessageBox("Fail to set media type!");
  return hr;
 }
 hr = m_pGB->AddFilter( pGrabBase, L"Grabber" );
 if( FAILED( hr ) ){
  AfxMessageBox("Fail to put sample grabber in graph");
  return hr;
 }

 // try to render preview/capture pin
 hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,m_pBF,pGrabBase,NULL);
 if( FAILED( hr ) )
  hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,m_pBF,pGrabBase,NULL);

  if( FAILED( hr ) ){
   AfxMessageBox("Can’t build the graph");
   return hr;
  }
 
 hr = m_pGrabber->GetConnectedMediaType( &mt );
 if ( FAILED( hr) ){
  AfxMessageBox("Failt to read the connected media type");
  return hr;
 }

 VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat;
 mCB.lWidth = vih->bmiHeader.biWidth;
 mCB.lHeight = vih->bmiHeader.biHeight;
 FreeMediaType(mt);
 hr = m_pGrabber->SetBufferSamples( FALSE );
 hr = m_pGrabber->SetOneShot( FALSE );
 hr = m_pGrabber->SetCallback( &mCB, 1 );

 //设置视频捕捉窗口
 m_hWnd = hWnd ; 
 SetupVideoWindow();
 hr = m_pMC->Run();//开始视频捕捉
 if(FAILED(hr)){AfxMessageBox("Couldn’t run the graph!");return hr;}
 return S_OK;
}
bool CCaptureVideo::BindFilter(int deviceId, IBaseFilter **pFilter)
{
 if (deviceId < 0)
 return false;

 // enumerate all video capture devices
 CComPtr<ICreateDevEnum> pCreateDevEnum;
 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
 if (hr != NOERROR)
 {
  return false;
 }
 CComPtr<IEnumMoniker> pEm;
 hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
 if (hr != NOERROR) 
 {
  return false;
 }
 pEm->Reset();
 ULONG cFetched;
 IMoniker *pM;
 int index = 0;
 while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK, index <= deviceId)
 {
  IPropertyBag *pBag;
  hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
  if(SUCCEEDED(hr)) 
  {
   VARIANT var;
   var.vt = VT_BSTR;
   hr = pBag->Read(L"FriendlyName", &var, NULL);
   if (hr == NOERROR) 
   {
    if (index == deviceId)
    {
     pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
    }
    SysFreeString(var.bstrVal);
   }
   pBag->Release();
  }
  pM->Release();
  index++;
 }
 return true;
}

HRESULT CCaptureVideo::InitCaptureGraphBuilder()
{
 HRESULT hr;

 // 创建IGraphBuilder接口
 hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB);
 // 创建ICaptureGraphBuilder2接口
 hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **) &m_pCapture);
 if (FAILED(hr))return hr;
 m_pCapture->SetFiltergraph(m_pGB);
 hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
 if (FAILED(hr))return hr;
 hr = m_pGB->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW);
 if (FAILED(hr))return hr;
 return hr;
}
HRESULT CCaptureVideo::SetupVideoWindow()
{
 HRESULT hr;
 hr = m_pVW->put_Owner((OAHWND)m_hWnd);
 if (FAILED(hr))return hr;
 hr = m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);
 if (FAILED(hr))return hr;
 ResizeVideoWindow();
 hr = m_pVW->put_Visible(OATRUE);
 return hr;
}
void CCaptureVideo::ResizeVideoWindow()
{
 if (m_pVW){
  //让图像充满整个窗口
  CRect rc;
  ::GetClientRect(m_hWnd,&rc);
  m_pVW->SetWindowPosition(0, 0, rc.right, rc.bottom);
 } 
}
void CCaptureVideo::GrabOneFrame(BOOL bGrab)
{
 bOneShot = bGrab;
}
void CCaptureVideo::FreeMediaType(AM_MEDIA_TYPE& mt)
{
 if (mt.cbFormat != 0) {
  CoTaskMemFree((PVOID)mt.pbFormat);
  // Strictly unnecessary but tidier
  mt.cbFormat = 0;
  mt.pbFormat = NULL;
 }
 if (mt.pUnk != NULL) {
  mt.pUnk->Release();
  mt.pUnk = NULL;
 }
}

如何使用视频捕捉类CCaptureVideo

  构建CCaptureVideo类以后,使用就方便多了,我们在编程中只需要是要下面三个类成员函数就可以实现用摄像头进行视频捕捉:

  ①int EnumDevices(HWND hList); //hList是下拉列表框的句柄,本函数用于枚举当前系统安装的所有视频捕捉设备

  ②HRESULT Init(int iDeviceID,HWND hWnd);//iDeviceID是视频捕捉设备序号,hWnd是视频捕捉窗口的句柄

  ③void GrabOneFrame(BOOL bGrab);//调用GrabOneFrame(true)就可以捕获当前的静态图像并保存到硬盘上
  
  具体示例:用MFC AppWizard(exe)创建一个对话框应用程序,取名为ds,给对话框添加一个下拉列表框(IDC_COMBO1)、两个按钮(IDC_PHOTO、IDC_HAVEALOOK)和一个Picture控件(ID:IDC_STATIC_SCREEN,Type: Rectangle,Color:Gray)。

  1、使用向导添加成员变量

CStatic m_staticScreen; // IDC_STATIC_SCREEN
CComboBox m_ListCtrl; // IDC_COMBO1
CCaptureVideo m_cap;

  2、为BOOL CDsDlg::OnInitDialog()添加如下代码:

// TODO: Add extra initialization here
m_cap.EnumDevices (m_ListCtrl);
m_ListCtrl.SetCurSel (0);

  3、为确定按钮添加代码如下:

void CDsDlg::OnOK() 
{
 //只需要四行代码就可以进行视频捕捉了
 UpdateData();
 HWND hWnd = m_staticScreen.GetSafeHwnd() ; 
 HRESULT hr = m_cap.Init(m_ListCtrl.GetCurSel (),hWnd);
 GetDlgItem(IDOK)->EnableWindow(FALSE);
}

  4、如果希望捕捉静态图像,为照相按钮添加如下代码:

void CDsDlg::OnPhoto() 
{
 m_cap.GrabOneFrame(true);
}

  运行程序时,选定摄像头后只需要按确定就可以了,实际效果如下图所示:

 

  结束语

  本文提供的视频捕捉类CcaptureVideo和示例,在Win2K + DirectX9 SDK + VC6 环境下调试通过。注意:编译时需要Strmiids.lib Quartz.lib两个库文件(DirectX9 SDK自带)。

Visual C++编程实现摄像头视频捕捉的更多相关文章

  1. 摄像头视频捕捉(简单通用--通过IsampleGrabberCB实现)

    前言 DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布.DirectShow为多媒体流的捕捉和回放提供了强有力的支持.用Direct ...

  2. Delphi - 利用DLL编程控制摄像头实现拍照、录制视频

    Delphi利用avicap32.dll编程控制摄像头实现拍照.录制视频 项目需求:平板电脑(Windows系统)一维/二维码扫描功能: 需求分析: 需要扫描一维/二维码时,分两步实现. 第一步,av ...

  3. 视频处理控件TVideoGrabber视频捕捉设设备相关问题

    选择一个视频捕捉设备 首先设置 VideoSource = vs_VideoCaptureDevice来选择一个视频捕捉设备作为一个视频源. 通过指定VideoDevice属性来选择当前的视频捕捉设备 ...

  4. 视频捕捉全教程(vc+vfw)

    目 录 一. 视频捕获快速入门 二.基本的捕获设置 1.设置捕获速度: 2.设置终止捕获 3.捕获的时间限制 三.关于捕获窗口 1.创建一个AVICAP捕获窗口 2.将一个捕获窗口连接至捕获设备 3. ...

  5. VirtualDub - 开源视频捕捉及线性处理软件

    VirtualDub是一个开放源代码的视频捕捉及线性处理软件.它由Avery Lee编写,遵循GPL协议.VirtualDub可以通过摄像头捕捉视频. 官方网站http://virtualdub.or ...

  6. Direcshow中视频捕捉和参数设置报告

    Direcshow中视频捕捉和参数设置报告 1.      关于视频捕捉(About Video Capture in Dshow) 1视频捕捉Graph的构建 一个能够捕捉音频或者视频的graph图 ...

  7. Direcshow之视频捕捉<转>

    关于视频捕捉(About Video Capture in Dshow) 1. 视频捕捉Graph的构建 一个能够捕捉音频或者视频的graph图都称之为捕捉graph图.捕捉graph图比一般的文件回 ...

  8. Direcshow中视频捕捉和參数设置报告

    Direcshow中视频捕捉和參数设置报告 1.      关于视频捕捉(About Video Capture in Dshow) 1视频捕捉Graph的构建 一个能够捕捉音频或者视频的graph图 ...

  9. Linux v4l2编程(摄像头信息采集)

    基于Linux3.4.2,自己做一点儿视频信息采集及网络传输的小实验,边做边学,一些基础知识同步整理..... 1. 定义 V4L2(Video For Linux Two) 是内核提供给应用程序访问 ...

随机推荐

  1. linux系统管理(1)之 内核编译选项查看

    三个方法 proc文件系统 ubunut debain 红帽等 proc文件系统 /proc/config.gz This file shows you the compile-time config ...

  2. redis安全(加入密码)

    一.前言 在使用云服务器时,安装的redis3.0+版本都关闭了protected-mode,因而都遭遇了挖矿病毒的攻击,使得服务器99%的占用率!! 因此我们在使用redis时候,最好更改默认端口, ...

  3. spark第十篇:Spark与Kafka整合

    spark与kafka整合需要引入spark-streaming-kafka.jar,该jar根据kafka版本有2个分支,分别是spark-streaming-kafka-0-8和spark-str ...

  4. spark第九篇:Spark操作ES

    spark操作es需要elasticsearch-hadoop-xxx.jar,版本需同es版本.从5.0版本开始,支持spark2.0. 把elasticsearch-hadoop-xxx.jar放 ...

  5. 关于Sql注入的那些事

    登陆注册应该是每一个网站的必做的业务,但是在选择使用Django中的ORM还是说执行原生的Sql语句不同的人应该会有不同的建议,有经验的开发人员都喜欢原生的sql语句,因为相对于ORM来说,执行效率高 ...

  6. ViewData与ViewBag的区别

    本文导读:在asp.net mvc程序设计中,传递数据常常会用到viewdata.viewbag.ViewData是一个字典集合,通过key值读取对应的value:ViewBag是动态类型,作用和Vi ...

  7. ps中的常用功能与技巧

    1.如何将多个png图片合成一个? 首先,打开ps,新建一个透明色画布,然后再将两张图片拖入(注意:回车拖入),然后再选中这三个图层,右键选择合并图层,最后快速导出为png即可. 2.如何快速找到ps ...

  8. Python数据类型(字典)

    文章内容参考了教程:http://www.runoob.com/python/python-basic-syntax.html#commentform Python 字典(Dictionary) 字典 ...

  9. 没有循环的JavaScript

    有些文章中提到过,缩进(并不能特别准确的)说明了代码的复杂程度.我们想要的是简单的JavaScript.之所以层层缩进,是因为我们用抽象的方式解决问题.但要选用什么抽象方法呢?截止目前,我们没有在特定 ...

  10. 在W3C SCHOOL网站上发现一个关于Schema的错误

    原地址是http://www.w3school.com.cn/schema/schema_complex_empty.asp 下面这个例子是不正确的 xmlspy报错. 因为<xs:restri ...