您的位置:首页 > 其它

directshow抓取摄像头数据的封装类

2016-01-15 14:11 579 查看
摘自:http://blog.chinaunix.net/uid-8272118-id-2033249.html

网上流传很广的CCaptureVideo类有一些小问题,而且使用起来也不是特别方便。为了方便开发人员使用directshow的方便功能,我又重新封装了一下,代码也改了一些,修正了原有的bug,这个版本我相信是稍加修改就可以作为商业代码使用的,让它支持多次摄像头的打开,关闭,以及开始数据采集和关闭数据采集。本来可以把那些没必要的静态成员都放到类里面去的,但是考虑到大家对网上的那个CCaptureVideo类比较熟悉,不想破坏了大家的兴致,所以保留原有CCaptureVideo的全局变量。现将该类的代码贴出来,希望能够对想做摄像头应用软件开发的开发人员有用。
CCaptureVideo类的头文件:
#pragma once

#endif // _MSC_VER > 1000

#include <atlbase.h>

#include <windows.h>

#include <dshow.h>

#include <Qedit.h> // 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<ISampleGrabberCB*> ( 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<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::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<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::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给加上了。
如果只使用视频的话,大家用的时候也可以去掉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: