VFW概念
VFW是微软公司1992年推出的关于数字视频的一个软件包,它能使应用程序通过数字化设备从传统的模拟视频源得到数字化的视频剪辑。VFW的一个关键思想是播放时不需要专用硬件,为了解决数字视频数据量大的问题,需要对数据进行压缩。它引进了一种叫AVI的文件标准,该标准未规定如何对视频进行捕获、压缩及播放,仅规定视频和音频该如何存储在硬盘上,以及在AVI文件中交替存储视频帧和与之相匹配的音频数据。
VFW给程序员提供.VBX和AVICap窗口类的高级编程工具,使程序员能通过发送消息或设置属性来捕获、播放和编辑视频剪辑。
VFW提供了基于消息的接口,而这些接口,也可以利用它本省定义的宏来实现。
在Windows 9x系统中,当用户在 安装VFW时,安装程序会自动地安装配置视频所需要的组件,如设备驱动程序、视频压缩程序等。
VFW主要由以下6个模块组成:
●AVICAP.DLL:包含执行视频捕获的函数,它给AVI文件的I/O处理和视频、音频设备驱动程序提供一个高级接口;
●MSVIDEO.DLL:包含一套特殊的DrawDib函数,用来处理屏幕上的视频操作;
●MCIAVI.DRV:包括对VFW的MCI命令解释器的驱动程序;
●AVIFILE.DLL:包含由标准多媒体I/O(mmio)函数提供的更高的命令,用来访问.AVI文件;
●视频压缩管理器(ICM):用于管理的视频压缩/解压缩的编译码器(Codec);
●音频压缩管理器ACM:提供与ICM相似的服务,适用于波形音频。
AVICap在显示视频时提供的两种模式:
(A)预览(Preview)模式:该模式使用CPU资源,视频帧先从捕获硬件传到系统内存,接着采用GDI函数在捕获窗中显示。在物理上,这种模式需要通过VGA卡
在监视器上显示。
(B)叠加(Overlay)模式:该模式使用硬件叠加进行视频显示,叠加视频的显示不经过VGA卡,叠加视频的硬件将VGA的输出信号与其自身的输出信号合并,形
成组合信号显示在计算机的监视器上。只有部分视频捕获卡才具有视频叠加能力。
AVICap为应用程序提供了一个简单的、基于消息的接口,使之能访问视频和波形音频硬件,并能在将视频流捕获到硬盘上的过程中进行控制。
Microsoft Video for Windows(VFW) 提供的函数可以让应用程序去处理视频数据。 VFW 在16位Windows的时候就被引入了。它的许多重要功能已经被DirectX取代了。 下面介绍VFW的视频捕获:
你可以使用windows的AVICap 类轻松地完成视频捕获。AVICap 提供给应用程序一个简单的、基于消息的接口去访问视频设备和录音设备,并且可以控制处理视频流捕获。AVICap支持实是视频流捕获和实时单帧图像捕获。另外,AVICap 提供了对视频源的控制(MCI媒体控制接口设备),因此使用者可以通过应用程序控制一个视频源开始和结束的位置,并且可以加大对帧捕获的控制。
VFW 是Microsoft公司为开发Windows平台下的视频应用程序提供的软件工具包,
提供了一系列应用程序编程接口(API),
用户可以通过这些接口很方便地实现视频捕获、视频编辑及视频播放等通用功能,
还可利用回调函数开发比较复杂的视频应用程序。
该技术的特点是播放视频时不需要专用的硬件设备,而且应用灵活,可以满足视频应用程序开发的需要。Windows操作系统自身就携带了VFW技术,系统安装时,会自动安装VFW的相关组件。
VFW处理视频原理
用AVICAP.DLL实现图像采集,首先要用函数capCreateCaptureWindowA创建一个视频采集窗口,然后向视频采集窗口发送相应的消息,实现视频设备的连接、回调函数设置、预览比例和速率设置、预览和叠加模式的设置、图像文件设置等操作。如果以上操作成功,就可以通过视频设备采集图像了。回调函数由程序员编写,用于特殊的视频采集中,例如,在视频会议中用回调函数将采集的视频和音频实时地传递到远程计算机中。
在VC++中,头文件VFW.H中不仅包含了AVICAP.DLL中函数的原型定义,还定义了与视频采集有关的数据结构、消息和发送消息的宏。利用这些函数、数据结构、消息和发送消息的宏可以方便地编写图像采集程序,如果要对采集的图像实时处理,可以通过编写回调函数实现。
2.1 建立捕获窗口
利用AVICAP 组件函数 capCreateCaptureWindow() 建立视频捕获窗口,它是所有捕获工作及设置的基础,其主要功能包括:① 动态地同视频和音频输入器连接或断开;② 设置视频捕获速率;③ 提供视频源、视频格式以及是否采用视频压缩的对话框;④ 设置视频采集的显示模式为Overlay或为Preview; ⑤ 实时获取每一帧视频数据;⑥ 将一视频流和音频流捕获并保存到一个AVI文件中; ⑦ 捕获某一帧数字视频数据,并将单帧图像以DIB格式保存;⑧ 指定捕获数据的文件名,并能将捕获的内容拷贝到另一文件。
2.2 登记回调函数[2]
登记回调函数用来实现用户的一些特殊需要。在以一些实时监控系统或视频会议系统中,需要将数据流在写入磁盘以前就必须加以处理,达到实时功效。应用程序可用捕获窗来登记回调函数,以便及时处理以下情况:捕获窗状态改变、出错、使用视频或音频缓存、放弃控制权等,相应的回调函数分别为 capStatusCallback(), capErrorCallback(), capVideoStreamCallback(), capWaveStreamCallback(),capYieldCallback()。
2.3 获取捕获窗口的缺省设置
通过宏capCaptureGetSetup(hWndCap,&m_Parms,sizeof(m_Parms))来完成。
2.4 设置捕获窗口的相关参数
通过宏capCaptureSetSetup(hWndCap,&m_Parms,sizeof(m_Parms))来完成。
2.5 连接捕获窗口与视频捕获卡
通过宏capDriveConnect(hWndCap,0)来完成。
2.6 获取采集设备的功能和状态
通过宏capDriverGetCaps(hWndCap,&m_CapDrvCap,sizeof(CAPDRIVERCAPS))来获取
视频设备的能力,通过宏capGetStatus(hWndCap,&m_CapStatus,sizeof(m_CapStatus))
来获取视频设备的状态。
2.7 设置捕获窗口显示模式
视频显示有Overlay(叠加)和Preview(预览)两种模式。在叠加模式下,捕获视频数据布展系统资源,显示速度快,视频采集格式为YUV格式,可通过capOverlay(hWndCap,TRUE)来设置;预览模式下要占用系统资源,视频由系统调用GDI函数在捕获窗显示,显示速度慢,它支持RGB视频格式。
2.8 捕获图像到缓存或文件并作相应处理
若要对采集数据进行实时处理,则应利用回调机制,由capSetCallbackOnFrame(hWndCap, FrameCall-
backProc)完成单帧视频采集;由capSetCallbackOnVideoStream(hWndCap, VideoCallbackProc)完成视频流采集。如果要保存采集数据,则可调用capCaptureSequence(hWnd);要指定文件名,可调用capFileSetCap-
ture(hwnd, Filename)。
2.9 终止视频捕获 断开与视频采集设备的连接
调用capCatureStop(hWndCap)停止采集,调用capDriverDisconnect(hWndCap), 断开视频窗口与捕获驱动程序的连接。
3 视频编辑和播放
利用VFW,不仅可以实现视频流的实时采集,还提供了编辑和播放功能,主要通过AVIFILE、ICM、ACM、MCIWnd 等组件之间的协作来完成。
3.1 编辑处理AVI[3]视频文件的一般流程(图2)
图2 编辑视频文件的一般流程图
Fig.2 General flow chart of editing video files
1) AVIFileInit();//初始化;
2) AVIFileOpen(); //打开一个AVI文件并获文件的句柄;
3) AVIFileInfo(); //获取文件的相关信息,如图像的Width和Height等;
4) AVIFileGetStream(); //建立一个指向需要访问的数据流的指针;
5) AVIStreamInfo(); //获取存储数据流信息的AVISTREAMINFO结构;
6) AVIStreamRead(); //读取数据流中的原始数据, 对AVI文件进行所需的编辑处理;
7) AVIStreamRelease(); //释放指向视频流的指针;
8) AVIFileRelease();AVIFileExit(); //释放AVI文件。
若数据是压缩过的,则用AVIStreamGetFrameOpen(),AVIStreamGetFrame()和AVIStreamGetFrameClose()来操作,可以完成对视频流的逐帧分解。
3.2 视频播放
对于实现视频流的播放,VFW提供了MCIWnd窗口类[4],主要用于创建视频播放区,控制并修改MCI窗口当前加载媒体的属性。一个由函数、消息和宏组成的库与MCIWnd相关联,通过它们可以进行AVI文件操作,很方便地使应用程序完成视频播放功能。
1)MCIWndCreate(); //注册MCIWnd窗口类,创建MCIWnd窗口,并指定窗口风格;
2)AVIFileInit(); //初始化;
3) AVIFileOpen(); //打开AVI文件;
4) AVIFileGetStream(); //获得视频流;
5)运用相关函数进行各种播放任务:MCIWndPlay()正向播放AVI文件内容,MCIWndPlayReverse()反向播放,MCIWndResume() 恢复播放,MCIWndPlayPause()暂停播放,MCIWndStop()停止播放等等。
6) AVIStreamRelease(); //释放视频流;
7)AVIFileRease();AVIFileExit(); //断开与AVI文件的连接,释放视频源。
由以上步骤可以看出,视频播放是视频编辑其中的一种操作。
——————————————————————————————————————————
●CapCreateCaptureWindow : 在进行视频捕获之前必需要先创建一个“捕获窗”,并以它为基础进行所有的捕获及设置操作。
捕获窗类似于标准控件(如按钮、列表框等),并具有下列功能:
●CapDriverConnect : 使一个捕获窗与一个设备驱动程序相关联。单独定义的一个捕获窗是不能工作的,它必需与一个设备相关联,这样才能取得视频信号。
●CapCaptureSetSetup,CapPreviewScale,CapPreviewRate : 设置视频设备的属性。
通过设置TcaptureParms结构变量的各个成员变量,可以控制设备的采样频率、中断采样按键、状态行为等等。
设置好 TCaptureParms结构变量后,可以用函数CapCaptureSetSetup使设置生效。
之后还可以用CapPreviewScale、 CapPreviewRate来设置预览的比例与速度,也可以直接使用设备的默认值。
●CapOverlay,CapPreview :打开预览。 利用函数CapOverlay选择是否采用叠加模式预览,这样占用系统资源小,并且视频显示速度快。然后用CapPreview启动预览功能,这时就可以在屏幕上看到来自摄像机的图像了。
VFW函数API总结
所有的实例主要使用AVICAP32.DLL中的函数和USER32.DLL中的函数,函数语法及结构如下。
(1)capCreateCaptureWindow函数
该函数用于创建一个视频捕捉窗口。语法如下:
[DllImport("avicap32.dll")]
public static extern IntPtr capCreateCaptureWindowA
(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
参数说明如下。
l lpszWindowName:标识窗口的名称。
l dwStyle:标识窗口风格。
l x、y:标识窗口的左上角坐标。
l nWidth、nHeight:标识窗口的宽度和高度。
l hWnd:标识父窗口句柄。
l nID:标识窗口ID。
l 返回值:视频捕捉窗口句柄。
capCreateCaptureWindowA
Declare capCreateCaptureWindow in "avicap32.dll" as "capCreateCaptureWindowA" ;
String lpszWindowName,;
Long dwStyle ,;
Long x ,;
Long y ,;
Long nWidth,;
Long nHeight ,;
Long hwndParent,;
Long nID
函数返回打开的句柄值。
· lpszWindowName:视频窗口的名字
· dwStyle:样式
· x:左边距(相对于父窗口,一般为0)
· y:上边距(相对于父窗口,一般为0)
· nWidth:视频窗口的宽度(注:此项并不会改变摄像头的分辨率,超出部分不会被刷新)
· nHeight:视频窗口的高度
· hWndParent:父窗口句柄
· nID:标识符(一般为0)
这是VFW编程最基本的函数了,例如:
lwndC = capCreateCaptureWindowA("我的视频监视窗口", WS_CHILD , 0, 0, 160, 120, hwnd, 0)
AVICAP.DLL是微软公司提供的用于视频采集的动态链接库文件,其中定义了视频采集的函数。AVICAP.DLL支持实时的视频流采集和单帧采集并提供对视频源的控制。利用AVICAP.DLL可以方便地编写视频和图像采集程序。
在AVICAP.DLL中定义了如下的接口函数:
(1)创建视频采集窗口函数
HWND WINAPI capCreateCaptureWindowA (
LPCSTR lpszWindowName,
DWORD dwStyle,
int x, int y, int nWidth,
int nHeight,
HWND hwndParent, int nID);
其中,参数lpszWindowName指明窗口的名称,参数dwStyle指明窗口的风格,参数x、y、nWidth和nHeight指明窗口的位置和大小,参数hwndParent指明父窗口的句柄,参数nID指明窗口标识。
在AVICAP.DLL中还定义了一个创建视频采集窗口函数,是capCreateCaptureWindowW,其函数原型与capCreateCaptureWindowA相同,它们的区别是前一个函数用于ANSI字符编码方式下,而后一个函数用于UNICODE字符编码方式下。在头文件VFW.H中用条件编译和宏定义统一为capCreateCaptureWindow。
capGetDriverDescriptionA
(2)取得视频采集设备描述信息函数
BOOL WINAPI capGetDriverDescriptionA (UINT wDriverIndex,LPSTR lpszName,
int cbName,LPSTR lpszVer, int cbVer);
其中,参数wDriverIndex指明设备的索引号,参数lpszName指明设备的名称缓冲区的地址,参数cbName指明设备的名称缓冲区的大小,参数lpszVer指明设备的描述缓冲区的地址,参数cbVer指明设备的描述缓冲区的大小。
同样,在AVICAP.DLL中还定义了一个取得视频采集设备描述信息函数,是capGetDriverDescriptionW,它们的区别也是使用于不同的编码方式下。在头文件VFW.H中统一为capGetDriverDescription。
(2)SendMessage函数
用于向Windows系统发送消息机制。
[DllImport("User32.dll")]
private static extern bool SendMessage
(IntPtr hWnd, int wMsg, int wParam, int lParam);
参数说明如下。
l hWnd:窗口句柄。
l wMsg:将要发送的消息。
l wParam、lParam:消息的参数,每个消息都有两个参数,参数设置由发送的消息而定。
捕获一个视频流或当前设备状态时分别使用以下函数:
//捕获一个视频流
CapSetCallbackOnVideoStream;
//得到一个设备错误
CapSetCallbackOnError;
//得到一个设备状态
CapSetCallbackOnStatus
}
自定义的函数1 //定义一个帧捕获回调函数
CapSetCallbackOnFrame (ghCapWnd,LongInt(@VideoStreamCallBack));
//将一个捕获窗口与一个设备驱程相关联,第二个参数是个序号,当系统中装有多个显视驱动程序时,其值分别依次为0到总个数
CapDriverConnect(ghCapWnd, 0);
//设置设备属性的结构变量
CapParms.dwRequestMicroSecPerFrame:=40000;
CapParms.fLimitEnabled := FALSE;
CapParms.fCaptureAudio := FALSE; // NO Audio
CapParms.fMCIControl := FALSE;
CapParms.fYield := TRUE;
CapParms.vKeyAbort := VK_ESCAPE;
CapParms.fAbortLeftMouse := FALSE;
CapParms.fAbortRightMouse := FALSE;
//使设置生效
CapCaptureSetSetup(ghCapWnd,LongInt(@CapParms),sizeof(TCAPTUREPARMS));
//设置预览时的比例
CapPreviewScale(ghCapWnd, 1);
//设置预览时的帧频率
CapPreviewRate(ghCapWnd,66);
//如果要捕获视频流,则要使用函数指定不生成文件。否则将会自动生成AVI文件
CapCaptureSequenceNoFile(ghCapWnd);
//指定是否使用叠加模式,使用为1,否则为0
CapOverlay(ghCapWnd, 1);
//打开预览
CapPreview(ghCapWnd, 1);
//停止捕获
capCaptureAbort(ghCapWnd);
//将捕获窗同驱动器断开
capDriverDisconnect(ghCapWnd);
VFW示例
名词解释
avicap32.dll
avicap32.dll是Windows API应用程序接口相关模块,用于对摄像头和其它视频硬件进行AVI电影和视频的截取。
API编程使用方法:
'// Capture Function Declares
Declare Function capCreateCaptureWindow Lib "avicap32.dll" Alias "capCreateCaptureWindowA" _
(ByVal lpszWindowName As String, _
ByVal dwStyle As Long, _
ByVal x As Long, _
ByVal y As Long, _
ByVal nWidth As Long, _
ByVal nHeight As Long, _
ByVal hwndParent As Long, _
ByVal nID As Long) As Long 'returns HWND
Declare Function capGetDriverDescription Lib "avicap32.dll" Alias "capGetDriverDescriptionA" _
(ByVal dwDriverIndex As Long, _
ByVal lpszName As String, _
ByVal cbName As Long, _
ByVal lpszVer As String, _
ByVal cbVer As Long) As Long 'returns C BOOL
user32.dll
user32.dll是Windows用户界面相关应用程序接口,用于包括Windows处理,基本用户界面等特性,如创建窗口和发送消息。
在早期32-bit 版本的Windows中,用户控件是在ComCtl32中实现的,但是一些控件的显示功能是在User32.dll中实现的。例如在一个窗口中非客户区域(边框和菜单)的绘制就是由User32.dll来完成的。User32.dll 是操作系统的一个核心控件,它和操作系统是紧密联系在一起的。也就是说,不同版本的Windows中User32.dll 是不同。因此,应用程序在不同版本的Windows中运行的时候,由于User32.dll的不同,会导致应用程序的界面通常会有微小的不同
VFW开发总结
使用VFW写的C#控制摄像头最大的问题就在于需要自己手动另起一个线程。(这里,我们定义一个叫AviCapture.cs的类,用于引入avicap32.dll以及相关的内容)在avicap32.dll中,CAPTUREPARMS结构里有一个fYield的东东,代表的意思是另起线程标志位,如果为真,则程序重新启动一个线程用于视频流的捕获,默认值是假。但是如果你是为了真,你必须要在程序中处理一些潜在的操作,因为当视频捕获时,其他操作并没有被屏蔽。。在AviCapture这个类的基础上定义一个叫Video的类(自己定义的),实现控制设想头的一些方法,如打开摄像头,关闭摄像头,开始录像,结束录像,拍照片等等。。。。在Video类中还要定义两个C#控制摄像头函数如下:
使用如下代码加入视频捕获,
function capCreateCaptureWindow(
lpszWindowName : LPCSTR;
dwStyle : DWORD;
x, y : int;
nWidth, nHeight : int;
hwndParent : HWND;
nID : int
): HWND;
lpszWindowName: 是捕获窗口的名称,可以是任意字符串,delphi中是pchar类型
dwStyle: 是捕获窗口的类型,一般设为WS_CHILD | WS_VISIBLE.
x, y, nWidth, nHeight: 是显示捕获视频的rect
hwndParent: 是显示视频窗口的句柄
nID : 默认为0;
函数返回值Hwnd类型,即摄像设备的句柄,以后的操作基本上都是针对设备句柄进行的.
然后用function capDriverConnect(hwnd: HWND; i: INT): BOOL;连接设备的驱动,
hwnd为设备句柄,i是驱动的序号(如果系统装有多个设备驱动,要选择一个正确的驱动才行,这些稍后在介绍).
驱动连接成功后
,用function capPreviewRate(hwnd: HWND; wMS: WORD):
BOOL
;函数设置预览时的帧速,wMS为帧速值(单位:帧/秒),一般取值范围(10-30).
设置完速率后
,用function
capPreview(hwnd: HWND; f: BOOL): BOOL;函数进行预览了,设置f为true后就可以看到摄像头的画面了.
以上简单的显示了视频画面,但是在显示视频画面的过程中还要进行一些参数的设置.今天就Ok了.
窗口句柄 (HWND)。
m_hCapWnd=capCreateCaptureWindow((LPTSTR)TEXT("视频捕捉测试程序"),WS_CHILD|WS_VISIBLE|WS_EX_CLIENTEDGE|WS_EX_DLGMODALFRAME,0,0,rect.Width(),rect.Width(),pWnd ->GetSafeHwnd(),0); // 设置预示窗口
ASSERT(m_hCapWnd);
if(capDriverConnect(m_hCapWnd,0)){// 连接第0 号驱动器
m_bInit=TRUE;
// 得到驱动器的性能
capDriverGetCaps(m_hCapWnd,sizeof(CAPDRIVE RCAPS), &m_CapDrvCap);
if(m_CapDrvCap.fCaptureInitialized){
// 如果初始化成功
capGetStatus(m_hCapWnd, &m_CapStatus,sizeof(m_CapStatus)); // 得到驱动器状态
capPreviewRate(m_hCapWnd,30); // 设置预示帧频
capPreview(m_hCapWnd,TRUE); // 设置预示方式
}
else{// 初始化未成功
AfxMessageBox("视频捕捉卡初始化失败!");
AfxGetMainWnd() ->PostMessage (WM_CLOSE);
}
}
else{// 未能连接到驱动器
AfxMessageBox("与视频捕捉卡连接失败!");
AfxGetMainWnd() ->PostMessage(WM_CLOSE);
}
m_CapFileName="c://Capture.avi";// 设置捕获文件
capFileSetCaptureFile(m_hCapWnd,m_CapFileName.GetBuffer(255));
5、在对话框类中加入响应“设置格式”消息的函数OnFormat()。
capDlgVideoFormat(m_hCapWnd);// 设置格式对话框
6、在对话框类中加入响应“设置图像源”消息的函数OnSource()。
capDlgVideoSource(m_hCapWnd);// 设置图像源对话框
7、在对话框类中加入响应“设置压缩”消息的函数OnCompress()。
capDlgVideoCompression(m_hCapWnd);// 设置压缩对话框
8、在对话框类中加入响应“捕捉”消息的函数OnCapture()。
capCaptureGetSetup(m_hCapWnd, &m_Parms,sizeof(m_Parms));// 得到设置参数
if(capCaptureSetSetup(m_hCapWnd, &m_Parms,sizeof(m_Parms))==TRUE){
BOOL suc=TRUE;
suc=capCaptureSequence(m_hCapWnd); // 捕捉到文件
return suc};
else
return FALSE;
注意点:在VideoCaptureDlg.h中把afx_msg void OnCapture();改为afx_msg BOOL OnCapture();
在VideoCaptureDlg.cpp 修改为BOOL CVideoCaptureDlg::OnCapture() //捕捉
9、在对话框类中加入响应“定帧”消息的函数OnFreezed()。
capPreview(m_hCapWnd,FALSE);// 定帧
10、在对话框类中加入响应“单帧捕获”消息的函数OnImage()。
capGrabFrameNoStop(m_hCapWnd);// 截获当前图像
capEditCopy(m_hCapWnd);// 将图像拷贝到剪贴板
11、在对话框类中加入响应“停止”消息的函数OnStop()。
capCaptureStop(m_hCapWnd);// 停止捕捉
12、在对话框类中加入响应“退出”消息的函数OnExit()退出前断开捕捉器与驱动器的连接,并关闭窗口。
capDriverDisconnect(m_hCapWnd);
CDialog::OnCancel();
附加说明:以上添加的按钮用于捕捉图像(button);
另外添加(Static Text)用于创建并设置捕获窗口;
保存图像的方法:点击“单帧捕获”——>打开附近里的“画图”——>“编辑”中的“粘贴”——>即可保存捕捉的位图。
以上方法主要是采用了VFW的函数和宏,有兴趣的朋友可以尝试用消息的方法,同样也可以实现。
如:SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0,
要做的就是根据相关的功能改变相应的参数就可以了。
capCreateCaptureWindow(
lpszWindowName : LPCSTR;
dwStyle : DWORD;
x, y : int;
nWidth, nHeight : int;
hwndParent : HWND;
nID : int
): HWND;
lpszWindowName: 是捕获窗口的名称,可以是任意字符串,delphi中是pchar类型
dwStyle: 是捕获窗口的类型,一般设为WS_CHILD | WS_VISIBLE.
x, y, nWidth, nHeight: 是显示捕获视频的rect
hwndParent: 是显示视频窗口的句柄
nID : 默认为0;
函数返回值Hwnd类型,即摄像设备的句柄,以后的操作基本上都是针对设备句柄进行的.
然后用function capDriverConnect(hwnd: HWND; i: INT): BOOL;连接设备的驱动,hwnd为设备句柄,i是驱动的序号(如果系统装有多个设备驱动,要选择一个正确的驱动才行,这些稍后在介绍).
驱动连接成功后,用function capPreviewRate(hwnd: HWND; wMS: WORD): BOOL;函数设置预览时的帧速,wMS为帧速值(单位:帧/秒),一般取值范围(10-30).
设置完速率后,用function capPreview(hwnd: HWND; f: BOOL): BOOL;函数进行预览了,设置f为true后就可以看到摄像头的画面了.
以上简单的显示了视频画面,但是在显示视频画面的过程中还要进行一些参数的设置.今天就Ok了.
1、 int intDevice = 0;是什么意思?起初我猜测0表示第一个摄像头,那么1不就表示第二个摄像头吗?经过我的尝试发现并非如此,那么0到底是什么意思呢?
回答:这只是捕获窗口的窗口名称而已。你可以看到实际上他转换成了String类型,在capCreateCaptureWindow里面作为第一个参数,这个参数就是表示窗口名称。
2、hHwnd = Camera1.capCreateCaptureWindowA(ref refDevice, 1342177280, 0, 0, 640, 480, this.panel1.Handle.ToInt32(), 0);各个参数的含义,这个我从msdn中查到了,但是对于第二个参数和最后一个参数还是不理解,希望大家给以解答。
回答:第二个参数表示窗体风格,也就是CreateWindow的时候都要使用的,具体的说就是表示你窗体有没得边框啦,支持不支持拖放拉等等。这个可以去MSDN里面查 CreateWindowEx 函数便知。
最后一个参数表示窗口标识符。只要每个窗体不一样,能区分即可。
3、Camera1.SendMessage(hHwnd, 0x
回答:0x
你要连接两个摄像头,着眼点就是这儿,只需要重新创建一个铺货窗口,并连接到另外一个摄像头的驱动上,事先知道设备ID就行了。这个可以通过设备枚举获得,具体参见MSND中Enumerating Installed Capture Drivers相关章节。就OK了。
4、 Camera1.SendMessage(this.hHwnd, 0x435, -1, 0);
Camera1.SendMessage(this.hHwnd, 0x434, 0x42, 0);
Camera1.SendMessage(this.hHwnd, 0x432, -1, 0);
分别表示的意思是什么?
回答:0x435表示WM_CAP_SET_SCALE,可以打开或者关闭预览的比例缩放,后面是参数,这些都可以在MSDN中查到。我就不说了。比如这里是-1就是表示为真,也就是说图像会根据窗口大小进行缩放。
0x434表示WM_CAP_SET_PREVIEWRATE,这个是设置显示速度的。也就是帧率。
0x432表示WM_CAP_SET_PREVIEW,这个标识打开关闭预览模式。这儿是-1,自然表示为真,也就是打开了。
5、在一个程序中,能不能同时使用两个摄像头,需不需要第三方软件的支持?
回答:当然可以。不需要三方支持。这是微软的活儿,只有别人依赖他支持,怎么可能微软还需要依赖别人。
解释一下,什么是回调函数呢,它有什么用处?
回调函数,就是你自己写的函数,符合规定的参数和返回值类型,符合规定的调用约定,比如上面这个函数
就是回调函数,参数和返回值类型都是规定好的,调用约定为CALLBACK,CALLBACK
其实是一个宏
#define CALLBACK__stdcall
满足一定条件时,此函数可以被系统自动调用,在回调函数当中,你可以写自己的代码完成一定功能。
比如在这里,用capSetCallbackOnFrame(m_hVideo, FrameCallbackProc)注册后,当每得到一桢数据后,系统就
调用函数FrameCallbackProc。
但是,我的目的还没有达到,我其实想在每一桢显示之前,能处理一下这一桢的数据,那么,去哪里找这桢数据存
放的位置呢?
(8)为了完成我的目标,我把步骤(7)中的两句代码先注释掉。在对话框上加一个按钮,并在对单击做出响应的响应函数
中写下面代码:
capGrabFrame(m_hVideo);
这是一个宏,将鼠标移动到这段代码上,右键单击,选择Go To Definition of capGrabFrame,你会看到
#define capGrabFrame(hwnd)((BOOL)AVICapSM(hwnd, WM_CAP_GRAB_FRAME, (WPARAM)0, (LPARAM)
而继续察看AVICapSM宏你会看到其实是在调用SendMessage函数呢,对吧,其实就是在发送消息。至于消息谁处理了,我们就不去
关心了,我们关心的是,发送消息后,系统会调用我们刚才注册的回调函数
LRESULT CALLBACK FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr) ;
(9)好了,如果你单击按钮,capGrabFrame(m_hVideo)就发送消息了,然后,我们就进入回调函数了,这太好了。
看到回调函数传递的两个参数了么?我们更关心第二个参数,这个就是单击按钮我们捕捉到的一桢数据的入口啊!
LPVIDEOHDR 是结构体VIDEOHDR的指针,而在MSDN中察看结构体VIDEOHDR,我们就可以找到桢数据的存贮位置指针了。
VIDEOHDR定义如下:
typedef struct videohdr_tag {
LPBYTElpData;
DWORDdwBufferLength;
DWORDdwBytesUsed;
DWORDdwTimeCaptured;
DWORDdwUser;
DWORDdwFlags;
DWORD_PTRdwReserved[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;
RGBQUADbmiColors[1];
} BITMAPINFO, *PBITMAPINFO;
而BITMAPINFOHEADER定义如下:
typedef struct tagBITMAPINFOHEADER{
DWORDbiSize;
LONGbiWidth;
LONGbiHeight;
WORDbiPlanes;
WORDbiBitCount;
DWORDbiCompression;
DWORDbiSizeImage;
LONGbiXPelsPerMeter;
LONGbiYPelsPerMeter;
DWORDbiClrUsed;
DWORDbiClrImportant;
} 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(红色分量)。然后存储倒数第二行,仍然按照图像从左向右存储,然后倒数第三行,
倒数第四行。。。。。。等等,最后存储正数第一行。
事件委托和委托有啥区别啊?委托不是就够了吗?为什么还要事件委托呢
最佳答案
1.
事件在本类型外部只能用“+=”和“-=”去订阅/取消订阅代理,
委托不管在本类型外部还是内部都可以用“+=”、“-=”和“=”订阅/取消订阅代理。
2.
事件只能在本类型内部“触发”,
委托不管在本类型内部还是外部都可以“调用”。
即:事件,只有本类才能激发这个事件,如果用委托取代的话,可想而知,
举个例子,按钮的Click事件,只有你的鼠标点击按钮才能由按钮触发,如果Click是委托的话,不管鼠标点击不单击那个按钮,我只要用程序调用这个委托,就可以使得按钮激发Click事件,完全不符合事实,