代码改变世界

VFW基础细节

2011-08-25 18:49  沐海  阅读(1000)  评论(0编辑  收藏  举报


视频捕获快速入门 

视频捕捉将一个视频流和音频流数字化然后存储在硬盘或其他存储介质上

一个AVICap视窗口句柄描述了声频与视频流的细节这样就使你的应用程序从AVI文件格式声频视频缓冲管理低层声频视频驱动访问等等解脱出来, AVICap为应用程序提供了一个灵活的介面你可以仅仅使用如下几行代码就可以将视频捕捉加入你的程序

hWndC = capCreateCaptureWindow ( "My Own Capture Window", 

WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID); 

 

SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0 /* wIndex */, 0L); 

SendMessage (hWndC, WM_CAP_SEQUENCE, 0, 0L); 

 

一个宏其实也是使用SendMessage, 只不过提供给程序一个更易读的代码而已下面的这些示例就是使用宏的方法将视频捕捉加入程序

hWndC = capCreateCaptureWindow ( "My Own Capture Window", 

WS_CHILD | WS_VISIBLE , 0, 0, 160, 120, hwndParent, nID); 

capDriverConnect (hWndC, 0); 

capCaptureSequence (hWndC); 


当你创建了一个AVICap类的捕捉窗口并将它连接到一个视频驱动时此捕捉窗口即可以开始捕捉数据你的程序可以简单的发送WM_CAP_SEQUENCE消息(或者使用capCaptureSequence)来开始捕捉

如果是缺省的设置, WM_CAP_SEQUENCE会开始捕捉视频音频流到CAPTURE.AVI文件中直到下面的某一事件发生为止

用户按下了ESC键或者一个鼠标键 

你的应用程序终止或异常中断捕捉操作 

磁盘已满 


在一个应用程序里你可以发送WM_CAP_STOP消息来终止捕捉数据(或者使用capCaptureStop), 你也可以发送WM_CAP_ABORT消息(或者使用capCaptureAbort)来终止


.基本的捕获设置 

基 本的捕获设置包括:设置捕获速度(每秒捕获多少帧),是否同时捕获声频,捕获缓冲,允许最大丢失多少帧,是否使用DOS内存,以及用键盘的哪个键或鼠标的哪个键来终止捕获等等。这些基本的设置都可以使用CAPTUREPARAMS结构来描述,你可以使用capCaptureGetSetup宏来得到当前的设置,然后改变此结构的成员变量,再使用capCaptureSetSetup宏设置新的设置。 

例如: 

1.设置捕获速度: 

捕 捉速度是指捕捉任务每秒钟捕获的帧数你可以发送WM_CAP_GET_SEQUENCE_SETUP消息(或者使用capCaptureGetSetup)来得到当前的捕捉速度当前的捕捉速度保存在CAPTUREPARAMS结构的dwRequestMicroSecPerFrame成员变量中你可以通过设置此变量来改变当前设置单位是每毫秒连续的帧数你可以发送WM_CAP_SET_SEQUENCE_SETUP消息(或者使用capCaptureSetSetup), dwRequestMicroSecPerFrame的值是66667, 相当于每秒15


2.设置终止捕获 

你可以允许用户按下某键或某组合键或者鼠标的左右键来终止一个捕获任务如果是实时的捕获则捕获的文件将会被丢弃如果是单步捕获在终止之前所捕获的内容将会被保存

你 可以通过发送WM_CAP_GETQUENCE_SETUP消息(或者使用capCaptureGetSetup)来得到当前的设置当前的按键设置保存在CAPTUREPARAMSvKeyAbort成员中当前的鼠标设置保存在fAbortLeftMousefAbortRightMouse成员中你可以设置新的按键或按键组合或者鼠标左右键当你修改的CAPTUREPARAMS,应该发送WM_CAP_SET_SEQUENCE_SETUP消息来进行更新(或者使用 capCaptureSetSetup). 缺省的按键是VK_ESCAPE. 你必须在指定按键之前使用RegisterHotKey函数鼠标缺省的值是fAbortLeftMousefAbortRightMouse都为TRUE. 


3.捕获的时间限制 

CAPTUREPARAMS结构中的fLimitEnabled指示是否有时间限度, wTimeLimit指示最大的持续时间单位为秒

得 到fLimitEnabledwTimeLimit的值可以发送WM_CAP_GET_SEQUENCE_SETUP消息(或使用 capCatureGetSetup), 当设置了这些成员变量后应该发送消息WM_CAP_SET_SEQUENCE_SETUP消息(capCaptureSetSetup)来更新CAPTUREPARAMS结 构


.关于捕获窗口 

在捕获之前必须创建一个捕获窗口(capture window),在发送消息或使用宏的过程中都需要使用此窗口。 

.创建一个AVICap捕获窗口 

你可以使用capCreateCaptureWindow函数来创建一个AVICap捕获窗口此函数将会返回一个句柄此句柄以后在发送消息时要用

你可以在一个程序里创建一个或多个捕获窗口然后给每一个窗口连接不同的捕获设置


2.将一个捕获窗口连接至捕获设备 

你可以动态的在一个捕获窗口与一个捕获设备之前连接或断接你可以发送WM_CAP_DRIVER_CONNECT消息来使一个捕获窗口与一个捕获设备连接或关当连接上以后你就可以通过捕获窗口向捕获设备发送各种消息

如果你的系统里装有多个捕获设备你可以在发送WM_CAP_DRIVER_CONNECT消息时用wParam参数指定使用哪一个参数是登记在SYSTEM.INI文件的[drivers]一节里的列表中的某一项, 0为第一个

你可以使用capGetDriverDescription函数来得到已安装的捕获设备的名称及版本这样你的程序就可以列举所有已安装的捕获设备和驱这样用户就可以选择其中的一个来与你的捕获窗口连接

你可以发送WM_CAP_DRIVER_GET_NAME消息(capDriverGetName)来得到连接到捕获窗口的捕获设备的名称得到版本发送WM_CAP_DRIVER_GET_VERSION消息(capDriverGetVersion

你可以发送WM_CAP_DRIVER_DISCONNECT消息(capDriverDisconnect)来断接


3. 父窗口与子窗口的交互 

一些象WM_PALETTECHANGEDWM_QUERYNEWPALETTE的系统级消息只能发送到顶级窗口或OVERLAPPED窗口如果一个捕获窗口是子窗口,就必须通过父窗口转送

同样的如果父窗口的尺寸改变了它就需要通知捕获窗口相反地如果捕获窗口的尺寸改变了捕获窗口就需要发送消息给父窗口一个简单的方法就是始终保持捕获窗口的尺寸与视频流的尺寸一致并随时将尺寸的改变通知父窗口


4.捕获窗口的状态 

你可以发送WM_CAP_GET_STATUS消息(capGetStatus)来得到当前捕获窗口的状态得到的是一个CAPSTATUS结构的拷贝它包含图片的尺寸卷轴的当前位置, overlaypreview是否已设置

因为CAPSTATUS信息是动态的你的程序应该只要捕获的视频流的尺寸或格式可能发生了改变就应该进行刷新(例如显示了捕获设备的视频格式以后). 

改变捕获窗口的尺寸并不影响实际的捕获的视频流的尺寸视频捕获设备的格式对话框捕获频流的尺寸


.视频捕获驱动和音频驱动 

1.视频捕获驱动的性能

你可以通过发送WM_CAP_DRIVER_GET_CAPS消息(或者capDriverGetCaps)来得到当前连接的视频驱动的硬件性能得到的信息保存在CAPDRIVERCAPS结构中


2.视频对话框

每一个视频驱动能够提供四个对话框来控制视频捕获和数字化处理定义压缩品质等这些对话框都定义在视频捕获驱动中

Video Source对话框用于控制选择视频来源此对话框列举了此视频捕获卡连接的所有视频源(典型的例如:SVHS和合成输入), 并提供了改变色调对比度饱和度如果视频驱动支持此对话框你就可以显示并更新它使用WM_CAP_DLG_VIDEOSOURCE消息(capDlgVideoSource). 

Video Format对话框定义视频帧的尺寸以及精度视频捕获卡的压缩设置如果卡支持的话可以发送消息WM_CAP_DLG_VIDEOFORMAT消息或(capDlgVideoFormat). 

Video Display对话框控制在视频捕获期间在显示器上的显示此控制不会影响视频数字数据但是他们可能会影响数字信号的表现形式例如如果捕获设备支持overlay, 可能允许改变色调和饱和度关键色彩 或者overlay队列如果卡支持你可以发送WM_CAP_DLG_VIDEODISPLAY消息(或者使用capDlgVideoDisplay). 

Video Compression对话框控制压缩品质如果卡支持发送消息WM_CAP_DLG_VIDEOCOMPRESSION(capDlgVideoCompression). 


3.Preview  Overlay模式

一个视频捕获驱动对进入的视频流有两种工作模式: Preview模式和overlay模式如果一个捕获驱动能够执行两种方法用户可以在其中选择一种

Preview模式把从捕获硬件传来的数据送入系统内存并使用图形设备介面(GDI)将数字化帧显示在捕获窗口内应用程序可以在父窗口失去焦点时减缓显示速度当重新又得到焦点后加快显示速度此种模式要占用大量CPU时间

有三种消息控制Preview操作

WM_CAP_SET_PREIVEW消息(capPreview)允许或禁止preview模式 

WM_CAP_SET_PREVIEWRATE(capPreviewRate)当帧在preview模式显示时设置速度

WM_CAP_SET_SCALE(capPreviewScale)允许或禁止preview视频的缩放比例

previewscaling同时使用捕获的视频帧将会根据捕获窗口的尺寸自动缩放允许preview模式会自动关闭overlay模式

overlay模式是一个硬件函数它将数据送入捕获缓冲区中因而不占用CPU资源. 你可以发送消息WM_CAP_SET_OVERLAY(capOverlay)给捕获窗口来启用或终止overlay模式允许overlay模式会自动禁止preview模式

你同时也可以在preview模式或overlay模式里发送WM_CAP_SET_SCROLL消息(capSetScrollPos)来设置视频帧的客户区卷轴位置


4.视频格式 

你 可以通过发送WM_CAP_GET_VIDEOFORMAT消息(capGetVideoFormatcapGetVideoFormatSize )来得到视频格式的结构或结构的尺寸你可以通过发送CAP_SET_VIDEOFORMAT消息(capSetVideoFormat)来设置视频格式


5.视频捕获设置 

CAPTUREPARMS结构包含了对视频捕获流的控制参数你可以完成以下这些任务

指定帧数 

指定分配多少视频缓冲 

允许或禁止声频捕获 

指定捕获的时间间隔 

指定在捕获的过程中是否使用MCI设置(VCR或者videodisc) 

指定终止流的键盘或鼠标 

specify the type of video averaging applied during capture. 


得到:WM_CAP_GET_SEQUENCE_SETUP消息(capCaptureGetSetup

设置:WM_CAP_SET_SEQUENCE_SETUP消息(capCaptureSetSetup


6.声频格式 

你 可以通过发送WM_CAP_GET_AUDIOFORMAT消息(或capGetAudioFormat宏和capGetAudioFormatSize 宏)来得到当前捕获音频数据的格式或尺寸格式。缺省的声频格式是:单声道、8位、11kHz PCM。 当你使用WM_CAP_GET_AUDIOFORMAT时,总是使用WAVEFORMATEX结构。 

设置发送消息WM_CAP_SET_AUDIOFORMAT消息(或capSetAudioFormat宏),可以传送WAVEFORMATWAVEFORMATEXPCMWAVEFORMAT结构指针。 

五.使用视频捕获 

1.创建捕获窗口(Creating a Capture Window) 

hWndC = capCreateCaptureWindow ( 

(LPSTR) "My Capture Window", // window name if pop-up 

WS_CHILD | WS_VISIBLE, // window style 

0, 0, 160, 120, // window position and dimensions 

(HWND) hwndParent, 

(int) nID /* child ID */); 


2.
连接到捕获驱动(Connecting to a Capture Driver) 

下面的例子是将MSVIDEO驱动连接到句柄为hWndC的捕获窗口然后调用capDriverDisconnect宏来断接

fOK = SendMessage (hWndC, WM_CAP_DRIVER_CONNECT, 0, 0L); 

// 

// Or, use the macro to connect to the MSVIDEO driver: 

// fOK = capDriverConnect(hWndC, 0); 

// 

// Place code to set up and capture video here. 

// 

capDriverDisconnect (hWndC); 


3.
列举所有已安装的捕获驱动(Enumerating Installed Capture Drivers) 

下面的例子使用capGetDriverDescription函数得到已安装的捕获驱动的名称及版本

char szDeviceName[80]; 

char szDeviceVersion[80]; 


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

if (capGetDriverDescription (wIndex, szDeviceName, 

sizeof (szDeviceName), szDeviceVersion, 

sizeof (szDeviceVersion)) 

// Append name to list of installed capture drivers 

// and then let the user select a driver to use. 


4.
得到捕获驱动的性能(Obtaining the Capabilities of a Capture Driver) 

发 送WM_CAP_DRIVER_GET_CAPS消息可以得到捕获驱动的性能,并保存入一个CAPDRIVERCAPS结构.每当程序连接一个新的捕获驱 动到一个捕获窗口时就应该更新CAPDRIVERCAPS结构下面的程序举例说明了如何使用capDriverGetCaps宏来得到捕获驱动的性能


CAPDRIVERCAPS CapDrvCaps; 

SendMessage (hWndC, WM_CAP_DRIVER_GET_CAPS, 

sizeof (CAPDRIVERCAPS), (LONG) (LPVOID) &CapDrvCaps); 

// Or, use the macro to retrieve the driver capabilities. 

// capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS)); 


5.
得到捕获窗口的状态(Obtaining the Status of a Capture Window) 

下面的例子使用SetWindowPos函数使捕获窗口与进来的视频流尺寸保持一致视频流的基本信息是使用capGetStatus宏得到的保存在CAPSTATUS结构中


CAPSTATUS CapStatus; 

capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS)); 

SetWindowPos(hWndC, NULL, 0, 0, CapStatus.uiImageWidth, 

CapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE); 


6.
显示对话框设置视频特征(Displaying Dialog Boxes to Set Video Characteristics) 

每个视频捕获卡一般能提供三个不同的对话框用于控制视频捕获及数字化处理下面的例子说明如何显示这些对话框在显示这些对话框之前,使用了capDriverGetCaps宏来检查CAPDRIVERCAPS结构以检测该卡是否有显示这些对话框


CAPDRIVERCAPS CapDrvCaps; 

capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS)); 


// Video source dialog box. 

if (CapDriverCaps.fHasDlgVideoSource) 

capDlgVideoSource(hWndC); 


// Video format dialog box. 

if (CapDriverCaps.fHasDlgVideoFormat) 

capDlgVideoFormat(hWndC); 

// Are there new image dimensions? 

capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS)); 

// If so, notify the parent of a size change. 


// Video display dialog box. 

if (CapDriverCaps.fHasDlgVideoDisplay) 

capDlgVideoDisplay(hWndC); 


7.
得到和设置视频格式(Obtaining and Setting the Video Format) 

BITMAPINFO 结构的长度既适应于标准的也适应于压缩的数据格式所有程序必须总是询问此结构的尺寸以便在得到当前的视频格式之前分配内存下面的例子就是使用capGetVideoFormatSize宏来得到缓冲区尺寸并调用capGetVideoFormat宏来得到当前的视频格式


LPBITMAPINFO lpbi; 

DWORD dwSize; 

dwSize = capGetVideoFormatSize(hWndC); 

lpbi = GlobalAllocPtr (GHND, dwSize); 

capGetVideoFormat(hWndC, lpbi, dwSize); 


// Access the video format and then free the allocated memory. 


程序可以使用capSetVideoFormat(WM_CAP_SET_VIDEOFORMAT消息)发送一个BITMAPINFO头结构给捕获窗口, 因为视频格式是设备细节你的程序应该检查返回值以便确定此格式是否已被接受


8. 
预览视频(Previewing Video) 

下面的例子使用capPreviewRate宏来设置每66毫秒显示一帧并使用capPreview宏将它放置在捕获窗口里


capPreviewRate(hWndC, 66); // rate, in milliseconds 

capPreview(hWndC, TRUE); // starts preview 

// Preview 

capPreview(hWnd, FALSE); // disables preview 


9.
将视频设置为overlay模式(Enabling Video Overlay) 

下面的例子: capDriverGetCaps宏确定此捕获卡是否有overlay功能如果有就使用宏来设置它 


CAPDRIVERCAPS CapDrvCaps; 

capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS)); 


if (CapDrvCaps.fHasOverlay) 

capOverlay(hWndC, TRUE); 


10.
命名捕获文件(Naming the Capture File) 

下面的例子使用capFileSetCaptureFile宏来指定预备文件名为:MYCAP.AVI, capFileAlloc宏预先指定它的大小为5M


char szCaptureFile[] = "MYCAP.AVI"; 

capFileSetCaptureFile( hWndC, szCaptureFile); 

capFileAlloc( hWndC, (1024L * 1024L * 5)); 


11.
格式化声频捕获(Formatting Audio Capture) 

下面的例子使用capSetAudioFormat来设置声频格式为:11kHz, PCM 8立体声 


WAVEFORMATEX wfex; 

wfex.wFormatTag = WAVE_FORMAT_PCM; 

wfex.nChannels = 2; // Use stereo 

wfex.nSamplesPerSec = 11025; 

wfex.nAvgBytesPerSec = 22050; 

wfex.nBlockAlign = 2; 

wfex.wBitsPerSample = 8; 

wfex.cbSize = 0; 


capSetAudioFormat(hWndC, &wfex, sizeof(WAVEFORMATEX)); 


12.
改变视频捕获设置(Changing a Video Capture Setting) 

下面的例子使用capCaptureGetSetupcapCaptureSetSetup宏得将捕获帧数从缺省的15帧改成每秒10


CAPTUREPARMS CaptureParms; 

float FramesPerSec = 10.0; 


capCaptureGetSetup(hWndC, &CaptureParms, sizeof(CAPTUREPARMS)); 


CaptureParms.dwRequestMicroSecPerFrame = (DWORD) (1.0e6 /FramesPerSec); 

capCaptureSetSetup(hWndC, &CaptureParms, sizeof (CAPTUREPARMS)); 


13.
捕获数据(Capturing Data) 

下面的例子使用capCaptureSequence宏来开始捕获视频并使用capFileSaveAs宏来将捕获的数据拷贝至NEWFILE.AVI文件中


char szNewName[] = "NEWFILE.AVI"; 

// Set up the capture operation. 

capCaptureSequence(hWndC); 

// Capture. 

capFileSaveAs(hWndC, szNewName); 


14.
增加一个信息块(Adding an Information Chunk) 

如 果你需要在你的程序捕获的声频和视频数据中加入你的其他信息你可以创建一个信息块并将它们插入捕获文件中信息块可以包含一些典型的信息例如:版权信息,视频来源外部定位信息等下面的例子使用capFileSetInfoChunk宏来插入一个信息块里面包含了一个SMPTE的时间代码


// This example assumes the application controls 

// the video source for preroll and postroll. 

CAPINFOCHUNK cic; 

// . 

// . 

// . 

cic.fccInfoID = infotypeSMPTE_TIME; 

cic.lpData = "00:20:30:12"; 

cic.cbData = strlen (cic.lpData) + 1; 

capFileSetInfoChunk (hwndC, &cic); 


15.
程序中加入一个回调函数(Adding Callback Functions to an Application) 

一个程序可以为捕获窗口登记一个回调函数以便在以下的这些情况下通知程序


状态改变 

错误发生 

视频框架和声频缓冲区变得可用 

程序应用在捕获视频流的过程中接收 


下面的例子创建一个捕获窗口并登记状态,错误,视频流和框架回调函数在消息处理对列中也包括了一个终止回调函数的说明


case WM_CREATE: 

char achDeviceName[80] 

char achDeviceVersion[100] 

char achBuffer[100] 

WORD wDriverCount = 0 

WORD wIndex 

WORD wError 

HMENU hMenu 


// Create a capture window using the capCreateCaptureWindow macro. 

ghWndCap = capCreateCaptureWindow((LPSTR)"Capture Window", 

WS_CHILD | WS_VISIBLE, 0, 0, 160, 120, (HWND) hWnd, (int) 0); 


// Register the error callback function using the 

// capSetCallbackOnError macro. 

capSetCallbackOnError(ghWndCap, fpErrorCallback); 


// Register the status callback function using the 

// capSetCallbackOnStatus macro. 

capSetCallbackOnStatus(ghWndCap, fpStatusCallback); 


// Register the video-stream callback function using the 

// capSetCallbackOnVideoStream macro. 

capSetCallbackOnVideoStream(ghWndCap, fpVideoCallback); 


// Register the frame callback function using the 

// capSetCallbackOnFrame macro. 

capSetCallbackOnFrame(ghWndCap, fpFrameCallback); 


// Connect to a capture driver 


break; 

case WM_CLOSE: 

// Use the capSetCallbackOnFrame macro to 

// disable the frame callback. Similar calls exist for the other 

// callback functions. 


capSetCallbackOnFrame(hWndC, NULL); 


break; 


16.
创建一个状态回调函数(Creating a Status Callback Function) 

下面的例子是创建一个简单的状态回调函数,登记此回调函数使用capSetCallbackOnStatus


// StatusCallbackProc: status callback function 

// hWnd: capture window handle 

// nID: status code for the current status 

// lpStatusText: status text string for the current status 

// 

LRESULT PASCAL StatusCallbackProc(HWND hWnd, int nID, 

LPSTR lpStatusText) 

if (!ghWndMain) 

return FALSE; 


if (nID == 0) { // Clear old status messages. 

SetWindowText(ghWndMain, (LPSTR) gachAppName); 

return (LRESULT) TRUE; 

// Show the status ID and status text... 

wsprintf(gachBuffer, "Status# %d: %s", nID, lpStatusText); 


SetWindowText(ghWndMain, (LPSTR)gachBuffer); 

return (LRESULT) TRUE; 


17.
创建一个错误回调函数( Creating an Error Callback Function) 

下面的例子是创建一个简单的错误回调函数,登记此回调函数使用capsetCallbackOnError


// ErrorCallbackProc: error callback function 

// hWnd: capture window handle 

// nErrID: error code for the encountered error 

// lpErrorText: error text string for the encountered error 

// 

LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID, 

LPSTR lpErrorText) 

if (!ghWndMain) 

return FALSE; 


if (nErrID == 0) // Starting a new major function. 

return TRUE; // Clear out old errors. 


// Show the error identifier and text. 

wsprintf(gachBuffer, "Error# %d", nErrID); 


MessageBox(hWnd, lpErrorText, gachBuffer, 

MB_OK | MB_ICONEXCLAMATION); 


return (LRESULT) TRUE; 


18.
创建一个框架回调函数(Creating a Frame Callback Function) 

登记此回调函数使用capSetCallbackOnFrame


// FrameCallbackProc: frame callback function 

// hWnd: capture window handle 

// lpVHdr: pointer to struct containing captured 

// frame information 

// 

LRESULT PASCAL FrameCallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr) 

if (!ghWndMain) 

return FALSE; 


wsprintf(gachBuffer, "Preview frame# %ld ", gdwFrameNum++); 

SetWindowText(ghWndMain, (LPSTR)gachBuffer); 

return (LRESULT) TRUE 



 

记录生活、工作、学习点滴!
E-Mail:mahaisong@hotmail.com 欢迎大家讨论。
沐海博客园,我有一颗,卓越的心!