VFW视频采集详细介绍

关键词 VFW 视频采集 VC

作者(原创)逄格民 2007-11-06

刚刚做了一个利用VFW(Video For Windows)的视频采集程序,就想写出来,给需要的人分享一下。程序并不复杂,

关键是在没人指导的情况下,学习是比较痛苦和漫长的过程,我经历了这个过程,如果大家想避免走弯路,直接看我

下面的解释就好了。由于我仅仅作出了结果,对很多东西的理解也许并不完全正确或者是完全错误,愿请指教。

提前说一句,我的程序是在Visual C++6.0平台下写的。

下面我慢慢说,你也慢慢听。

1 什么是VFW

VFW 是微软的一个软件包,至少可以用来开发视频采集程序,当然还有别的用处,但不是我想关心的。VFW提供了

基于消息的接口,而这些接口,也可以利用它本省定义的宏来实现。

2 怎么使用VFW

写之前提示一句,可以参照MSDN看下面的内容,一定会更好。

(1)创建一个基于对话框的程序,工程名称Grasp

因为要用VFW,所以要包含头文件

可在GraspDlg.h中加入 #include<Vfw.h>,然后Project

->Settings,在link标签页的Object/library modules :里面加入

Vfw32.lib

 

(2)在CGraspDlg类中添加一个窗口句柄

HWND m_hVideo;

(3)利用capCreateCaptureWindow函数创建窗口,并且得到返回的窗口句柄。

m_hVideo=::capCreateCaptureWindow("Me",WS_CHILD | WS_VISIBLE,

0,0,500,500,m_hWnd,0);

上面这个函数写在BOOL CGraspDlg::OnInitDialog()中。参数m_hWnd是你的工程中

对话框的句柄,窗口类中都有这个成员变量,而对话框的类是窗口类的子类,记得?

(4)用capSetCallbackOnFrame宏注册回调函数,也写在BOOL CGraspDlg::OnInitDialog()中。

capSetCallbackOnFrame(m_hVideo, FrameCallbackProc);

上面第二个参数是回调函数的地址,名字可以自己来定义,但是回调函数必须有如下参数和返回值。

LRESULT CALLBACK FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr);

人家规定的,咱们也没办法,就照着写就好啦。

解释一下,什么是回调函数呢,它有什么用处?

回调函数,就是你自己写的函数,符合规定的参数和返回值类型,符合规定的调用约定,比如上面这个函数

就是回调函数,参数和返回值类型都是规定好的,调用约定为CALLBACK,CALLBACK其实是一个宏

#define CALLBACK __stdcall

满足一定条件时,此函数可以被系统自动调用,在回调函数当中,你可以写自己的代码完成一定功能。

比如在这里,用capSetCallbackOnFrame(m_hVideo, FrameCallbackProc)注册后,当每得到一桢数据后,系统就

调用函数FrameCallbackProc。

(5)因为注册了回调函数,所以,当然要自己写出这个函数了。在GraspDlg.cpp中,且在

BOOL CGraspDlg::OnInitDialog()函数之前写下面代码:

LRESULT CALLBACK FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)

{

if (!ghVideo)

return FALSE;

 

return (LRESULT) TRUE ;

}

目前为止,该回调函数还没有什么作用,一会儿我们再来编写函数当中的代码,现在我就写的话,你也不见得看懂,

不是么。一会儿写的话,你就可以轻松明白了。

注意在这个函数中的ghVideo 了么?其实就和上面的m_hVideo一样,可是这里是全局函数,m_hVideo是对话框类的成员变量,

我写m_hVideo编译器是不认识的,对吧,所以,我又在GraspDlg.cpp当中定义了一个全局变量

HWND ghVideo;

并且,在m_hVideo=::capCreateCaptureWindow("Me",WS_CHILD | WS_VISIBLE,

0,0,500,500,m_hWnd,0);

之后加上一句ghVideo=m_hVideo; 这样就可以用ghVideo了。

(6)在BOOL CGraspDlg::OnInitDialog()中继续添加如下代码:

char szDeviceName[80];

char szDeviceVersion[80];

int wIndex;

 

for (wIndex = 0; wIndex < 10; wIndex++)

{

if (capGetDriverDescription (wIndex, szDeviceName,

sizeof (szDeviceName), szDeviceVersion,

sizeof (szDeviceVersion)) )

{

if(capDriverConnect(m_hVideo,wIndex))

{

 

}

}

 

}

上面代码中,capGetDriverDescription是列举所有可用视频的驱动程序,如果列举成功,用capDriverConnect进行连接。

其实,我的机器上就装了一个摄像头,所以,只有当wIndex=0的时候,列举成功,并且连接也成功。这段代码好像很奇怪,因为

列举成功之后,不论是否连接上,都没有做任何事情。其实可以用下面代码代替:

char szDeviceName[80];

char szDeviceVersion[80];

//Get Driver description

//and the code can also be deleted as you want.

capGetDriverDescription (0 szDeviceName,

sizeof (szDeviceName), szDeviceVersion,

sizeof (szDeviceVersion));

//connect window to driver

capDriverConnect(m_hVideo,0);

(7)到这里,再加下面两句话你就会有成就感了,在BOOL CGraspDlg::OnInitDialog()中继续添加如下代码:

capPreviewRate(m_hVideo, 40); // 设置Preview模式的显示速率

capPreview(m_hVideo, TRUE); //启动Preview模式

如果到此为止,已经完成了视频采集的全过程,你运行一下,就可以看到摄像头拍摄的画面了,显示在你的对话框上。

但是,我的目的还没有达到,我其实想在每一桢显示之前,能处理一下这一桢的数据,那么,去哪里找这桢数据存

放的位置呢?

(8)为了完成我的目标,我把步骤(7)中的两句代码先注释掉。在对话框上加一个按钮,并在对单击做出响应的响应函数

中写下面代码:

capGrabFrame(m_hVideo);

这是一个宏,将鼠标移动到这段代码上,右键单击,选择Go To Definition of capGrabFrame,你会看到

#define capGrabFrame(hwnd) ((BOOL)AVICapSM(hwnd, WM_CAP_GRAB_FRAME, (WPARAM)0, (LPARAM)0L))

而继续察看AVICapSM宏你会看到其实是在调用SendMessage函数呢,对吧,其实就是在发送消息。至于消息谁处理了,我们就不去

关心了,我们关心的是,发送消息后,系统会调用我们刚才注册的回调函数

LRESULT CALLBACK FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr) ;

(9)好了,如果你单击按钮,capGrabFrame(m_hVideo)就发送消息了,然后,我们就进入回调函数了,这太好了。

看到回调函数传递的两个参数了么?我们更关心第二个参数,这个就是单击按钮我们捕捉到的一桢数据的入口啊!

LPVIDEOHDR 是结构体VIDEOHDR的指针,而在MSDN中察看结构体VIDEOHDR,我们就可以找到桢数据的存贮位置指针了。

VIDEOHDR定义如下:

typedef struct videohdr_tag {

LPBYTE lpData;

DWORD dwBufferLength;

DWORD dwBytesUsed;

DWORD dwTimeCaptured;

DWORD dwUser;

DWORD dwFlags;

DWORD_PTR dwReserved[4];

} VIDEOHDR, NEAR *PVIDEOHDR, FAR * LPVIDEOHDR;

看到结构体中第一个参数了么?这个就是我们想要的桢数据的指针!后面参数,包括缓冲区长度等。

(10)终于得到了缓冲区的数据,可是,又一个问题出现了,缓冲区中的数据到底具体是啥含义啊?

这桢图像多大啊?size 是多少乘多少的啊?就是我们想要的像素信息么?

好的,我先告诉你,缓冲区中全部是像素信息,我们照着我的步骤这样做,其实是默认了一些参数,包括图像的

长度,宽度,色彩数,等等,那么,这个默认的值是多少呢?

(11)用一下capGetVideoFormat宏吧,你会得到想要的东西。

在BOOL CGraspDlg::OnInitDialog()中继续添加如下代码:

BITMAPINFO bmpInfo;

capGetVideoFormat(m_hVideo,&bmpInfo,sizeof(BITMAPINFO));

BITMAPINFO结构体内容自己看MSDN.定义如下

typedef struct tagBITMAPINFO {

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[1];

} BITMAPINFO, *PBITMAPINFO;

而BITMAPINFOHEADER定义如下:

typedef struct tagBITMAPINFOHEADER{

DWORD biSize;

LONG biWidth;

LONG biHeight;

WORD biPlanes;

WORD biBitCount;

DWORD biCompression;

DWORD biSizeImage;

LONG biXPelsPerMeter;

LONG biYPelsPerMeter;

DWORD biClrUsed;

DWORD biClrImportant;

} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

加入步骤(11)的两句话,调试运行,我发现,bmpInfo.bmiHeader.biWidth为320,就是采集的图像宽度;

bmpInfo.bmiHeader.biHeight为240,就是采集的图像高度,这些都是默认值,也可以改变这些值,通过

capSetVideoFormat宏来实现。

(12)那么,我们在步骤(9)中,回调函数第二个参数对应的结构体VIDEOHDR中,图像数据缓冲区的大小

dwBufferLength是多少呢?我们可以在回调函数中加一个MessageBox函数,输出这个值,

我们就可以发现,为230400,这个数很好,正好等于图像宽度X图像高度的3倍,

也就是说,是图像像素数目的3倍,这就对了,每个像素用3个字节存储的嘛。好啦,我们知道了,桢缓冲区中,存储

的完全是图像的像素信息,那么,具体哪个值对应哪个像素呢?

存储顺序是这样的:先从图像最下面一行开始,从左向右,依次存储,每一个像素用连续的3个字节,分别为B(蓝色分量),

G(绿色分量),R(红色分量)。然后存储倒数第二行,仍然按照图像从左向右存储,然后倒数第三行,

倒数第四行。。。。。。等等,最后存储正数第一行。

好啦,我所有想说的都说完啦,你明白了么?好累,现在是晚上,快10点钟了,我正好要去跑步去了。

希望我的工作可以带给你一些帮助,我也不反对你转贴,但是要带上我的名字,或者在文章后面标记:(转),然后

还要加上我的名字啊,呵呵。防止盗版就对了。

 

转自:http://dongtantan.blog.163.com/blog/static/280626462008101105526681/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值