2024年2月17日发(作者:望盈)
打开摄像头:
case IDM_OPENCAMERA:
if(capDriverConnect(hwndCap, 0)==TRUE)
{
capDriverGetCaps(hwndCap , &CapDriver ,sizeof (CAPDRIVERCAPS) );
if (erlay)
capOverlay(hwndCap ,TRUE);
else
{
capPreviewRate(hwndCap ,66) ;
capPreview(hwndCap, TRUE) ;
}
}
else {
MessageBox(hwndCap,"打开摄像头错误!n确定摄像头是否已连接","错 误",MB_OK);
}break;
//采集,捕捉摄像头单帧图像
case IDM_CAPTUREIMG:
capGrabFrame( hwndCap);
break;
//保存摄像头采集的图片
case IDM_CAPIMGSAVE:
MenuOpenDlg(hwnd,"视频采集保存对话框","save");
capFileSaveDIB(hwndCap,TempName);
break;
//
#include
#include
#include
#include
#include "VideoCapture.h"
#include "resource.h"
char TempName[256];
HWND hwndCap;
HWND hwndSetCapStutas;
CAPDRIVERCAPS CapDriver;
CAPSTATUS CapStat;
1 / 17
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
lpCmdLine,int nShowCmd)
{
if(DialogBox(hInstance,(LPCSTR)IDD_DIALOG1,NULL,DlgProc)==-1)
MessageBox(NULL,"对话框建立错误!","错 误",MB_OK);
return 0;
}
BOOL CALLBACK DlgProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM
lParam)
{
RECT rect;
switch(message)
{
case WM_INITDIALOG:
GetWindowRect(hdlg,&rect);
hwndCap =capCreateCaptureWindow("人脸录入" ,WS_CHILD|
WS_VISIBLE,30,20,320,240,hdlg ,0);
return FALSE;
case WM_COMMAND :
switch (LOWORD (wParam))
{
case IDOK :
if(capDriverConnect(hwndCap, 0)==TRUE)
{
capDriverGetCaps(hwndCap , &CapDriver ,sizeof (CAPDRIVERCAPS) );
if (erlay)
capOverlay(hwndCap ,TRUE);
else
{
capPreviewRate (hwndCap ,66) ;
capPreview(hwndCap, TRUE) ;
}
}
else
MessageBox(hdlg,"采集图像错误","提 示",MB_OK);
return 0;
break;
case IDC_BUTTON1:
2 / 17
if(gVideoSource)
capDlgVideoSource(hwndCap);
break;
case IDC_BUTTON2:
MessageBox(hdlg,"作者:Stewardn地址:云南大学信息学院","关于作者",MB_OK);
break;
case IDC_BUTTON3:
capGrabFrame( hwndCap);
break;
case IDC_BUTTON4:
MenuOpenDlg(hdlg,"视频采集保存对话框");
capFileSaveDIB(hwndCap,TempName);
break;
case IDCANCEL :
capCaptureStop(hwndCap);
capDriverDisconnect (hwndCap);
EndDialog (hdlg, 0) ;
return TRUE ;
}
break ;
}
return FALSE ;
}
void MenuOpenDlg(HWND hWnd,char *str1)
{
OPENFILENAME OpenTemp;
DWORD Errval,error;
char buf[5];
char Errstr[50]="GetOpenFileName returned Error #";
char szTemp[] = "BMP文件 (*.bmp;*.BMP)0*.bmp0所有文件 (*.*)0*.*0";
tSize = sizeof( OPENFILENAME );
ner = hWnd;
nce = 0;
ilter = (LPSTR)szTemp;
ustomFilter = NULL;
stFilter = 0;
rIndex = 1;
ile = (LPSTR)TempName;
le = sizeof( TempName );
ileTitle = NULL;
leTitle = 0;
3 / 17
nitialDir = NULL;
itle=str1;
= OFN_FILEMUSTEXIST |
OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
ffset = 0;
xtension = 0;
efExt = "*";
ata = 0L;
ok = NULL;
lateName = NULL;
error=0;
if (GetSaveFileName( &OpenTemp ) != TRUE)
error=1;
if (error==1) {
Errval=CommDlgExtendedError();
if (Errval!=0) {
sprintf(buf,"%ld",Errval);
strcat(Errstr,buf);
MessageBox(hWnd,Errstr,"WARNING",MB_OK|MB_ICONSTOP);
}
}
InvalidateRect( hWnd, NULL, TRUE );
}
//资源文件
// Dialog
//
IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 203, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "视频采集"
FONT 10, "System"
BEGIN
DEFPUSHBUTTON "打开",IDOK,31,143,39,14
PUSHBUTTON "退出",IDCANCEL,140,169,37,15
PUSHBUTTON "设置",IDC_BUTTON1,140,142,38,14
PUSHBUTTON "帮助",IDC_BUTTON2,84,170,38,14
PUSHBUTTON "捕捉图像",IDC_BUTTON3,85,142,38,15
PUSHBUTTON "保存图像",IDC_BUTTON4,32,170,37,14
END
// DESIGNINFO
4 / 17
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_DIALOG1, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 196
TOPMARGIN, 7
BOTTOMMARGIN, 194
END
END
实现原理
用实现图像采集,首先要用函数capCreateCaptureWindowA创建一个视频采集窗口,然后向视频采集窗口发送相应的消息,实现视频设备的连接、回调函数设置、预览比例和速率设置、预览和叠加模式的设置、图像文件设置等操作。如果以上操作成功,就可以通过视频设备采集图像了。回调函数由程序员编写,用于特殊的视频采集中,例如,在视频会议中用回调函数将采集的视频和音频实时地传递到远程计算机中。
在VC++中,头文件VFW.H中不仅包含了中函数的原型定义,还定义了与视频采集有关的数据结构、消息和发送消息的宏。利用这些函数、数据结构、消息和发送消息的宏可以方便地编写图像采集程序,如果要对采集的图像实时处理,可以通过编写回调函数实现。
实例分析
为了说明实现的方法和技巧,采用USB接口的摄像头作为视频采集设备,在VC++6.0中以实现采集一副图像为例说明。
在VC++6.0中创建一个基于MFC的单文档应用程序,工程名为“Video”,按照以下步骤完成:
(1)在VideoView.h的首部加入语句:
#include "Vfw.h"
(2)打开“Project Settings”对话框,选择“Link”选项卡,在“Object/library modules”中输入“”。
(3)在VideoView.h中对类CVideoView加入私有成员变量Hwndcap:
private:
//视频采集窗口的句柄
HWND Hwndcap;
(4)在Resources面板中单击“Menu”,双击“IDR_MAINFRAME”,添加菜单项“采集”。在“采集”下加入子菜单项“采集图像”和“保存BMP”,它们的ID分别设为“ID_CAP_BMP”和“ID_SAVE_BMP”。
(5)在MFC ClassWizard中,选择“Message Map”选项卡,在下拉列表框“Class name”中选择 5 / 17
“CVideoView”,在列表框“Object IDs”中选择“ID_CAP_BMP”,在列表框“Messages”中选择“Command”,单击“Add Function”按钮添加成员函数OnCapBmp(),按照同样方法为“ID_SAVE_BMP”响应消息“Command”添加成员函数OnSaveBmp()。再按照同样方法为“CVideoView”响应消息“WM_SIZE” 添加成员函数OnSize()。
(6)在类“CVideoView”的构造函数中加入:
Hwndcap=NULL;
(7)在类“CVideoView”的成员函数OnCapBmp()中加入以下代码:
CRect WRect;
//取得主窗口客户区域的位置和大小
GetClientRect(&WRect);
//创建视频采集窗口
Hwndcap=capCreateCaptureWindow(
(LPSTR) "采集图像",
WS_CHILD|WS_VISIBLE,
0,25,
(),(),
AfxGetMainWnd()->m_hWnd,
(int)0);
if(Hwndcap)
{
//连接视频采集设备
if(capDriverConnect(Hwndcap,0))
{
//设置视频预览图像的大小
capPreviewScale(Hwndcap,1);
//设置视频预览的速率
capPreviewRate(Hwndcap,66);
//设置视频以预览模式显示
capPreview(Hwndcap,1);
}
else
{//显示错误信息
MessageBox("摄像头未准备好!","提示信息",MB_OK|MB_ICONERROR);
//撤销视频采集窗口
::DestroyWindow(Hwndcap);
Hwndcap=NULL;
}}
(8)在类“CVideoView”的成员函数OnSaveBmp ()中加入以下代码:
if(Hwndcap)
//将当前帧保存为位图文件
capFileSaveDIB(Hwndcap,"");
else
MessageBox("请先采集图像!",
"提示信息",
6 / 17
MB_OK|MB_ICONERROR);
(9)在类“CVideoView”的成员函数OnSize ()中加入以下代码:
CRect WRect;
//取得主窗口客户区域的位置和大小
GetClientRect(&WRect);
//设置视频采集窗口的位置和大小
::SetWindowPos(Hwndcap,
NULL,
0,25,
(),
(),
SWP_NOZORDER);
以上各步完成后,经过编译和连接生成可执行文件“”。运行“”,单击菜单项“采集/采集图像”,结果如图1所示。
程序中的capDriverConnect等是头文件“Vfw.h”中定义的宏,这些宏实质是向视频采集窗口发送相应的消息,由于篇幅所限,这里对这些宏和消息的定义不作介绍,读者可参阅VC++中的头文件“Vfw.h”。
5 结束语
在VC++中利用实现图像采集简单,编程代码量少,程序员编写回调函数可以实现特殊需求的图像采集,不过,对于一般要求的图像采集不需要编写回调函数。
图1 程序运行结果
参考文献:
[1]赵军,平西建,肖永隆.视频信号的捕获采集编程[J].信息工程大学学报,2001,2(4):46-49.
[2]张弋,何晓,李艳军.多媒体信息捕获在Win32编程中的实现[J].计算机工程,2002,28(2):
205-207.
[3]张元.基于AVICap窗口类的视频监控及捕捉的实现[J].电脑知识与技术,2004,(14):7-9.
[4]阮林波,曹锦云. 基于USB摄像头的自动监控系统[J].计算机工程与设计,2004,25(6):988-989.
7 / 17
前言
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
/////////////////////////////////////////////////////////////////////
8 / 17
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include
#include
#include
#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
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_)
//-------------------------------------------------------------------
9 / 17
// CCaptureVideo视频捕捉类实现文件
//-------------------------------------------------------------------
// : 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:");
}
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( !bOneShot )return 0;
if (!pBuffer)return E_POINTER;
SaveBitmap(pBuffer, lBufferSize);
bOneShot = FALSE;
return 0;
}
//创建位图文件
BOOL SaveBitmap(BYTE * pBuffer, long lBufferSize )
10 / 17
{
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 ) );
= ’MB’;
= sizeof( bfh ) + lBufferSize + sizeof( BITMAPINFOHEADER );
its = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
DWORD dwWritten = 0;
WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
// 写位图格式
BITMAPINFOHEADER bih;
memset( &bih, 0, sizeof( bih ) );
= sizeof( bih );
h = lWidth;
ht = lHeight;
es = 1;
ount = 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!rn");
return;
}
m_hWnd = NULL;
m_pVW = NULL;
m_pMC = NULL;
m_pGB = NULL;
11 / 17
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
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;
= VT_BSTR;
hr = pBag-> Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
12 / 17
{
TCHAR str[2048];
id++;
WideCharToMultiByte(CP_ACP,0,l, -1, str, 2048, NULL, NULL);
::SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)str);
SysFreeString(l);
}
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_teInstance( CLSID_SampleGrabber );
if( !m_pGrabber ){
AfxMessageBox("Fail to create SampleGrabber, maybe is not registered?");
return hr;
}
CComQIPtr < IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );
//设置视频格式
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
ype = MEDIATYPE_Video;
e = 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" );
13 / 17
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*) at;
= vih-> h;
t = vih-> ht;
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,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
NULL,
14 / 17
{
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;
= VT_BSTR;
hr = pBag-> Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
if (index == deviceId)
{
pM-> BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
}
SysFreeString(l);
}
pBag-> Release();
}
pM-> Release();
index++;
}
return true;
}
HRESULT CCaptureVideo::InitCaptureGraphBuilder()
{
HRESULT hr;
// 创建IGraphBuilder接口
hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
15 / 17
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, , );
}
}
void CCaptureVideo::GrabOneFrame(BOOL bGrab)
{
bOneShot = bGrab;
}
void CCaptureVideo::FreeMediaType(AM_MEDIA_TYPE& mt)
{
if (at != 0) {
CoTaskMemFree((PVOID)at);
// Strictly unnecessary but tidier
at = 0;
at = NULL;
}
16 / 17
if ( != NULL) {
-> Release();
= 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_vices (m_ListCtrl);
m_Sel (0);
3、为确定按钮添加代码如下:
void CDsDlg::OnOK()
{
//只需要四行代码就可以进行视频捕捉了
UpdateData();
HWND hWnd = m_eHwnd() ;
HRESULT hr = m_(m_Sel (),hWnd);
GetDlgItem(IDOK)-> EnableWindow(FALSE);
}
4、如果希望捕捉静态图像,为照相按钮添加如下代码:
void CDsDlg::OnPhoto()
{
m_eFrame(true);
}
运行程序时,选定摄像头后只需要按确定就可以了,实际效果如下图所示:
结束语
本文提供的视频捕捉类CcaptureVideo和示例,在Win2K + DirectX9 SDK + VC6 环境下调试通过。注意:编译时需要 两个库文件(DirectX9 SDK自带)。
17 / 17
2024年2月17日发(作者:望盈)
打开摄像头:
case IDM_OPENCAMERA:
if(capDriverConnect(hwndCap, 0)==TRUE)
{
capDriverGetCaps(hwndCap , &CapDriver ,sizeof (CAPDRIVERCAPS) );
if (erlay)
capOverlay(hwndCap ,TRUE);
else
{
capPreviewRate(hwndCap ,66) ;
capPreview(hwndCap, TRUE) ;
}
}
else {
MessageBox(hwndCap,"打开摄像头错误!n确定摄像头是否已连接","错 误",MB_OK);
}break;
//采集,捕捉摄像头单帧图像
case IDM_CAPTUREIMG:
capGrabFrame( hwndCap);
break;
//保存摄像头采集的图片
case IDM_CAPIMGSAVE:
MenuOpenDlg(hwnd,"视频采集保存对话框","save");
capFileSaveDIB(hwndCap,TempName);
break;
//
#include
#include
#include
#include
#include "VideoCapture.h"
#include "resource.h"
char TempName[256];
HWND hwndCap;
HWND hwndSetCapStutas;
CAPDRIVERCAPS CapDriver;
CAPSTATUS CapStat;
1 / 17
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
lpCmdLine,int nShowCmd)
{
if(DialogBox(hInstance,(LPCSTR)IDD_DIALOG1,NULL,DlgProc)==-1)
MessageBox(NULL,"对话框建立错误!","错 误",MB_OK);
return 0;
}
BOOL CALLBACK DlgProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM
lParam)
{
RECT rect;
switch(message)
{
case WM_INITDIALOG:
GetWindowRect(hdlg,&rect);
hwndCap =capCreateCaptureWindow("人脸录入" ,WS_CHILD|
WS_VISIBLE,30,20,320,240,hdlg ,0);
return FALSE;
case WM_COMMAND :
switch (LOWORD (wParam))
{
case IDOK :
if(capDriverConnect(hwndCap, 0)==TRUE)
{
capDriverGetCaps(hwndCap , &CapDriver ,sizeof (CAPDRIVERCAPS) );
if (erlay)
capOverlay(hwndCap ,TRUE);
else
{
capPreviewRate (hwndCap ,66) ;
capPreview(hwndCap, TRUE) ;
}
}
else
MessageBox(hdlg,"采集图像错误","提 示",MB_OK);
return 0;
break;
case IDC_BUTTON1:
2 / 17
if(gVideoSource)
capDlgVideoSource(hwndCap);
break;
case IDC_BUTTON2:
MessageBox(hdlg,"作者:Stewardn地址:云南大学信息学院","关于作者",MB_OK);
break;
case IDC_BUTTON3:
capGrabFrame( hwndCap);
break;
case IDC_BUTTON4:
MenuOpenDlg(hdlg,"视频采集保存对话框");
capFileSaveDIB(hwndCap,TempName);
break;
case IDCANCEL :
capCaptureStop(hwndCap);
capDriverDisconnect (hwndCap);
EndDialog (hdlg, 0) ;
return TRUE ;
}
break ;
}
return FALSE ;
}
void MenuOpenDlg(HWND hWnd,char *str1)
{
OPENFILENAME OpenTemp;
DWORD Errval,error;
char buf[5];
char Errstr[50]="GetOpenFileName returned Error #";
char szTemp[] = "BMP文件 (*.bmp;*.BMP)0*.bmp0所有文件 (*.*)0*.*0";
tSize = sizeof( OPENFILENAME );
ner = hWnd;
nce = 0;
ilter = (LPSTR)szTemp;
ustomFilter = NULL;
stFilter = 0;
rIndex = 1;
ile = (LPSTR)TempName;
le = sizeof( TempName );
ileTitle = NULL;
leTitle = 0;
3 / 17
nitialDir = NULL;
itle=str1;
= OFN_FILEMUSTEXIST |
OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
ffset = 0;
xtension = 0;
efExt = "*";
ata = 0L;
ok = NULL;
lateName = NULL;
error=0;
if (GetSaveFileName( &OpenTemp ) != TRUE)
error=1;
if (error==1) {
Errval=CommDlgExtendedError();
if (Errval!=0) {
sprintf(buf,"%ld",Errval);
strcat(Errstr,buf);
MessageBox(hWnd,Errstr,"WARNING",MB_OK|MB_ICONSTOP);
}
}
InvalidateRect( hWnd, NULL, TRUE );
}
//资源文件
// Dialog
//
IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 203, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "视频采集"
FONT 10, "System"
BEGIN
DEFPUSHBUTTON "打开",IDOK,31,143,39,14
PUSHBUTTON "退出",IDCANCEL,140,169,37,15
PUSHBUTTON "设置",IDC_BUTTON1,140,142,38,14
PUSHBUTTON "帮助",IDC_BUTTON2,84,170,38,14
PUSHBUTTON "捕捉图像",IDC_BUTTON3,85,142,38,15
PUSHBUTTON "保存图像",IDC_BUTTON4,32,170,37,14
END
// DESIGNINFO
4 / 17
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_DIALOG1, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 196
TOPMARGIN, 7
BOTTOMMARGIN, 194
END
END
实现原理
用实现图像采集,首先要用函数capCreateCaptureWindowA创建一个视频采集窗口,然后向视频采集窗口发送相应的消息,实现视频设备的连接、回调函数设置、预览比例和速率设置、预览和叠加模式的设置、图像文件设置等操作。如果以上操作成功,就可以通过视频设备采集图像了。回调函数由程序员编写,用于特殊的视频采集中,例如,在视频会议中用回调函数将采集的视频和音频实时地传递到远程计算机中。
在VC++中,头文件VFW.H中不仅包含了中函数的原型定义,还定义了与视频采集有关的数据结构、消息和发送消息的宏。利用这些函数、数据结构、消息和发送消息的宏可以方便地编写图像采集程序,如果要对采集的图像实时处理,可以通过编写回调函数实现。
实例分析
为了说明实现的方法和技巧,采用USB接口的摄像头作为视频采集设备,在VC++6.0中以实现采集一副图像为例说明。
在VC++6.0中创建一个基于MFC的单文档应用程序,工程名为“Video”,按照以下步骤完成:
(1)在VideoView.h的首部加入语句:
#include "Vfw.h"
(2)打开“Project Settings”对话框,选择“Link”选项卡,在“Object/library modules”中输入“”。
(3)在VideoView.h中对类CVideoView加入私有成员变量Hwndcap:
private:
//视频采集窗口的句柄
HWND Hwndcap;
(4)在Resources面板中单击“Menu”,双击“IDR_MAINFRAME”,添加菜单项“采集”。在“采集”下加入子菜单项“采集图像”和“保存BMP”,它们的ID分别设为“ID_CAP_BMP”和“ID_SAVE_BMP”。
(5)在MFC ClassWizard中,选择“Message Map”选项卡,在下拉列表框“Class name”中选择 5 / 17
“CVideoView”,在列表框“Object IDs”中选择“ID_CAP_BMP”,在列表框“Messages”中选择“Command”,单击“Add Function”按钮添加成员函数OnCapBmp(),按照同样方法为“ID_SAVE_BMP”响应消息“Command”添加成员函数OnSaveBmp()。再按照同样方法为“CVideoView”响应消息“WM_SIZE” 添加成员函数OnSize()。
(6)在类“CVideoView”的构造函数中加入:
Hwndcap=NULL;
(7)在类“CVideoView”的成员函数OnCapBmp()中加入以下代码:
CRect WRect;
//取得主窗口客户区域的位置和大小
GetClientRect(&WRect);
//创建视频采集窗口
Hwndcap=capCreateCaptureWindow(
(LPSTR) "采集图像",
WS_CHILD|WS_VISIBLE,
0,25,
(),(),
AfxGetMainWnd()->m_hWnd,
(int)0);
if(Hwndcap)
{
//连接视频采集设备
if(capDriverConnect(Hwndcap,0))
{
//设置视频预览图像的大小
capPreviewScale(Hwndcap,1);
//设置视频预览的速率
capPreviewRate(Hwndcap,66);
//设置视频以预览模式显示
capPreview(Hwndcap,1);
}
else
{//显示错误信息
MessageBox("摄像头未准备好!","提示信息",MB_OK|MB_ICONERROR);
//撤销视频采集窗口
::DestroyWindow(Hwndcap);
Hwndcap=NULL;
}}
(8)在类“CVideoView”的成员函数OnSaveBmp ()中加入以下代码:
if(Hwndcap)
//将当前帧保存为位图文件
capFileSaveDIB(Hwndcap,"");
else
MessageBox("请先采集图像!",
"提示信息",
6 / 17
MB_OK|MB_ICONERROR);
(9)在类“CVideoView”的成员函数OnSize ()中加入以下代码:
CRect WRect;
//取得主窗口客户区域的位置和大小
GetClientRect(&WRect);
//设置视频采集窗口的位置和大小
::SetWindowPos(Hwndcap,
NULL,
0,25,
(),
(),
SWP_NOZORDER);
以上各步完成后,经过编译和连接生成可执行文件“”。运行“”,单击菜单项“采集/采集图像”,结果如图1所示。
程序中的capDriverConnect等是头文件“Vfw.h”中定义的宏,这些宏实质是向视频采集窗口发送相应的消息,由于篇幅所限,这里对这些宏和消息的定义不作介绍,读者可参阅VC++中的头文件“Vfw.h”。
5 结束语
在VC++中利用实现图像采集简单,编程代码量少,程序员编写回调函数可以实现特殊需求的图像采集,不过,对于一般要求的图像采集不需要编写回调函数。
图1 程序运行结果
参考文献:
[1]赵军,平西建,肖永隆.视频信号的捕获采集编程[J].信息工程大学学报,2001,2(4):46-49.
[2]张弋,何晓,李艳军.多媒体信息捕获在Win32编程中的实现[J].计算机工程,2002,28(2):
205-207.
[3]张元.基于AVICap窗口类的视频监控及捕捉的实现[J].电脑知识与技术,2004,(14):7-9.
[4]阮林波,曹锦云. 基于USB摄像头的自动监控系统[J].计算机工程与设计,2004,25(6):988-989.
7 / 17
前言
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
/////////////////////////////////////////////////////////////////////
8 / 17
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include
#include
#include
#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
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_)
//-------------------------------------------------------------------
9 / 17
// CCaptureVideo视频捕捉类实现文件
//-------------------------------------------------------------------
// : 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:");
}
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( !bOneShot )return 0;
if (!pBuffer)return E_POINTER;
SaveBitmap(pBuffer, lBufferSize);
bOneShot = FALSE;
return 0;
}
//创建位图文件
BOOL SaveBitmap(BYTE * pBuffer, long lBufferSize )
10 / 17
{
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 ) );
= ’MB’;
= sizeof( bfh ) + lBufferSize + sizeof( BITMAPINFOHEADER );
its = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
DWORD dwWritten = 0;
WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
// 写位图格式
BITMAPINFOHEADER bih;
memset( &bih, 0, sizeof( bih ) );
= sizeof( bih );
h = lWidth;
ht = lHeight;
es = 1;
ount = 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!rn");
return;
}
m_hWnd = NULL;
m_pVW = NULL;
m_pMC = NULL;
m_pGB = NULL;
11 / 17
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
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;
= VT_BSTR;
hr = pBag-> Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
12 / 17
{
TCHAR str[2048];
id++;
WideCharToMultiByte(CP_ACP,0,l, -1, str, 2048, NULL, NULL);
::SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)str);
SysFreeString(l);
}
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_teInstance( CLSID_SampleGrabber );
if( !m_pGrabber ){
AfxMessageBox("Fail to create SampleGrabber, maybe is not registered?");
return hr;
}
CComQIPtr < IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );
//设置视频格式
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
ype = MEDIATYPE_Video;
e = 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" );
13 / 17
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*) at;
= vih-> h;
t = vih-> ht;
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,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
NULL,
14 / 17
{
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;
= VT_BSTR;
hr = pBag-> Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
if (index == deviceId)
{
pM-> BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
}
SysFreeString(l);
}
pBag-> Release();
}
pM-> Release();
index++;
}
return true;
}
HRESULT CCaptureVideo::InitCaptureGraphBuilder()
{
HRESULT hr;
// 创建IGraphBuilder接口
hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
15 / 17
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, , );
}
}
void CCaptureVideo::GrabOneFrame(BOOL bGrab)
{
bOneShot = bGrab;
}
void CCaptureVideo::FreeMediaType(AM_MEDIA_TYPE& mt)
{
if (at != 0) {
CoTaskMemFree((PVOID)at);
// Strictly unnecessary but tidier
at = 0;
at = NULL;
}
16 / 17
if ( != NULL) {
-> Release();
= 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_vices (m_ListCtrl);
m_Sel (0);
3、为确定按钮添加代码如下:
void CDsDlg::OnOK()
{
//只需要四行代码就可以进行视频捕捉了
UpdateData();
HWND hWnd = m_eHwnd() ;
HRESULT hr = m_(m_Sel (),hWnd);
GetDlgItem(IDOK)-> EnableWindow(FALSE);
}
4、如果希望捕捉静态图像,为照相按钮添加如下代码:
void CDsDlg::OnPhoto()
{
m_eFrame(true);
}
运行程序时,选定摄像头后只需要按确定就可以了,实际效果如下图所示:
结束语
本文提供的视频捕捉类CcaptureVideo和示例,在Win2K + DirectX9 SDK + VC6 环境下调试通过。注意:编译时需要 两个库文件(DirectX9 SDK自带)。
17 / 17