在 Video for Windows (VFW) 推出前,數位視訊捕獲是極其複雜的系統級工程。VFW 1.1 隨 Windows 95 內建,提供統一的 AVI 檔案格式、壓縮管理器 (ICM)、音訊壓縮管理器 (ACM) 及設備驅動介面。Visual C++ 4.0 起內建支援 VFW,並提供 AVICap 窗口類——一個輕量級、訊息驅動的高階封裝,讓開發者無需處理複雜的驅動程式協定,即可實現視訊串流捕獲、預覽、壓縮存檔與單幀抓取。本文深入解析 AVICap 程式設計模型、核心結構、回呼機制與完整實戰範例。
VFW 六大核心模組:
DrawDib 系列函數,處理螢幕視訊繪製。avivideo、overlay 指令集。mmio 之上,提供 AVI 檔案高階存取。VC++ 專案需連結 vfw32.lib、msacm32.lib、winmm.lib。
透過 capCreateCaptureWindow 建立(風格 WS_CHILD | WS_VISIBLE),概念上類似標準控制項。九大核心功能:
| 模式 | 原理 | 硬體需求 | CPU 佔用 |
|---|---|---|---|
| Preview | 幀資料經系統記憶體 → GDI 繪製於捕獲窗 → VGA 輸出 | 通用顯示卡即可 | 較高(需 CPU 搬移像素) |
| Overlay | 捕獲卡硬體將視訊訊號直接與 VGA 輸出合成 | 需支援硬體疊加的捕獲卡 | 極低(零拷貝顯示) |
AVICap 允許登記四類回呼,滿足即時串流處理、視訊會議等進階需求:
capSetCallbackOnError:錯誤通知capSetCallbackOnStatus:狀態變更(如幀率、解析度)capSetCallbackOnFrame:單幀資料就緒(VIDEOHDR*)capSetCallbackOnYield:捕獲過程中讓出 CPU 給其他應用搭配 capCaptureSequenceNoFile + capSetCallbackOnVideoStream 可直接在回呼中獲取壓縮後串流資料,適用於即時傳輸場景。
| 結構體 | 用途 | 關鍵欄位 |
|---|---|---|
CAPSTATUS | 捕獲窗口當前狀態 | uiImageWidth, uiImageHeight, fLiveWindow, fOverlayWindow |
CAPDRIVERCAPS | 驅動程式能力指標 | fHasOverlay, fHasDlgVideoSource, fHasDlgVideoFormat, fHasDlgVideoDisplay, wDeviceIndex |
CAPTUREPARMS | 捕獲參數控制 | dwRequestMicroSecPerFrame (幀率), fYield, wTimeLimit, vKeyAbort |
VIDEOHDR | 視訊資料區塊標頭 | lpData (資料指標), dwBufferLength, dwBytesRecorded, dwFlags |
前三者皆有對應 Get/Set 宏/函數操作。
所有操作均透過 SendMessage(hWndCap, WM_CAP_..., ...) 完成,VC++ 提供同名宏簡化呼叫:
SendMessage(hWndCap, WM_CAP_DRIVER_CONNECT, 0, 0L) ≡ capDriverConnect(hWndCap, 0)
SendMessage(hWndCap, WM_CAP_SET_PREVIEW, TRUE, 0L) ≡ capPreview(hWndCap, TRUE)
常用宏分類:
capDriverConnect/Disconnect, capDriverGetCapscapPreview/PreviewRate, capOverlaycapFileSetCaptureFile, capFileAlloc, capCaptureSequence, capCaptureSingleFrameOpen/Close, capGrabFrame, capGrabFrameNoStopcapDlgVideoSource/Format/DisplaycapSetCallbackOnError/Status/Frame/VideoStream/YieldcapPaletteOpen/Save/PasteHWND g_hWndCap; // 捕獲窗口句柄
CAPDRIVERCAPS g_Caps; // 驅動能力
CAPSTATUS g_Status; // 窗口狀態
WM_CREATE:建立與初始化g_hWndCap = capCreateCaptureWindow("CaptureWindow", WS_CHILD|WS_VISIBLE,
0, 0, 320, 240, hWnd, 0);
// 登記回呼
capSetCallbackOnError(g_hWndCap, (FARPROC)ErrorCallback);
capSetCallbackOnStatus(g_hWndCap, (FARPROC)StatusCallback);
capSetCallbackOnFrame(g_hWndCap, (FARPROC)FrameCallback);
// 連接驅動程式(索引 0 為第一張卡)
capDriverConnect(g_hWndCap, 0);
capDriverGetCaps(g_hWndCap, &g_Caps, sizeof(CAPDRIVERCAPS));
WM_CLOSE:資源釋放capSetCallbackOnStatus(g_hWndCap, NULL);
capSetCallbackOnError(g_hWndCap, NULL);
capSetCallbackOnFrame(g_hWndCap, NULL);
capCaptureAbort(g_hWndCap);
capDriverDisconnect(g_hWndCap);
// 預覽模式
case IDM_PREVIEW:
capPreviewRate(g_hWndCap, 66); // ~15 fps
capPreview(g_hWndCap, TRUE);
break;
// 疊加模式(需硬體支援)
case IDM_OVERLAY:
if (g_Caps.fHasOverlay) capOverlay(g_hWndCap, TRUE);
break;
// 視訊來源/格式/顯示對話框
case IDM_VIDEOSOURCE:
if (g_Caps.fHasDlgVideoSource) capDlgVideoSource(g_hWndCap);
break;
case IDM_VIDEOFORMAT:
if (g_Caps.fHasDlgVideoFormat) capDlgVideoFormat(g_hWndCap);
break;
case IDM_VIDEODISPLAY:
if (g_Caps.fHasDlgVideoDisplay) capDlgVideoDisplay(g_hWndCap);
break;
// 捕獲視訊串流至 AVI
case IDM_CAPTURE_VIDEO:
capFileSetCaptureFile(g_hWndCap, "capture.avi");
capFileAlloc(g_hWndCap, 5*1024*1024); // 預分配 5MB
capCaptureSequence(g_hWndCap);
break;
// 單幀捕獲
case IDM_CAPTURE_FRAME:
capGrabFrame(g_hWndCap); // 觸發 FrameCallback
break;
LRESULT CALLBACK StatusCallback(HWND hWnd, int nID, LPSTR lpText) {
if (!g_hWndCap) return FALSE;
capGetStatus(g_hWndCap, &g_Status, sizeof(CAPSTATUS));
// 自動調整捕獲窗大小匹配視訊解析度
SetWindowPos(g_hWndCap, NULL, 0, 0, g_Status.uiImageWidth, g_Status.uiImageHeight,
SWP_NOZORDER|SWP_NOMOVE);
if (nID == 0) { SetWindowText(g_hWndCap, g_szAppName); return TRUE; }
char buf[256]; wsprintf(buf, "Status #%d: %s", nID, lpText);
SetWindowText(g_hWndCap, buf);
return TRUE;
}
LRESULT CALLBACK ErrorCallback(HWND hWnd, int nErrID, LPSTR lpText) {
if (!g_hWndCap) return FALSE;
if (nErrID == 0) return TRUE;
char buf[64]; wsprintf(buf, "Error #%d", nErrID);
MessageBox(hWnd, lpText, buf, MB_OK|MB_ICONEXCLAMATION);
return TRUE;
}
LRESULT CALLBACK FrameCallback(HWND hWnd, LPVIDEOHDR lpVHdr) {
if (!g_hWndCap) return FALSE;
// 範例:直接寫入檔案(實際應用可傳送網路、編碼、分析)
fwrite(fp, lpVHdr->lpData, lpVHdr->dwBufferLength, 1);
return TRUE;
}
#include <vfw.h>vfw32.lib預設捕獲期間獨佔 CPU。設定 CAPTUREPARMS.fYield = TRUE 或登記 YieldCallback 可讓出 CPU。回呼中使用 PeekMessage 過濾滑鼠等高頻訊息,防止重入導致堆疊溢位。
AVICap 以簡潔的訊息介面封裝了 VFW 複雜的驅動協定、緩衝管理與壓縮管線,是 Windows 平台視訊捕獲開發的利器。靈活運用回呼機制,可構建視訊會議、遠端監控、工業檢測等即時視訊應用。筆者團隊即以此技術實現電話網可視電話系統,同樣適用於 ISDN、區域網多媒體會議平台。掌握 AVICap,即掌握了 Windows 視訊捕獲的核心鑰匙。