VFW(Video For Windows)是微软公司1992年推出的关于数字视频的一个软件包,它能使应用程序通过数字化设备从传统的模拟视频源得到数字化的视频剪 辑。VFW的一个关键思想是播放时不需要专用硬件,为了解决数字视频数据量大的问题,需要对数据进行压缩。它引进了一种叫AVI的文件标准,该标准未规定 如何对视频进行捕获、压缩及播放,仅规定视频和音频该如何存储在硬盘上,以及在AVI文件中交替存储视频帧和与之相匹配的音频数据。VFW给程序员提 供.VBX和AVICap窗口类的高级编程工具,使程序员能通过发送消息或设置属性来捕获、播放和编辑视频剪辑。在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的输出信号与其自身的输出信号合并,形
成组合信号显示在计算机的监视器上。只有部分视频捕获卡才具有视频叠加能力。
Visual C++ 在支持VFW方面提供有vfw32.lib、 msacm32.lib 、winmm.lib等类似的库。特别是它提供了功能强大、简单易行、类似于 MCIWnd的窗口类AVICap。AVICap为应用程序提供了一个简单的、基于消息的接口,使之能访问视频和波形音频硬件,并能在将视频流捕获到硬盘 上的过程中进行控制。
开发步骤
●CapCreateCaptureWindow : 在进行视频捕获之前必需要先创建一个“捕获窗”,并以它为基础进行所有的捕获及设置操作。
捕获窗类似于标准控件(如按钮、列表框等),并具有下列功能:
将视频流和音频流捕获到一个AVI文件中;
动态地同视频和音频输入器件连接或断开;
以Overlay或Preview模式对输入的视频流进行实时显示;
在捕获时,可指定所用的文件名并能将捕获文件的内容拷贝到另一个文件;
设置捕获速率;
显示控制视频源、视频格式、视频压缩的对话框;
创建、保存或载入调色板;
将图像和相关的调色板拷贝到剪贴板;
将捕获的单帧图像保存为DIB格式的文件。
●CapDriverConnect : 使一个捕获窗与一个设备驱动程序相关联。单独定义的一个捕获窗是不能工作的,它必需与一个设备相关联,这样才能取得视频信号。
●CapCaptureSetSetup,CapPreviewScale,CapPreviewRate : 设置视频设备的属性。 通过设置TcaptureParms结构变量的各个成员变量,可以控制设备的采样频率、中断采样按键、状态行为等等。设置好 TCaptureParms结构变量后,可以用函数CapCaptureSetSetup使设置生效。之后还可以用CapPreviewScale、 CapPreviewRate来设置预览的比例与速度,也可以直接使用设备的默认值。
●CapOverlay,CapPreview :打开预览。 利用函数CapOverlay选择是否采用叠加模式预览,这样占用系统资源小,并且视频显示速度快。然后用CapPreview启动预览功能,这时就可以在屏幕上看到来自摄像机的图像了。
编程实例
为了能够捕获视频帧,要启动一个捕获帧回调函数VideoStreamCallBack。
捕获一个视频流或当前设备状态时分别使用以下函数:
//捕获一个视频流
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);
自定义的函数2 //定义捕获帧回调函数:
function FrameCallBack(hWnd:HWND; lpVHdr:LongInt) :LongInt; stdcall;
var
DataPoint:^byte;
DibLen,RectWidth,RectHeight:integer;
begin
//转换从回调函数中得到的指针
VideoStr:=LPVIDEOHDR(lpVHdr);
//得到返回的数据大小
DibLen:=VideoStr^.dwBufferLength;
GetMem(DataPoint,64000);
//将帧数据COPY到一个内存中,注意DATAPOINT要先分配空间
CopyMemory(DataPoint,VideoStr^.lpData,Diblen);
//一些其他处理
……
end;
灵活地使用AVICap窗口类的回调函数可以满足各种不同的需求,但要注意从视频卡中捕获的视频数据的格式和图像的长宽要参考视频卡的参数。而且有些视频卡通过设置可支持多种的格式和图像长宽,所以在还原图像时要注意参考所用的视频卡的参数。
与视频捕获相关的编程。
1、定义全局变量:
HWND ghWndCap ; //捕获窗的句柄
CAPDRIVERCAPS gCapDriverCaps ; //视频驱动器的能力
CAPSTATUS gCapStatus ; //捕获窗的状态
2、处理WM_CREATE消息:
//创建捕获窗,其中hWnd为主窗口句柄
ghWndCap = capCreateCaptureWindow((LPSTR)"Capture Window",WS_CHILD | WS_VISIBLE, 0, 0, 300,240, (HWND) hWnd, (int) 0);
//登记三个回调函数,它们应被提前申明
capSetCallbackOnError(ghWndCap, (FARPROC)ErrorCallbackProc); capSetCallbackOnStatus(ghWndCap, (FARPROC)StatusCallbackProc); capSetCallbackOnFrame(ghWndCap, (FARPROC)FrameCallbackProc);
capDriverConnect(ghWndCap,0); // 将捕获窗同驱动器连接
//获得驱动器的能力,相关的信息放在结构变量gCapDriverCaps中
capDriverGetCaps(ghWndCap,&gCapDriverCaps,sizeof(CAPDRIVERCAPS)) ;
3、处理WM_CLOSE消息:
//取消所登记的三个回调函数
capSetCallbackOnStatus(ghWndCap, NULL);
capSetCallbackOnError(ghWndCap, NULL);
capSetCallbackOnFrame(ghWndCap, NULL);
capCaptureAbort(ghWndCap);//停止捕获
capDriverDisconnect(ghWndCap); //将捕获窗同驱动器断开
4、处理菜单项Preview:
capPreviewRate(ghWndCap, 66); // 设置Preview模式的显示速率
capPreview(ghWndCap, TRUE); //启动Preview模式
5、处理菜单项Overlay:
if(gCapDriverCaps.fHasOverlay) //检查驱动器是否有叠加能力
capOverlay(ghWndCap,TRUE); //启动Overlay模式
6、处理菜单项Exit:
SendMessage(hWnd,WM_CLOSE,wParam,lParam);
7、分别处理Setting下的三个菜单项,它们可分别控制视频源、视频格式及显示:
if (gCapDriverCaps.fHasDlgVideoSource)
capDlgVideoSource(ghWndCap); //Video source 对话框
if (gapDriverCaps.fHasDlgVideoFormat)
capDlgVideoFormat(ghWndCap); // Video format 对话框
if (CapDriverCaps.fHasDlgVideoDisplay)
capDlgVideoDisplay(ghWndCap); // Video display 对话框
8、处理Video Stream菜单项,它捕获视频流到一个.AVI文件:
char szCaptureFile[] = "MYCAP.AVI";
capFileSetCaptureFile( ghWndCap, szCaptureFile); //指定捕获文件名
capFileAlloc( ghWndCap, (1024L * 1024L * 5)); //为捕获文件分配存储空间
capCaptureSequence(ghWndCap); //开始捕获视频序列
9、处理Single Frame菜单项:
capGrabFrame(ghWndCap); //捕获单帧图像
10、定义三个回调函数:
LRESULT CALLBACK StatusCallbackProc(HWND hWnd, int nID, LPSTR lpStatusText)
{
if (!ghWndCap) return FALSE;
//获得捕获窗的状态
capGetStatus(ghWndCap, &gCapStatus, sizeof (CAPSTATUS));
//更新捕获窗的大小
SetWindowPos(ghWndCap, NULL, 0, 0, gCapStatus.uiImageWidth,
gCapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE);
if (nID == 0) { // 清除旧的状态信息
SetWindowText(ghWndCap, (LPSTR) gachAppName);
return (LRESULT) TRUE;
}
// 显示状态 ID 和状态文本
wsprintf(gachBuffer, "Status# %d: %s", nID, lpStatusText);
SetWindowText(ghWndCap, (LPSTR)gachBuffer);
return (LRESULT) TRUE;
}
LRESULT CALLBACK ErrorCallbackProc(HWND hWnd, int nErrID,LPSTR lpErrorText)
{
if (!ghWndCap)
return FALSE;
if (nErrID == 0)
return TRUE;// 清除旧的错误
wsprintf(gachBuffer, "Error# %d", nErrID); //显示错误标识和文本
MessageBox(hWnd, lpErrorText, gachBuffer,MB_OK | MB_ICONEXCLAMATION);
return (LRESULT) TRUE;
}
LRESULT CALLBACK FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
if (!ghWndCap)
return FALSE;
//假设fp为一打开的.dat文件指针
fwrite(fp,lpVHdr->lpData,lpVHdr->dwBufferLength,1);
return (LRESULT) TRUE ;
}
值得注意的是:应在.cpp文件中加入#include 一句,在Link设置中加入vfw32.lib。
上述的回调函数FrameCallbackProc是将视频数据直接从缓冲写入文件,也可利用memcpy函数将视频数据直接拷贝到另一缓存。同理,可 定义VideoStreamCallbackProc。capSetCallbackOnVideoStream的使用比 capSetCallbackOnFrame稍微复杂一些。在捕获过程中,当一个新的视频缓冲可得时,系统就调用它所登记的回调函数。在缺省情况下,捕获 窗在捕获过程中不允许其它应用程序继续运行。为了取消这个限制,可以设置CAPTUREPARMS的成员fYield为TRUE或建立一个Yield回调 函数。为了解决潜在的重入(reentry)问题,可在YieldCallbackProc中用PeekMessage过滤掉一些消息,例如鼠标消息。