//win32 攝像頭捕獲系統vfw
// VideoRecord.h
/// 用于定義一些資源ID
#include "resource.h"
//#define EXIT 104
#define HELP 105
#define MINIMIZE 106
//#define DISPLAY 107
#define BUTTONSIZE 15
#define PHOTO 108
#define RECORDVIDEO 109
// #define RESOURCE 110
// #define FORMAT 111
#define CONNECT 112
#include <Windows.h>
#include <STRING>
#include <vfw.h>
#include "VideoRecord.h"
#pragma comment(lib, "vfw32.lib")
LRESULT WINAPI MainWndProc( HWND, UINT, WPARAM, LPARAM );
LRESULT WINAPI SelCapDrcProc( HWND, UINT, WPARAM, LPARAM );
int EnumCapDrv();
VOID APIENTRY HandlePopupMenu(HWND, POINT); // 右鍵彈出菜單回調函數
WORD WINAPI VideoThreadProc( LPVOID lParam); // 視訊捕捉線程函數
////////////////全局變量
HANDLE ghInstance; // 應用程式執行個體
HWND hwndMain; // 主視窗句柄
HWND hwndVideo; // 視訊捕捉視窗句柄
HWND hwndSelCapDrvDlg; // 選擇捕捉驅動對話框句柄
HWND hwndSelCapDrvLBox; // 選擇驅動對話框列舉驅動名稱清單框句柄
HWND hwndExit;
HWND hwndMin;
HWND hwndHelp;
HWND hwndPhoto;
HWND hwndRecord;
HWND hwndBtnExit;
HWND hwndSource;
HWND hwndFormat;
HWND hwndConnect;
HANDLE hVideoTread; // 視訊采集線程
HRGN hRoundRgn; // 視窗修正區域
CAPDRIVERCAPS capDrvCaps; // 驅動性能
bool bRecordFileOpen = false; // 錄像開始标志
bool bPhotoFileOpen = false; // 照相開始标志
bool bRecording = false; // 判斷是否正在錄像
bool bThreadEnd = false; // 判斷視訊線程是否終止
char recordFile[260]; //保持錄像标志
char photoFile[260]; // 保持照相标志
char gachBuffer[260]; // 儲存錯誤資訊
//////////////////////////////////////////////////////////////////////////////
// ErrorCallbackProc: 錯誤回調函數,過capSetCallbackOnError宏來注冊回調
// hWnd: 捕獲視窗句柄
// nErrID: 錯誤代碼
// lpErrorText: 關于錯誤的文本資訊
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK ErrorCallbackProc(HWND hWnd,int nErrID,LPSTR lpErrorText)
{
if(!hwndMain)
return FALSE;
if(nErrID==0)
return TRUE;//清除舊的錯誤
wsprintf(gachBuffer,"Error# %d",nErrID);//顯示錯誤辨別和文本
MessageBox(hWnd, lpErrorText, gachBuffer,MB_OK | MB_ICONEXCLAMATION);
return (LRESULT) TRUE;
}
/////////////////////////////////////
int PASCAL WinMain( IN HINSTANCE hInstance,
IN HINSTANCE hPrevInstance,
IN LPSTR lpCmdLine,
IN int nShowCmd )
WNDCLASS wndClass;
MSG msg;
char className[] = "VideoClass";
// 如果不是前執行個體
if (!hPrevInstance)
{
wndClass.lpszClassName = className;
wndClass.lpfnWndProc = MainWndProc;
wndClass.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hbrBackground = CreateSolidBrush( RGB(200,200,100) );
wndClass.lpszMenuName = NULL;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
RegisterClass( &wndClass );
}
ghInstance = hInstance;
// 建立主視窗
hwndMain = CreateWindow(className,
"視訊監控系統",
WS_POPUP|WS_OVERLAPPED,
GetSystemMetrics(SM_CXFULLSCREEN)/2 - 600/2,
GetSystemMetrics(SM_CYFULLSCREEN)/2-600/2,
600, 600,
NULL,
hInstance,
NULL);
ShowWindow(hwndMain, nShowCmd);
// 設定主視窗主要區域
SetWindowRgn(hwndMain, hRoundRgn, 1);
while( GetMessage(&msg, NULL, 0, 0))
TranslateMessage(&msg);//該函數将虛拟鍵消息轉換為字元消息。
//字元消息被寄送到調用線程的消息隊列裡,當下一次線程調用函數GetMessage或PeekMessage時被讀出
DispatchMessage(&msg);//該函數排程一個消息給視窗程式。
//通常排程從GetMessage取得的消息。消息被排程到的視窗程式即是MainProc()函數
return msg.wParam;
LRESULT CALLBACK MainWndProc( HWND hMain, UINT msg, WPARAM wParam, LPARAM lParam )
HDC hdc = GetDC(hMain);
RECT rc; // 客戶區
POINT pt; // 滑鼠位置
HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
switch(msg)
case WM_LBUTTONDBLCLK:
SetFocus(hMain);
break;
case WM_RBUTTONDOWN:
GetClientRect(hMain, (LPRECT)&rc);
pt.y = HIWORD(lParam);
pt.x = LOWORD(lParam);
if ( PtInRect(&rc, pt) )
{
HandlePopupMenu(hMain, pt);
}
case WM_PAINT:
RECT helpRect, minRect, exitRect;
HRGN helpRgn, minRgn, exitRgn;
FrameRgn(hdc, hRoundRgn, CreateSolidBrush(RGB(0,0,0)), 2, 2);
BringWindowToTop(hwndSelCapDrvDlg);
return DefWindowProc(hMain, msg, wParam, lParam);
case WM_CREATE:
// 注冊錯誤提示回調函數
capSetCallbackOnError(hwndVideo, (FARPROC)ErrorCallbackProc);
// 主視窗主要圓角矩形區域
hRoundRgn = CreateRoundRectRgn(100, 70, 500, 460, 20, 20);
// 捕捉視訊區域s
hwndVideo = capCreateCaptureWindow((LPSTR)"My Capture Window",
WS_CHILD|WS_VISIBLE,
140, 100, 320, 280,
(HWND)hMain,
(int)1);
hwndExit = CreateWindow("button", // 建立一個按鈕,更多如BUTTON,COMBOBOX,LISTBOX
"x", // button text
WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON|BS_CENTER|BS_VCENTER,
470, 75, BUTTONSIZE, BUTTONSIZE,
hMain,
(HMENU)EXIT,
(HINSTANCE)ghInstance,
(LPVOID)NULL );
hwndMin = CreateWindow("button", // 建立一個按鈕,更多如BUTTON,COMBOBOX,LISTBOX
"-", // button text
450, 75, BUTTONSIZE, BUTTONSIZE,
(HMENU)MINIMIZE,
hwndHelp = CreateWindow("button", // 建立一個按鈕,更多如BUTTON,COMBOBOX,LISTBOX
"?", // button text
WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON/*|BS_CENTER|BS_VCENTER*/,
430, 75, BUTTONSIZE, BUTTONSIZE,
(HMENU)HELP,
hwndPhoto = CreateWindow("button",
"照 相",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
150, 410, 50, 20,
(HMENU)PHOTO,
SendMessage(hwndPhoto, WM_SETFONT, (WPARAM)hFont, 1); // 設定按鈕字型為系統預設字型
hwndRecord = CreateWindow("button",
"錄 像",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_VCENTER|BS_CENTER,
220, 410, 50, 20,
(HMENU)RECORDVIDEO,
SendMessage(hwndRecord, WM_SETFONT, (WPARAM)hFont, 1);// 設定按鈕字型為系統預設字型
hwndBtnExit = CreateWindow("button", // 退出按鈕
"退 出",
400, 410, 50, 20,
SendMessage(hwndBtnExit, WM_SETFONT, (WPARAM)hFont, 1);
// 設定按鈕區域
GetClientRect(hwndHelp, &helpRect);
GetClientRect(hwndMin, &minRect);
GetClientRect(hwndExit, &exitRect);
helpRgn = CreateEllipticRgnIndirect(&helpRect);
minRgn = CreateEllipticRgnIndirect(&minRect);
exitRgn = CreateEllipticRgnIndirect(&exitRect);
SetWindowRgn(hwndExit,exitRgn,1);
SetWindowRgn(hwndMin,minRgn,1);
SetWindowRgn(hwndHelp,helpRgn,1);
// 建立選擇驅動對話框
hwndSelCapDrvDlg = CreateDialog((HINSTANCE)ghInstance,
MAKEINTRESOURCE(DLG_SELCAPDRIVER),
0, (DLGPROC)SelCapDrcProc);
hwndSelCapDrvLBox = GetDlgItem(hwndSelCapDrvDlg, LISTBOX_SELCAPDRIVER);
EnumCapDrv();
case WM_COMMAND:
CAPSTATUS capStatus;
switch(wParam)
case EXIT: // 退出系統
SendMessage(hMain, WM_SYSCOMMAND, SC_CLOSE, 0);
break;
case MINIMIZE: // 最小化
SendMessage(hMain, WM_SYSCOMMAND, SC_MINIMIZE, 0);
case HELP: // 幫助按鈕
SendMessage(hMain, WM_SYSCOMMAND, SC_CONTEXTHELP, 0);
case RESOURCE: // 視訊源選項
if (capDrvCaps.fHasDlgVideoSource)
{
capDlgVideoSource(hwndVideo);
}
case FORMAT: // 視訊格式選項
if (capDrvCaps.fHasDlgVideoFormat)
capDlgVideoFormat(hwndVideo);
case CONNECT:
case DISPLAY: // 視訊顯示選項
if (capDrvCaps.fHasDlgVideoDisplay)
capDlgVideoDisplay(hMain);
capGetStatus(hwndVideo, &capStatus, sizeof(CAPSTATUS));// 得到目前捕獲視窗的狀态
SetWindowPos(hwndVideo, NULL, 0, 0, capStatus.uiImageWidth, capStatus.uiImageHeight, SWP_NOZORDER|SWP_NOMOVE);
case PHOTO: // 抓取圖檔
if ( capFileSaveDIB(hwndVideo, "test.bmp") ){
MessageBox(hMain, "Save Bmp File Succeeded!", "photo save", MB_OK|MB_ICONINFORMATION);
else break;
if (!bPhotoFileOpen)
OPENFILENAME ofnp; // open file name structure
ZeroMemory(&ofnp, sizeof(OPENFILENAME));
ofnp.lStructSize = sizeof(OPENFILENAME);
ofnp.hwndOwner = hMain;
ofnp.lpstrFile = photoFile;
ofnp.lpstrFilter = "*.bmp";
ofnp.nMaxFile = sizeof(photoFile);
ofnp.nFilterIndex = 1;
ofnp.nMaxFileTitle = 0;
ofnp.lpstrInitialDir = NULL;
ofnp.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
// 顯示儲存檔案對話框
if (GetSaveFileName(&ofnp) == TRUE)
{
bPhotoFileOpen = TRUE;
strcpy(photoFile, ofnp.lpstrFile);
strcat(photoFile, ".bmp");
capDriverConnect(hwndPhoto, 0); // 與驅動連接配接
capOverlay(hwndPhoto, TRUE); // //使用 hardware overlay
//使 video input directlyinto framebuffer,使不用 CPU
capPreview(hwndVideo, TRUE); // 抓取圖像顯示在視窗
capPreviewScale(hwndVideo, TRUE); // 使圖像延伸到所設定的視窗大小
capPreviewRate(hwndVideo, 1000/30); // 每秒圖像變化張數
capPreview(hwndVideo, TRUE); // 預覽圖像顯示
capGrabFrameNoStop(hwndVideo);
capEditCopy(hwndVideo);
if (capFileSaveDIB(hwndVideo, photoFile)){
MessageBox(hMain, "Save Bmp File Succeeded!", "photo save", MB_OK|MB_ICONINFORMATION);
}
break;
}
case RECORDVIDEO: // 錄制視訊
if (HIWORD(wParam) == BN_CLICKED && (HWND)lParam == hwndRecord)
OPENFILENAME ofnp;
if (!bRecordFileOpen)
ZeroMemory(&ofnp, sizeof(OPENFILENAME));
ofnp.lStructSize = sizeof(OPENFILENAME);
ofnp.nMaxFile = sizeof(recordFile);
ofnp.hwndOwner = hMain;
ofnp.lpstrFile = recordFile;
ofnp.lpstrFilter = "*.avi";
ofnp.lpstrInitialDir = NULL;
ofnp.nFilterIndex = 1;
ofnp.lpstrFileTitle = NULL;
ofnp.nMaxFileTitle = 0;
ofnp.Flags = OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST;
// 顯示儲存檔案對話框
if (GetSaveFileName(&ofnp) == TRUE)
{
strcpy(recordFile, ofnp.lpstrFile);
strcat(recordFile, ".avi");
bRecordFileOpen = TRUE;
// 建立視訊采集線程
DWORD id;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
hVideoTread = CreateThread(&sa,
(ULONG)0,
(LPTHREAD_START_ROUTINE)VideoThreadProc,
(LPVOID)(ULONG)0,
0,
&id);
// 如果建立線程失敗,彈出提示對話框
if (hVideoTread == NULL)
{
MessageBox(NULL, "Video Thread Create Failure!", "Thread", MB_OK|MB_ICONINFORMATION);
}
if (bRecordFileOpen) // 已經打開一個選擇對話框
if (bRecording) // 已經在錄制視訊中
bThreadEnd = true;
// 結束錄制,并儲存
if ( capFileSaveAs(hwndVideo, recordFile) ){
MessageBox(NULL, "Save Avi File Succeeded!", "RecordVideo", MB_OK|MB_ICONINFORMATION);
SetWindowText(hwndRecord, "錄 像");
bRecording = false;
break;
else // 沒有錄像但是選擇了一個檔案
int rt = 0;
rt = MessageBox(hMain, "Do you want write over that file?", "系統提示", MB_YESNO|MB_ICONINFORMATION);
if (rt == IDYES) // 繼續錄制,覆寫目前檔案
{
if (capCaptureSequence(hwndRecord))
{
bRecording = true;
SetWindowText(hwndRecord, "停 止");
}
else
bRecordFileOpen = false;
SendMessage(hMain, WM_COMMAND, MAKEWPARAM(RECORDVIDEO, BN_CLICKED), (LPARAM)hwndRecord);
}
case WM_DESTROY:
capSetCallbackOnError(hwndVideo, NULL);
capPreview(hwndVideo, FALSE); // 停止預覽
capCaptureAbort(hwndVideo); // 停止捕獲
capDriverDisconnect(hwndVideo); // 斷開驅動連接配接
PostQuitMessage(0);
default:
return 0;
LRESULT WINAPI SelCapDrcProc( HWND hMian, UINT msg, WPARAM wParam, LPARAM lParam )
// ShowWindow(hwndSelCapDrvDlg, SW_SHOW);
case WM_INITDIALOG:
return TRUE;
case BTN_SEL:
int sel = 0;
SendMessage(hwndSelCapDrvLBox, LB_GETSELITEMS, 1, sel ); // 擷取選擇的驅動
SendMessage(hwndVideo, WM_CAP_DRIVER_CONNECT, sel, 0L); // 連接配接驅動
SendMessage(hwndSelCapDrvDlg, WM_CLOSE, 0, 0); // 關閉對話框
SendMessage(hwndVideo, WM_CAP_DRIVER_GET_CAPS, sizeof(CAPDRIVERCAPS), (long)(LPVOID)&capDrvCaps); // 更新驅動的能力
if (capDrvCaps.fHasOverlay)
capOverlay(hwndVideo, TRUE);
capPreviewRate(hwndVideo, 66); //設定視訊捕獲速率提高到66 miliseconds
if ( !capPreview(hwndVideo, TRUE)) //用CapPreview啟動預覽功能
MessageBox(NULL, "預覽啟動失敗", "提示", MB_OK|MB_ICONINFORMATION);
SendMessage(hMian, WM_COMMAND, EXIT, 0);
return FALSE;
case WM_CLOSE:
DestroyWindow(hwndSelCapDrvDlg);
return (0L);
int EnumCapDrv()
char drvName[100];
char drvVesion[100];
char drvStr[200];
for (int i = 0; i < 5; i++) // 掃面擷取驅動資訊
if ( capGetDriverDescription(i, drvName, sizeof(drvName), drvVesion, sizeof(drvVesion)) )
strcpy(drvStr, drvName);
strcat(drvStr, " ");
strcat(drvStr, drvVesion);
SendMessage(hwndSelCapDrvLBox, LB_ADDSTRING, 0, (LPARAM)drvStr); // 向清單框增加字元串
SendMessage(hwndSelCapDrvLBox, LB_SETITEMDATA, i, (LPARAM)i);
VOID APIENTRY HandlePopupMenu( HWND hwnd, POINT pt )
HMENU hmenu;
HMENU hPopupMenu;
hmenu = LoadMenu((HINSTANCE)ghInstance, MAKEINTRESOURCE(IDR_POPUPMENU));
if(hmenu == NULL) return;
// 擷取子菜單
hPopupMenu = GetSubMenu(hmenu, 0);
// 彈出菜單使用螢幕坐标,是以轉換滑鼠點選坐标到螢幕坐标
ClientToScreen(hwnd, &pt);
TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN|TPM_LEFTBUTTON, pt.x, pt.y, 0, hwnd, NULL);
DestroyMenu(hmenu); // 銷毀指定菜單,并釋放此菜單占用的記憶體
WORD WINAPI VideoThreadProc( LPVOID lParam )
// 捕獲視訊
if( capCaptureSequence(hwndVideo) ) //該函數調控啟動流視訊和音頻捕獲到一個檔案,你能使用這個功能發送消息
SetWindowText(hwndRecord, "停 止");
MessageBox(NULL, "Start to capture video.", "提示", MB_OK|MB_ICONINFORMATION);
bRecording = true;
while(!bThreadEnd);
MessageBox(NULL, "Leaving Record Thread", "Thread",NULL);