VFW(Video for Windows)是Microsoft推出的關於數位視訊的一個軟體開發包,VFW的核心是AVI檔案標準。AVI(Audio Video Interleave)檔案中的音、視頻資料幀交錯存放。圍繞AVI檔案,VFW推出了一整套完整的視頻採集、壓縮、解壓縮、回放和編輯的應用程式介面(API)。由於AVI檔案格式推出較早且在數位視訊技術中有廣泛的應用,所以VFW仍然有很大的實用價值,而且進一步發展的趨勢。
在VC++開發環境中調用VFW和使用其它開發包沒有什麼不同,只是需要將VFW32.lib檔案加入工程中,但在開放視頻捕捉與壓縮管理程式時需要其它軟體硬體設定。VFW為AVI檔案提供了豐富的處理函數和宏定義,AVI檔案的特點在於它是典型的資料流檔案,它由視頻流、音頻流、文字資料流組成。所以對AVI檔案的處理主要是處理檔案流。
編程執行個體
為了能夠捕獲視訊框架,要啟動一個捕獲幀回呼函數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過濾掉一些訊息,例如滑鼠訊息。
常用問題代碼
Q:利用網上的代碼做了一個視頻採集的小程式,現在需要實現如下功能
已有如下函數:
/*******************************************************************************
Function : EnablePreviewVideo
Arguments : Parent (input) - Parent window that will display video.
x (input) - X Location in parent where video will be shown.
y (input) - Y location in parent where video will be shown.
Width (input) - Width of preview window.
Height (input) - Height of preview window.
PreviewRate (input) - Rate of preview in FPS.
Return : TRUE Success, FALSE Failed.
Description: Enables preview video mode.
*******************************************************************************/
BOOL CVFWImageProcessor::EnablePreviewVideo(HWND Parent, INT x, INT y, INT Width, INT Height, INT PreviewRate)
{
// Reset any error conditions.
GetPreviousError(NULL,NULL,TRUE);
SetParent(m_hWndVideo,Parent);
SetWindowLong(m_hWndVideo,GWL_STYLE,WS_CHILD);
SetWindowPos(m_hWndVideo,NULL,x,y,
Width,
Height,
SWP_NOZORDER);
ShowWindow(m_hWndVideo,SW_SHOW);
capPreviewRate(m_hWndVideo, PreviewRate);
return capPreview(m_hWndVideo,TRUE);
}
現在已經通過擷取預覽視窗CRect rectVideo的大小,但是視頻顯示的還是與視頻格式設定大小一致,只能在視窗內顯示視頻左上方的一部分,沒有實現視頻的縮放,所以顯示不全。我現在就想縮放視頻預覽顯示到設定地區,但實際的視頻大小不要改變,因為抓圖還是要實際大小的。
A:自動縮放視頻到視頻視窗大小:
在你的capPreviewRate(...)之後加上capPreviewScale(hwnd,TRUE)試試
附MSDN:
BOOL capPreviewScale( hwnd, f );
Parameters
hwnd
Handle of a capture window.
f
Preview scaling flag. Specify TRUE for this parameter to stretch preview frames to the size of the capture window or FALSE to display them at their natural size.
Return Values
Returns TRUE if successful or FALSE otherwise.
Remarks
Scaling preview images controls the immediate presentation of captured frames within the capture window. It has no effect on the size of the frames saved to file.
Scaling has no effect when using overlay to display video in the frame buffer.
另:
抓圖時候設定大小:
HWND m_capwnd=capCreateCaptureWindow(.......);
BITMAPINFO m_bmpinfo;
capGetVideoFormat(m_capwnd,&m_bmpinfo,sizeof(m_bmpinfo));
m_bmpinfo.bmiHeader.biWidth和m_bmpinfo.bmiHeader.biHeight就是你想要的視頻寬度和高度
設定大小:
m_bmpinfo.bmiHeader.biWidth=176;//320
m_bmpinfo.bmiHeader.biHeight=144;//240
BOOL ret=capSetVideoFormat(m_capwnd,&m_bmpinfo,sizeof(m_bmpinfo));