Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1514323
  • 博文数量: 114
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 1357
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-19 18:13
文章分类
文章存档

2010年(8)

2009年(9)

2008年(27)

2007年(62)

2006年(8)

我的朋友

分类: C/C++

2007-06-28 10:44:02

北京理工大学 20981 陈罡
网上流传很广的CCaptureVideo类有一些小问题,而且使用起来也不是特别方便。为了方便开发人员使用directshow的方便功能,我又重新封装了一下,代码也改了一些,修正了原有的bug,这个版本我相信是稍加修改就可以作为商业代码使用的,让它支持多次摄像头的打开,关闭,以及开始数据采集和关闭数据采集。本来可以把那些没必要的静态成员都放到类里面去的,但是考虑到大家对网上的那个CCaptureVideo类比较熟悉,不想破坏了大家的兴致,所以保留原有CCaptureVideo的全局变量。现将该类的代码贴出来,希望能够对想做摄像头应用软件开发的开发人员有用。
CCaptureVideo类的头文件:
#pragma once
#endif // _MSC_VER > 1000
#include
#include
#include
#include // ISampleGrabberCB
#ifndef SAFE_RELEASE
#define SAFE_RELEASE( x ) \
if ( NULL != x ) \
{ \
  x->Release( ); \
  x = NULL; \
}
#endif
class CVdoFrameHandler {
public:
 virtual void VdoFrameData(double dblSampleTime, BYTE * pBuffer, long lBufferSize) = 0 ;
};
class CSampleGrabberCB : public ISampleGrabberCB
{
public:
 long       lWidth ;
 long       lHeight ;
 CVdoFrameHandler *  frame_handler ;
 BOOL       bGrabVideo ;
public:
 CSampleGrabberCB(){
  lWidth = 0 ;
  lHeight = 0 ;
  bGrabVideo = FALSE ;
  frame_handler = NULL ;
 }
 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 ( this );
   return NOERROR;
  }
  return E_NOINTERFACE;
 }
 STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample )  {
  return 0;
 }
 STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize ){
  if (!pBuffer) return E_POINTER;
  if(bGrabVideo && frame_handler) frame_handler->VdoFrameData(dblSampleTime, pBuffer, lBufferSize) ;
  return 0;
 }
};
class CCaptureVideo : public CWnd
{
 friend class CSampleGrabberCB;
public:
 void GrabVideoFrames(BOOL bGrabVideoFrames, CVdoFrameHandler * frame_handler);
    HRESULT Open(int iDeviceID,HWND hWnd);
 HRESULT Close();
    int EnumDevices(HWND hList);
    CCaptureVideo();
    virtual ~CCaptureVideo();
protected:
    HWND     m_hWnd;
    IGraphBuilder *   m_pGB;
    ICaptureGraphBuilder2* m_pCapture;
    IBaseFilter*   m_pBF;
    IMediaControl*   m_pMC;
    IVideoWindow*   m_pVW;
    ISampleGrabber*   m_pGrabber;
protected:
    void FreeMediaType(AM_MEDIA_TYPE& mt);
    bool BindFilter(int deviceId, IBaseFilter **pFilter);
    void ResizeVideoWindow();
    HRESULT SetupVideoWindow();
    HRESULT InitCaptureGraphBuilder();
};
 
CCaptrureVideo类的源文件:
#include "stdafx.h"
#include "CaptureVideo.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CSampleGrabberCB mCB;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCaptureVideo::CCaptureVideo()
{
  //COM Library Intialization
  if(FAILED(CoInitialize(NULL))) /*, COINIT_APARTMENTTHREADED)))*/
  {
    AfxMessageBox("CCaptureVideo CoInitialize Failed!\r\n");
    return;
  }
  m_hWnd = NULL;
  m_pVW = NULL;
  m_pMC = NULL;
  m_pGB = NULL;
  m_pBF = NULL;
  m_pGrabber = NULL;
  m_pCapture = NULL;
}
CCaptureVideo::~CCaptureVideo()
{
  // Stop media playback
 // Stop media playback
 if(m_pMC)m_pMC->StopWhenReady();
 if(m_pVW){
  m_pVW->put_Visible(OAFALSE);
  m_pVW->put_Owner(NULL);
 }
 SAFE_RELEASE(m_pMC);
 SAFE_RELEASE(m_pVW);
 SAFE_RELEASE(m_pGB);
 SAFE_RELEASE(m_pBF);
 SAFE_RELEASE(m_pGrabber);
 SAFE_RELEASE(m_pCapture);
 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 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::Close()
{
 // Stop media playback
 if(m_pMC)m_pMC->StopWhenReady();
 if(m_pVW){
  m_pVW->put_Visible(OAFALSE);
  m_pVW->put_Owner(NULL);
 }
 SAFE_RELEASE(m_pMC);
 SAFE_RELEASE(m_pVW);
 SAFE_RELEASE(m_pGB);
 SAFE_RELEASE(m_pBF);
 SAFE_RELEASE(m_pGrabber);
 SAFE_RELEASE(m_pCapture);
 return S_OK ;
}
HRESULT CCaptureVideo::Open(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 = CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_ISampleGrabber, (void**)&m_pGrabber );
  if(FAILED(hr)){
    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; // 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;
  mCB.bGrabVideo = FALSE ;
  mCB.frame_handler = NULL ;
  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 pCreateDevEnum;
  HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
  if (hr != NOERROR)
  {
    return false;
  }
  CComPtr 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::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;
  }
}
void CCaptureVideo::GrabVideoFrames(BOOL bGrabVideoFrames, CVdoFrameHandler * frame_handler)
{
 mCB.frame_handler = frame_handler ;
 mCB.bGrabVideo = bGrabVideoFrames ;
}
在这里,声明了一个CVdoFrameHandler的纯虚基类,里面有一个纯虚函数,用户使用的时候,只要继承这个类,然后重载这个纯虚函数,就可以自动采集视频图像了。
首先:
class CMyClass : pulic CVdoFrameHandler {
...
public:
// 重载它,这里的pBuffer指针里面放的就是BGR24的的采集结果,lBufferSize就是pBuffer长度
 void VdoFrameData(double dblSampleTime, BYTE * pBuffer, long lBufferSize) ;
public:
CCaptureVideo m_cap_vdo; // 这个就是视频采集的类的对象了
};
 
采集的顺序:
HWND hWnd = m_cam_scr.GetSafeHwnd() ;
// 这个m_cam_scr是一个CStatic的对象,只要是个hWnd就行,
// directshow要在上面绘制出全屏的摄像头数据预览的
打开摄像头:
m_cap_vdo.Open(0, hWnd) ;
 
开始视频采集:
m_cap_vdo.GrabVideoFrames(TRUE, this) ;
此时就会周期性的自动的调用上面重载的VdoFrameData函数了,然后再编码发送,呵呵,就没问题了。
 
停止视频采集:
m_cap_vdo.GrabVideoFrames(FALSE, NULL) ;
 
关闭摄像头:
m_cap_vdo.Close() ;
 
最后,需要说明的是上述程序需要安装directx 9b的sdk,然后加入对应的lib:
strmiids.lib Quartz.lib winmm.lib dsound.lib dxguid.lib
我顺便也把directsound的lib给加上了。
如果只使用视频的话,大家用的时候也可以去掉。
阅读(13858) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~