原文地址: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>
停止预览就是定帧,取消预览就是关闭预览窗口。点击拍照即可将抓取的图片复制到剪切板中,打开画图板复制进去就可以看到图片。。。