在桌面視頻會議、可視電話等多媒體應用中,獲得數字視頻是一個關鍵的前提。在VideoforWindows(VFW)出現之前,捕獲數字視頻是一項極其復雜的工作。Microsoft的VisualC++自從4.0版就開始支持VideoforWindows(簡稱VFW),這給視頻捕獲編程帶來了很大的方便。關於多媒體應用開發,市面流行資料中介紹較多的是MCI(媒體控制接口),而本文著重介紹的是如何使用VisualC++提供的AVICap窗口類進行視頻捕獲以及其中涉及到的概念和關鍵問題。
一、VideoforWindows簡介
VFW是Microsoft1992年推出的關於數字視頻的一個軟件包,它能使應用程序數字化並播放從傳統模擬視頻源得到的視頻剪輯。VFW的一個關鍵思想是播放時不需要專用硬件,為了解決數字視頻數據量大的問題,需要對數據進行壓縮。它引進了一種叫AVI的文件標准,該標准未規定如何對視頻進行捕獲、壓縮及播放,僅規定視頻和音頻該如何存儲在硬盤上,在AVI文件中交替存儲視頻幀和與之相匹配的音頻數據。
VFW給程序員提供.VBX和AVICap窗口類的高級編程工具,使程序員能通過發送消息或
設置屬性來捕獲、播放和編輯視頻剪輯。現在用戶不必專門安裝VFW了,Windows95本身包括了VideoforWindows1.1,當用戶在安裝Windows時,安裝程序會自動地安裝配置視頻所需的組件,如設備驅動程序、視頻壓縮程序等。
VFW主要由以下六個模塊組成:
(1)AVICAP.DLL:包含了執行視頻捕獲的函數,它給AVI文件I/O和視頻、音頻
設備驅動程序提供一個高級接口;
(2)MSVIDEO.DLL:用一套特殊的DrawDib函數來處理屏幕上的視頻操作;
(3)MCIAVI.DRV:此驅動程序包括對VFW的MCI命令的解釋器;
(4)AVIFILE.DLL:支持由標准多媒體I/O(mmio)函數提供的更高的命令來訪問.AVI文件;
(5)壓縮管理器(ICM):管理用於視頻壓縮-解壓縮的編解碼器(CODEC);
(6)音頻壓縮管理器ACM:提供與ICM相似的服務,不同的是它適於波形音頻。
VisualC++在支持VFW方面提供有vfw32.lib、msacm32.lib、winmm.lib等類似的庫。特別是它提供了功能強大、簡單易行、類似於MCIWnd的窗口類AVICapAVICap
為應用程序提供了一個簡單的、基於消息的接口,使之能訪問視頻和波形音頻硬件,並能在將視頻流捕獲到硬盤上的過程中進行控制。
二、AVICap編程簡介
AVICap支持實時的視頻流捕獲和單幀捕獲並提供對視頻源的控制。雖然MCI也提供數字視頻服務,比如它為顯示.AVI文件的視頻提供了avivideo命令集,為視頻疊加提供了overlay命令集,但這些命令主要是基於文件的操作,它不能滿足實時地直接從視頻緩存中取數據的要求,對於使用沒有視頻疊加能力的捕獲卡的PC機來說,用MCI提供的命令集是無法捕獲視頻流的。而AVICap在捕獲視頻方面具有一定的優勢,它能直接訪問視頻緩沖區,不需要生成中間文件,實時性很強,效率很高。同時,它也可將數字視頻捕獲到文件。
在視頻捕獲之前需要創建一個捕獲窗,所有的捕獲操作及其設置都以它為基礎。
用AVICap窗口類創建的窗口(通過capCreateCaptureWindow函數創建)被稱為“捕獲窗”,其窗口風格一般為WS_CHILD和WS_VISIBLE。在概念上,捕獲窗類似於標准控制(如按鈕、列表框等)。捕獲窗具有下列功能:
(1)將一視頻流和音頻流捕獲到一個AVI文件中;
(2)動態地同視頻和音頻輸入器件連接或斷開;
(3)以Overlay或Preview模式對輸入的視頻流進行實時顯示;
(4)在捕獲時可指定所用的文件名並能將捕獲文件的內容拷貝到另一個文件;
(5)設置捕獲速率;
(6)顯示控制視頻源、視頻格式、視頻壓縮的對話框;
(7)創建、保存或載入調色板;
(8)將圖像和相關的調色板拷貝到剪貼板;
(9)將捕獲的一個單幀圖像保存為DIB格式的文件。
這裡需要解釋一下AVICap在顯示視頻時提供的兩種模式:
(A)預覽(Preview)模式:該模式使用CPU資源,視頻幀先從捕獲硬件傳到系統內存,接著采用GDI函數在捕獲窗中顯示。在物理上,這種模式需要通過VGA卡
在監視器上顯示。
(B)疊加(Overlay)模式:該模式使用硬件疊加進行視頻顯示,疊加視頻的顯示不經過VGA卡,疊加視頻的硬件將VGA的輸出信號與其自身的輸出信號合並,形
成組合信號顯示在計算機的監視器上。只有部分視頻捕獲卡才具有視頻疊加能力。
除了利用捕獲窗的九個功能外,靈活編寫AVICap提供的回調函數還可滿足一些特殊需求,比如將宏capCaptureSequenceNoFile同用capSetCallbackOnVideoStream
登記的回調函數一起使用可使應用程序直接使用視頻和音頻數據,在視頻會議的應用程序中可利用這一點來獲得視頻幀,回調函數將捕獲的圖像傳到遠端的計算機。應用程序可用捕獲窗來登記回調函數(由用戶編寫,而由系統調用),以便在發生下列情況時它能通知應用程序作出相應的反應:
(1)捕獲窗狀態改變;
(2)出錯;
(3)視頻幀和音頻緩存可以使用;
(4)在捕獲過程中,其它應用程序處於讓步(Yield)地位。
與普通SDK編程一樣,視頻捕獲編程也要用到涉及視頻捕獲的結構、宏、消息和函數。讓編程人員感到輕松的是,發送AVICap窗口消息所能完成的功能都能調用相應的宏來完成。例如,SendMessage(hWndCap,WM_CAP_DRIVER_CONNECT,0,0L)與capDriverConnect(hWndCap,0)的作用相同,都是將創建的捕獲窗同視頻輸入器件連接起來。
在利用AVICap編程時,應該熟悉與視頻捕獲相關的結構,下面對常用的四個結構作一簡要介紹,對於前三個結構都有對應的函數來設置和獲得結構包含的信息:
(1)CAPSTATUS:定義了捕獲窗口的當前狀態,如圖像的寬、高等;
(2)CAPDRIVERCAPS:定義了捕獲驅動器的能力,如有無視頻疊加能力、有無控制視頻源、視頻格式的對話框等;
(3)CAPTUREPARMS:包含控制視頻流捕獲過程的參數,如捕獲幀頻、指定鍵盤或鼠標鍵以終止捕獲、捕獲時間限制等;
(4)VIDEOHDR:定義了視頻數據塊的頭信息,在編寫回調函數時常用到其數據成員lpData(指向數據緩存的指針)和dwBufferLength(數據緩存的大小)。
三、AVICap編程示例
下面以一個簡單的應用程序為例說明AVICap的使用,該程序對輸入的視頻流進行實時的顯示和捕獲,演示需要一個視頻捕獲卡和攝像頭。界面中的菜單項如圖1所示。其中,菜單項Display可以以Preview或Overlay模式顯示圖像;菜單項Setting可通過彈出AVICap提供的對話框VideoSource、VideoFormat和VideoDisplay來對捕獲進行設置,圖4中的圖像就是按照圖2、圖3的對話框所示進行設置、以Preview模式顯示的結果;菜單項Capture可將視頻流或單幀圖像捕獲到指定的文件中去。
由於篇幅有限,下面僅介紹與視頻捕獲相關的編程。
1、定義全局變量:
HWNDghWndCap;//捕獲窗的句柄
CAPDRIVERCAPSgCapDriverCaps;//視頻驅動器的能力
CAPSTATUSgCapStatus;//捕獲窗的狀態
2、處理WM_CREATE消息:
//創建捕獲窗,其中hWnd為主窗口句柄
ghWndCap=capCreateCaptureWindow((LPSTR)"CaptureWindow",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);//Videosource對話框
if(gapDriverCaps.fHasDlgVideoFormat)
capDlgVideoFormat(ghWndCap);//Videoformat對話框
if(CapDriverCaps.fHasDlgVideoDisplay)
capDlgVideoDisplay(ghWndCap);//Videodisplay對話框
8、處理VideoStream菜單項,它捕獲視頻流到一個.AVI文件:
charszCaptureFile[]="MYCAP.AVI";
capFileSetCaptureFile(ghWndCap,szCaptureFile);//指定捕獲文件名
capFileAlloc(ghWndCap,(1024L*1024L*5));//為捕獲文件分配存儲空間
capCaptureSequence(ghWndCap);//開始捕獲視頻序列
9、處理SingleFrame菜單項:
capGrabFrame(ghWndCap);//捕獲單幀圖像
10、定義三個回調函數:
LRESULTCALLBACKStatusCallbackProc(HWNDhWnd,intnID,LPSTRlpStatusText)
{
if(!ghWndCap)returnFALSE;
//獲得捕獲窗的狀態
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;
}
LRESULTCALLBACKErrorCallbackProc(HWNDhWnd,intnErrID,LPSTRlpErrorText)
{
if(!ghWndCap)
returnFALSE;
if(nErrID==0)
returnTRUE;//清除舊的錯誤
wsprintf(gachBuffer,"Error#%d",nErrID);//顯示錯誤標識和文本
MessageBox(hWnd,lpErrorText,gachBuffer,MB_OK|MB_ICONEXCLAMATION);
return(LRESULT)TRUE;
}
LRESULTCALLBACKFrameCallbackProc(HWNDhWnd,LPVIDEOHDRlpVHdr)
{
if(!ghWndCap)
returnFALSE;
//假設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過濾掉一些消息,例如鼠標消息。
四、結束語
VisualC++提供的AVICap窗口類為捕獲數字視頻流及其相關操作提供了很大的方便,靈活編寫其中的回調函數可滿足實時視頻傳輸的需要,例如應用程序可直接從緩沖中取得數字視頻並對其進行壓縮編碼後實時地傳到遠端的計算機。筆者所從事的電話網上的可視電話系統就是采用AVICap進行視頻捕獲的,這種方法同樣可用於其它多媒體會議系統中,如ISDN、局域網上的會議系統等。