趁热打铁之---VS2010基于VFW的视频控件编写,实现预览、拍照功能

原文地址:https://blog.csdn.net/wu_tongyu/article/details/39641085

一、VFW简介

VFW(Video for Windows)是微软提供的数字视频开发包。

VFW有许多函数,详细可参考http://blog.csdn.net/laolei1986/article/details/5733051

下面介绍几个用到的函数:

1、创建视频捕捉窗口

HWND VFWAPI capCreateCaptureWindow(

LPCSTR lpszWindowName, //视频捕捉窗口的名称

DWORD dwStyle, //视频捕捉窗口风格,比如WS_CHILD和 WS_VISIBLE

int x,//(x,y)表示视频捕捉窗口的左上角坐标

int y, 

int nWidth, //窗口宽度

int nHeight, //窗口高度

HWND hWnd, //父窗口句柄

int nID//窗口标志

);

2、视频驱动程序与预览窗口连接函数

BOOL VFWAPI capDriverConnect(

HWND hWnd, //capCreateWindow函数生成的窗口句柄,即捕捉窗口句柄

int driverId //驱动程序编号,默认为0。可以在System.ini文件中查找

);

3、设置预览帧频率

BOOL VFWAPI  capPreviewRate(

HWND hWnd, //预览窗口句柄

in frame//帧频率

);

4、预览函数

BOOL VFWAPI  capPreview(

HWNDhWnd, //预览窗口句柄

BOOLflag //FALSE定帧,即预览停止,TRUE则开始预览

);

5、截获当前图片

BOOL VFWAPI capGrabFrameNoStop(

HWND hwnd//预览窗口句柄

);

//复制到剪切板

BOOL VFWAPI capEditCopy(

HWND hwnd //预览窗口句柄

);

二、编码实现

1、新建工程,切换到资源视图,添加一个dialog用于显示视频预览,属性Border设置为None,Control设置为True,在对话框上添加一个Picture控件用于显示预览。如下图:

2、为该对话框添加CVedioDlg类,(Video拼错了,囧,忽略之)

在该类中引入VFW库

要访问对话框资源,所以直接在对话框类中实现预览功能,在有Control类来调用其方法。VedioDlg.h文件如下:

#pragma once
 
#include <vfw.h> //视频处理类库
#pragma comment(lib,"vfw32.lib")
// CVedioDlg 对话框
 
class CVedioDlg : public CDialogEx
{
	DECLARE_DYNAMIC(CVedioDlg)
 
public:
	CVedioDlg(CWnd* pParent = NULL);   // 标准构造函数
	virtual ~CVedioDlg();
 
public:
	void OpenCamera();//打开摄像头
	void StartCapture();//开始捕捉
	void TakePicture();//拍照
	void StopCamera();//停止捕捉
	void CloseCamera();//关闭摄像头
public:
	friend UINT vproc(LPVOID pParam);//暂时没用到
	friend LRESULT CALLBACK backproc(HWND hwnd,LPVIDEOHDR lpVHDR);<span style="font-family: Arial, Helvetica, sans-serif;">//暂时没用到</span>
 
	HWND videohwnd;
	CStatic CAPVIDEO;
	int wIndex;
	CEvent e;
	BOOL m_connect;//设备连接标志
	BOOL m_open;//设备打开标志
 
// 对话框数据
	enum { IDD = IDD_PICTURE };
 
protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
 
	DECLARE_MESSAGE_MAP()
};

VedioDlg.cpp实现,其实可以将capCreateCaptureWindow()放到StartCapture中实现,将OpenCamera和StartCapture函数合并

// VedioDlg.cpp : 实现文件
//
 
#include "stdafx.h"
#include "VedioCapture.h"
#include "VedioDlg.h"
#include "afxdialogex.h"
 
 
// CVedioDlg 对话框
 
IMPLEMENT_DYNAMIC(CVedioDlg, CDialogEx)
 
CVedioDlg::CVedioDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CVedioDlg::IDD, pParent)
{
	wIndex = 0;
	m_connect = FALSE;
	m_open = FALSE;
}
 
 
CVedioDlg::~CVedioDlg()
{
}
 
void CVedioDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX,IDC_VIDEO,CAPVIDEO);
}
 
 
BEGIN_MESSAGE_MAP(CVedioDlg, CDialogEx)
END_MESSAGE_MAP()
 
 
// CVedioDlg 消息处理程序
void CVedioDlg::OpenCamera()
{
//	tran = NULL;
/*	e.ResetEvent();*/
	videohwnd = capCreateCaptureWindow(NULL,WS_POPUP,0,0,10,10,0,0);
//  	AfxBeginThread(vproc,(void*)this);//开始线程
//  	::WaitForSingleObject(e,INFINITE);
	if (videohwnd == 0)
	{
		m_open = FALSE;
		AfxMessageBox(_T("创建AVI窗口失败"));
		return;
	}
	m_open = TRUE;//打开标志
}
 
void CVedioDlg::StartCapture()
{
	char szDeviceName[80];
	char szDeviceVersion[80];
 
	CRect rc;
	CAPVIDEO.GetWindowRect(&rc);
	ScreenToClient(rc);
	CAPDRIVERCAPS capd;
	CAPSTATUS caps;
	if (m_connect == TRUE)//处于连接状态
	{
		//capPreviewRate(videohwnd,60);
		capPreview(videohwnd,TRUE);
		return;
	}
// 	if(!capGetDriverDescription(wIndex,(LPWSTR)szDeviceName,sizeof szDeviceName
// 		,(LPWSTR)szDeviceVersion,sizeof szDeviceVersion))
// 	{
// 		AfxMessageBox(_T("无可用视频设备!"));
// 		return ;
// 	}
	if (capDriverConnect(videohwnd,wIndex) == TRUE)
	{
		m_connect == TRUE;
		capDriverGetCaps(videohwnd,sizeof(CAPDRIVERCAPS),&capd);
		capGetStatus(videohwnd,&caps,sizeof CAPSTATUS);
		//capCaptureSequence(videohwnd); //开始捕捉视频,默认保存为CAPTURE.avi
		::SetParent(videohwnd,*this);
		::SetWindowLong(videohwnd,GWL_STYLE,WS_CHILD);
		::SetWindowPos(videohwnd,NULL,rc.left,rc.top,rc.Width(),rc.Height(),SWP_NOZORDER);
		::ShowWindow(videohwnd,SW_SHOW);
		capPreviewRate(videohwnd,60);
		capPreview(videohwnd,TRUE);
	}
}
 
void CVedioDlg::StopCamera()
{
	//capCaptureStop(videohwnd);//停止捕捉
	capPreview(videohwnd,FALSE);//定帧
	//Sleep(5000);
	//capPreview(videohwnd,TRUE);
	//capDriverDisconnect(videohwnd);//断开连接
}
 
void CVedioDlg::CloseCamera()
{
	if(m_open == TRUE)
	{
		capDriverDisconnect(videohwnd);//断开连接
		::CloseWindow(videohwnd);//最小化窗口
		::DestroyWindow(videohwnd);//销毁窗口
		m_open = FALSE;
		m_connect = FALSE;
	}
	//this->OnCancel();//退出对话框
}
 
void CVedioDlg::TakePicture()
{
	CAPTUREPARMS params;
	capCaptureGetSetup(videohwnd,¶ms,sizeof params);
	capPreview(videohwnd,FALSE);//定帧
	capGrabFrameNoStop(videohwnd);//截获当前图像
	capEditCopy(videohwnd);//将图像拷贝到剪切板
}
 
static UINT vproc(LPVOID pParam)
{
	CVedioDlg *p = (CVedioDlg*)pParam;
	HWND hwnd = capCreateCaptureWindow(NULL,WS_POPUP,0,0,10,10,0,0);
	if (hwnd)
	{
		p->videohwnd = hwnd;
		p->e.SetEvent();
	}
	MSG msg;
	while (GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
 
LRESULT CALLBACK backproc(HWND hwnd,LPVIDEOHDR lpVHDR)
{
	return 0;
}

3、然后就是在CVedioCaptureCtrl类中添加方法,调用VedioDlg类的方法,添加的方法如下:


实现方法:

头文件中定义CVedioDlg m_VedioDlg;

// CVedioCaptureCtrl 消息处理程序
 
 
int CVedioCaptureCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (COleControl::OnCreate(lpCreateStruct) == -1)
		return -1;
 
	// TODO:  在此添加您专用的创建代码
	m_VedioDlg.Create(IDD_PICTURE,this);//创建对话框
	m_VedioDlg.OpenCamera();
	return 0;
}
 
 
void CVedioCaptureCtrl::OnSize(UINT nType, int cx, int cy)
{
	COleControl::OnSize(nType, cx, cy);
 
	// TODO: 在此处添加消息处理程序代码
	RECT ActiveXRect;
	GetClientRect(&ActiveXRect);
	m_VedioDlg.MoveWindow(&ActiveXRect);
}
 
//开始生成预览
void CVedioCaptureCtrl::StartPreview(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
	// TODO: 在此添加调度处理程序代码
	int count = 2;//尝试连接次数
	if (m_VedioDlg.m_open == FALSE)
	{
		m_VedioDlg.OpenCamera();//先打开设备
	}
	while(count-- > 0)
	{
		m_VedioDlg.StartCapture();
	}
}
 
//停止预览
void CVedioCaptureCtrl::StopPreview(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
	// TODO: 在此添加调度处理程序代码
	m_VedioDlg.StopCamera();
}
 
//关闭预览
void CVedioCaptureCtrl::CancelPreview(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
	// TODO: 在此添加调度处理程序代码
	//m_VedioDlg.StopCamera();
	m_VedioDlg.CloseCamera();
}
 
//拍照
void CVedioCaptureCtrl::TakePicture(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
	// TODO: 在此添加调度处理程序代码
	m_VedioDlg.TakePicture();
}
 
 
void CVedioCaptureCtrl::OpenPreview(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
	// TODO: 在此添加调度处理程序代码
	if(!m_VedioDlg.m_open)
		m_VedioDlg.OpenCamera();
}

注意:其中在void CVedioCaptureCtrl::StartPreview(void)函数中尝试连接2次的原因是,开始启动控件时,会弹出选择视频源的窗口,然后就连接失败,这个问题一直没得到解决。项目中解决方案是不用VFW来实现视频预览,而是使用DirectX来实现,具体怎么实现,以后再研究一下补充上。。

4、测试

html代码:这html代码一看就不专业,毕竟不是研究这个的,囧

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<title> New Document </title>
		<meta name="Generator" content="EditPlus">
		<meta name="Author" content="">
		<meta name="Keywords" content="">
		<meta name="Description" content="">
	</head>
	<!--调用代码
	-----------------------------
	JS处理代码如下-->
	<script type="text/javascript">
	function JoinGame()
	{
		//alert("已执行");
		var tgame=document.getElementById("ClockOCX");
		tgame.Hello();
	}
	function StartPreview()
	{
		var video=document.getElementById("VedioCapture");
		video.StartPreview();
	}
	function StopPreview()
	{
		var video=document.getElementById("VedioCapture");
		video.StopPreview();
	}
	function CancelPreview()
	{
		var video=document.getElementById("VedioCapture");
		video.CancelPreview();
	}
	function Takepicture()
	{
		var video=document.getElementById("VedioCapture");
		video.Takepicture();
	}
	</script>
	 <body>
		<div id="container">
			<!-- 加载时钟控件-->
			<div id="clock">
				<div id="tip">
					<p>时钟显示:</p>
				</div>
				<OBJECT  ID="ClockOCX"  CLASSID="clsid:33E08641-67AD-48DA-9B03-EE694A20B51F"></OBJECT> 	
				<div id="clockctrl">
					<!--通过一个html的按钮实现调用:-->
					<input type="button" onclick="JoinGame()" value="调用"/>
				</div>
			</div>
			
			<!--视频控件-->
			<div id="video">
				<OBJECT  ID="VedioCapture"  CLASSID="clsid:E1971791-858E-44F1-8D58-9D4F30C1B4F8"
					height=280px width=380px>
				</OBJECT> 
				<div id="videoctrl">
					<button type="button" onclick="StartPreview()">开始预览</button>
					<button type="button" onclick="Takepicture()">拍照</button>
					<button type="button" onclick="CancelPreview()">取消预览</button>
					<button type="button" onclick="StopPreview()">停止预览</button>
				</div>
			</div>
		</div>
	 </body>
</html>

停止预览就是定帧,取消预览就是关闭预览窗口。点击拍照即可将抓取的图片复制到剪切板中,打开画图板复制进去就可以看到图片。。。

猜你喜欢

转载自blog.csdn.net/p312011150/article/details/82254665