/********************************************************************\
* CapPicture.c: 类的源码 *
* *
* Comments: 视频信号的采集、显示、存储和一些处理 *
* *
* Functions: *
* WinMain - Application entry point *
* MainWndProc - 主要窗口程序 *
* *
* *
\********************************************************************/
/********************* 头文件 *********************/
#include <windows.h> // windows GUI and services
#include <vfw.h> // 视频库
#include <commdlg.h> // common dialogs
#include "CapPicture.h" // resource header
/********************* Prototypes ***********************/
// 主要窗口程序
LRESULT WINAPI MainWndProc( HWND, UINT, WPARAM, LPARAM );
// 选择捕捉程序驱动程序
LRESULT WINAPI SelCapDrvProc( HWND, UINT, WPARAM, LPARAM );
// 列举捕捉驱动
int EnumCapDrv();
// 在主窗口创建按钮
//int CreateWndButtons(); this doesnt work
// 右键弹出菜单句柄
VOID APIENTRY HandlePopupMenu(HWND, POINT);
// 视频程序线程(video thread procedure)
DWORD WINAPI videoThreadProc(LPVOID lParam);
/******************* 全程变量 ********************/
HANDLE ghInstance; // application instance
HWND hwndMain; // 主要窗口句柄(main window handle)
HWND hwndVideo; // 视频捕捉窗口句柄(video capture window)
HWND hwndSelCapDrvDlg; // 选择捕捉驱动对话句柄(Select the capture driver dialog)
HWND hwndSelCapDrvDlg_LBox; // 选择捕捉驱动对话列举框句柄(list box for select capture driver dialog)
HWND hwndExit; // 退出按钮(exit button)
HWND hwndMin; // 最小化按钮(minimize button)
HWND hwndHelp; // 帮助按钮(help button)
HWND hwndRecord; // 录像按钮(record button)
HANDLE hVideoThread; // 停止录取视频线程(thread to stop the hang when recording video)
HRGN hRegion1; // 窗口修正区域(region for window shaping)
CAPDRIVERCAPS CapDrvCaps; // 驱动性能(driver capabilities)
bool isRecordFileOpen = false; // 录像开始标记(flag set if record file is open)
char recordFile[260]; // 保持录像标记(file to hold recording)
bool isPicFileOpen = false; // flag set if snapshot file is open
char pictureFile[260]; // file to hold snapshot
bool isRecording = false; // 判断是否录像?(are we recording?)
bool threadEnd = false; // 判断视频线程是否终止?(should the video thread end?)
/********************************************************************\
* *
* CLASSES, ENUMS, & STRUCTS *
* *
/********************************************************************/
/********************************************************************\
* Function: int PASCAL WinMain(HINSTANCE, HINSTANCE, LPSTR, int) *
* *
* 用途: 应用程序初始化 *
* *
* 注释: 注册窗口类, 创建显示主要窗口, 进入消息循环 *
* *
* *
\********************************************************************/
int PASCAL WinMain( HINSTANCE hInstance,//HINSTANCE:实例的句柄(Handle to an instance)
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow )
{
WNDCLASS wc;//WNDCLASS结构包含了RegisterClass函数注册窗口类时的窗口类属性
MSG msg;
if( !hPrevInstance )//如果不是前实例
{
wc.lpszClassName = "GenericAppClass";
wc.lpfnWndProc = MainWndProc;//函数名代表首地址,指定一个回调函数
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = CreateSolidBrush (RGB(0, 64, 128));
wc.lpszMenuName = "GenericAppMenu";
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
RegisterClass( &wc );//注册窗口类
}
ghInstance = hInstance;
hwndMain = CreateWindow( "GenericAppClass",//该函数创建一个重叠式窗口、弹出式窗口或子窗口。
"视频捕获",
WS_POPUP,
0,
0,
500,
500,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow( hwndMain, nCmdShow );//显示窗口
//将主要的窗户设定为区域(Set the main window to the region)
SetWindowRgn(hwndMain,hRegion1,1);//注释掉此句的话,则显示矩形窗口,但是也同时显示一个圆角矩形
while( GetMessage( &msg, NULL, 0, 0 ) ) {
TranslateMessage( &msg );//该函数将虚拟键消息转换为字符消息。字符消息被寄送到调用线程的消息队列里,当下一次线程调用函数GetMessage或PeekMessage时被读出
DispatchMessage( &msg );//该函数调度一个消息给窗口程序。通常调度从GetMessage取得的消息。消息被调度到的窗口程序即是MainProc()函数
}
return msg.wParam;
}
/********************************************************************\
* Function: LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM) *
* *
* 目的:应用程序的信息 *
* *
* 注释: 下面的消息将被处理 *
* *
* WM_PAINT *
* WM_CREATE *
* WM_DESTROY *
* *
* *
\********************************************************************/
LRESULT CALLBACK MainWndProc( HWND hwndMain, UINT msg, WPARAM wParam,
LPARAM lParam )//这就表示此函数是一个回调函数。在MFC中,得到Message消息以后系统会进行回调,当然,我们需要编写一个回调函数来响应。为了区别于其它函数,在回调函数前加上LRESULT CALLBACK
{
HDC hDC = GetDC(hwndMain);//该函数检索一指定窗口的客户区域或整个屏幕的显示设备上下文环境的句柄,以后可以在GDI函数中使用该句柄来在设备上下文环境中绘图
RECT rc; // 客户区域(client area)
POINT pt; // 鼠标点击位置(location of mouse click)
switch( msg ) {
/**************************************************************\
* WM_LBUTTONDBLCLK: *
\**************************************************************/
case WM_LBUTTONDBLCLK: //微软WINDOWS窗口消息.表示左键双击事件,该消息可由Win32底层函数PeekMessage和GetMessage取得
SetFocus(hwndMain);//此功能设置键盘焦点到指定的窗口。随后的所有键盘输入是针对此窗口。窗口,如果有的话,以前的键盘焦点失去它。
break;
/**************************************************************\
* WM_LBUTTONDOWN: *
\**************************************************************/
case WM_RBUTTONDOWN: //右键按下
// Get the bounding rectangle of the client area.
GetClientRect(hwndMain, (LPRECT) &rc); //该函数获取窗口客户区的坐标。
// Get the client coordinates for the mouse click.
pt.x = LOWORD(lParam); //This macro retrieves the low-order word from the specified 32-bit value.
//在intel的CPU上,字节存取是反过来的,低八位也就是前八位
//
pt.y = HIWORD(lParam); //This macro retrieves the high-order word from the specified 32-bit value
// If the mouse click took place inside the client
// area, execute the application-defined function
// that displays the shortcut menu.
if (PtInRect((LPRECT) &rc, pt)) //判断点是否在矩形中
HandlePopupMenu(hwndMain, pt);
break;
/**************************************************************\
* WM_PAINT: *
\**************************************************************/
case WM_PAINT://当视窗显示区域的一部分显示内容或者全部变为「无效」,以致于必须「更新画面」时,将由这个讯息通知程式
//给该区域一个红色边框(Give the region a red border)
FrameRgn(hDC,hRegion1,CreateSolidBrush(RGB(0,0,0)),2,2); //The FrameRgn function draws a border around the specified region by using the specified brush
// 将对话框置于前端(bring our dialog to the foreground)
BringWindowToTop(hwndSelCapDrvDlg);//This function brings the specified window to the top of the z-order. If the window is a top-level window, it is activated. If the window is a child window, the top-level parent window associated with the child window is activated
return( DefWindowProc( hwndMain, msg, wParam, lParam ));//该函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理。调用DefWindowProc函数时使用窗口过程接收的相同参数
/**************************************************************\
* WM_COMMAND: *
\**************************************************************/
case WM_COMMAND://消息被发送,当用户从菜单中选择一项,当一个控制消息发出给它的父窗口,或是一个加速按键被释放,一个窗口通过 WindowProc 接收消息
CAPSTATUS CapStatus;
switch( wParam ) {
case SOURCE:
if(CapDrvCaps.fHasDlgVideoSource)
capDlgVideoSource(hwndVideo);
break;
case FORMAT://why doesnt this work
//if(CapDrvCaps.fHasDlgVideoFormat)
//{
capDlgVideoFormat(hwndMain);
// 是否有新的图片尺寸(Are there new image dimensions)
capGetStatus(hwndVideo, &CapStatus, sizeof(CAPSTATUS));
SetWindowPos(hwndVideo, NULL, 0, 0, CapStatus.uiImageWidth,
CapStatus.uiImageHeight, SWP_NOZORDER | SWP_NOMOVE);
//}
break;
case DISPLAY://why doesnt this work//显示
//if (CapDrvCaps.fHasDlgVideoDisplay)
capDlgVideoDisplay(hwndVideo);
break;
case EXIT://退出
SendMessage(hwndMain, WM_SYSCOMMAND, SC_CLOSE, 0);
break;
case MINIMIZE://最小化
SendMessage(hwndMain, WM_SYSCOMMAND, SC_MINIMIZE, 0);
break;
case HELP://帮助
SendMessage(hwndMain, WM_SYSCOMMAND, SC_CONTEXTHELP, 0);
break;
case RECORDVIDEO://录制视频
if(HIWORD(wParam) == BN_CLICKED && (HWND) lParam == hwndRecord)
{
if (!isRecordFileOpen)
{
OPENFILENAME ofn; // open file name structure
// initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwndMain;
ofn.lpstrFile = recordFile;
ofn.nMaxFile = sizeof(recordFile);
ofn.lpstrFilter = "Video\0*.avi";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// 显示保存对话框(Display the Save dialog box).
if(GetSaveFileName(&ofn) == TRUE)
{
strcpy(recordFile, ofn.lpstrFile);
strcat(recordFile, ".avi");
isRecordFileOpen = true;
// 创建视频采集线程(create the video capture thread)
DWORD id;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
hVideoThread = CreateThread(&sa, (ULONG)0,
videoThreadProc, (LPVOID)(ULONG)0, (ULONG)0, &id);
if(hVideoThread == NULL) //如果建立线程失败,则弹出对话框提示
MessageBox(NULL, "Creation of Record Thread failed!", "Thread", MB_OK | MB_ICONEXCLAMATION);
break;
}
}
if (isRecordFileOpen) // 已经有一个选择(we already have a file selected)
{
if(isRecording) // 已经在录制(we're already recording)
{
threadEnd = true;
// 结束捕获并保存它(end the capture and save it)
capFileSaveAs(hwndVideo, recordFile);
// 将录制按钮的文本设为"录制视频"(make the record button say "Record Video")
SetWindowText(hwndRecord, "Record Video");
isRecording = false;
break;
}
if(!isRecording ) // 没有在录制,但是已经选择了一个文件(we're not recording, but a file's selected)
{
int a = 0;
MessageBox(hwndMain, "Do you want to write over the open file?",
"File warning", MB_YESNO | MB_ICONWARNING);
if (a != IDYES)
{
isRecordFileOpen = false;
SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(RECORDVIDEO, BN_CLICKED), (LPARAM) hwndRecord);
}
if (a == IDYES)
{
capCaptureSequence(hwndVideo);
isRecording = true;
}
break;
}
}
}
break;
}
break;
/**************************************************************\
* WM_CREATE: *
\**************************************************************/
case WM_CREATE:
RECT helpRect, minRect, exitRect;
HRGN helpRgn, minRgn, exitRgn;
// 创建主区域(make the main region)
//创建一个圆角矩形,该矩形由X1,Y1-X2,Y2确定,并由X3,Y3确定的椭圆描述圆角弧度 返回值 Long,执行成功则为区域句柄,失败则为0
hRegion1 = CreateRoundRectRgn(0,0,500,400, 200, 200); //若注释掉此句,则生成的窗口是规则的矩形,否则为圆角的矩形
// 建立一个视频捕获窗口
hwndVideo = capCreateCaptureWindow(
(LPSTR) "My Capture Window",//Null-terminated string containing the name used for the capture window
WS_CHILD | WS_VISIBLE ,//视频捕获窗口的样式,可以设置更多,可参考MSDN的CreateWindowEx,
160, 120, 200, 148,//The x-coordinate of the upper left corner of the capture window
//The y-coordinate of the upper left corner of the capture window
//Width of the capture window
//Height of the capture window
(HWND) hwndMain,//Handle to the parent window
(int) 1);
// 建立主窗体的各个按钮
// 建立一个关闭按钮
hwndExit = CreateWindow (
"button", // 建立一个按钮,更多如BUTTON,COMBOBOX,LISTBOX
"x", // button text
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER, // Window Styles,其它样式如WS_BORDER,WS_CAPTION,WS_HSCROLL
275, 10, BUTTONSIZE, BUTTONSIZE, // position and size
hwndMain, // Parent is main window
(HMENU) EXIT,// Control ID: EXIT
(HINSTANCE)ghInstance,
(LPVOID)NULL);
// 建立最小化按钮
hwndMin = CreateWindow (
"button", // Builtin button class
"-", // button text
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER, // styles
250, 10, BUTTONSIZE, BUTTONSIZE, // position and size
hwndMain, // Parent is main window
(HMENU) MINIMIZE,// Control ID: MINIMIZE
(HINSTANCE)ghInstance,
(LPVOID)NULL);
// 建立帮助按钮:
/*
hwndHelp = CreateWindow (
"button", // Builtin button class
"?", // button text
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER, // styles
225, 10, BUTTONSIZE, BUTTONSIZE, // position and size
hwndMain, // Parent is main window
(HMENU) HELP,// Control ID: HELP
(HINSTANCE)ghInstance,
(LPVOID)NULL);
*/
// 创建视频录制按钮
hwndRecord = CreateWindow (
"button", // Builtin button class
"录制视频", // button text
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER, // styles
200, 280, 90, 28, // position and size
hwndMain, // Parent is main window
(HMENU) RECORDVIDEO,// Control ID: RECORDVIDEO
(HINSTANCE)ghInstance,
(LPVOID)NULL);
// 获取按钮矩形区域 rectangles
//GetClientRect(hwndHelp, &helpRect);
GetClientRect(hwndMin, &minRect);
GetClientRect(hwndExit, &exitRect);
// 建立按钮区域
//helpRgn = CreateEllipticRgnIndirect(&helpRect);
minRgn = CreateEllipticRgnIndirect(&minRect);
exitRgn = CreateEllipticRgnIndirect(&exitRect);
//Set the window to the region
SetWindowRgn(hwndExit,exitRgn,1);//SetWindowRgn 功能设定窗户的窗户区域。 窗户区域决定系统允许图画的窗户里面的区域。 系统不显示在窗户区域的外面躺卧的窗户的任何部分
SetWindowRgn(hwndMin,minRgn,1);
//SetWindowRgn(hwndHelp,helpRgn,1);
// 创建一个驱动选择对话框
hwndSelCapDrvDlg = CreateDialog((HINSTANCE)ghInstance,
MAKEINTRESOURCE( SELCAPDRVDLG ),
0, (DLGPROC)SelCapDrvProc);
// 取得列表框的句柄(get the handle to the list box)
hwndSelCapDrvDlg_LBox = GetDlgItem(hwndSelCapDrvDlg,
SELCAPDRVDLG_LSTBOX);
EnumCapDrv();//显示驱动列表框供用户选择
break;
/****************************************************************\
*WM_DESTROY: PostQuitMessage() is called and get rid of vfw stuff*
\****************************************************************/
case WM_DESTROY://这一个讯息指示,Windows正在根据使用者的指示关闭视窗。该讯息是使用者单击Close按钮或者在程式的系统功能表上选择 Close时发生的
capPreview(hwndVideo, FALSE); // 结束预览
capDriverDisconnect(hwndVideo); // disconnect from driver
PostQuitMessage( 0 );//该函数向系统表明有个线程有终止请求。通常用来响应WM_DESTROY消息。
break;
/**************************************************************\
* Let the default window proc handle all other messages *
\**************************************************************/
default:
return( DefWindowProc( hwndMain, msg, wParam, lParam ));//该函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理。调用DefWindowProc函数时使用窗口过程接收的相同参数
}
return 0;
}
LRESULT CALLBACK SelCapDrvProc( HWND hWnd, UINT msg, /*callback procedure */
WPARAM wParam, LPARAM lParam )
{
switch(msg)
{
// dialog created
//对话框被建立
case WM_INITDIALOG:
return TRUE;
// command
case WM_COMMAND:
switch ( wParam )
{
// user clicked the select driver button
//用户点击了选择驱动按钮
case SELCAPDRVDLG_BUTTON:
int sel = 0;
// get the selected driver
//得到选择的驱动
SendMessage( hwndSelCapDrvDlg_LBox, LB_GETSELITEMS, 1, sel);//该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。而函数PostMessage不同,将一个消息寄送到一个线程的消息队列后立即返回
// connect to the driver
//连接驱动
SendMessage( hwndVideo, WM_CAP_DRIVER_CONNECT, sel, 0L);
// then close this dialog
//关闭对话框
SendMessage( hwndSelCapDrvDlg, WM_CLOSE, 0, 0);
// update the driver capabilities
//更新驱动程序的能力
SendMessage( hwndVideo, WM_CAP_DRIVER_GET_CAPS,
sizeof(CAPDRIVERCAPS), (LONG) (LPVOID) &CapDrvCaps);
// set preview rate to 66 miliseconds
capPreviewRate( hwndVideo, 66 );////设置视频捕获速率提高到66 miliseconds
// start preview video
//用CapPreview启动预览功能
capPreview( hwndVideo, TRUE );
}
return TRUE;
// user wants to close dialog
case WM_CLOSE:
DestroyWindow(hwndSelCapDrvDlg);//销毁指定的窗口
return TRUE;
}
return( 0L );
}
int EnumCapDrv() // 列举安装的捕捉驱动(enumerate the installed capture drivers)
{
char szDeviceName[80]; // driver name驱动名称
char szDeviceVersion[80]; // driver version驱动版本
char item[161]; // concatinated string
int i; // counter计数
for (i=0; i<10; i++)
{
if ( capGetDriverDescription(i, szDeviceName, sizeof(szDeviceName),//The capGetDriverDescription function retrieves the version description of the capture driver
//获得视频驱动的版本描述
szDeviceVersion, sizeof(szDeviceVersion)) )
{
//把驱动器名和驱动器版本连起来
strcpy(item, szDeviceName);
strcat(item, ":::::");
strcat(item, szDeviceVersion);
// add item to list box
//增加条目到列表框
SendMessage(hwndSelCapDrvDlg_LBox, LB_ADDSTRING, 0,
(LPARAM) item);
SendMessage(hwndSelCapDrvDlg_LBox, LB_SETITEMDATA, i, (LPARAM) i);
}
}
return 0;
}
/*int CreateWndButtons()
{//why doesnt this work
RECT rc; // window rectangle
GetClientRect(hwndMain, &rc);
// create the button
hwndExit = CreateWindow (
"button", // Builtin button class
"X", // button text
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, // styles
100, 100, BUTTONSIZE, BUTTONSIZE, // position and size
hwndMain, // Parent is main window
(HMENU) EXIT,// Control ID: EXIT
(HINSTANCE)ghInstance,
(LPVOID)NULL);
return 0;
}
*/
VOID APIENTRY HandlePopupMenu(HWND hwnd, POINT pt)
{
HMENU hmenu; // menu template
HMENU hmenuTrackPopup; // shortcut menu
// Load the menu template containing the shortcut menu from the
// application's resources.
hmenu = LoadMenu((HINSTANCE)ghInstance, "PopupMenu"); //该函数从与应用事例相联系的可执行文件(.EXE)中加载指定的菜单资源
if (hmenu == NULL)
return;
//获取第一个菜单中的关闭菜单,这是跟踪弹出菜单
// Get the first shortcut menu in the menu template. This is the
// menu that TrackPopupMenu displays.
//获取子菜单
hmenuTrackPopup = GetSubMenu(hmenu, 0); //该函数取得被指定菜单激活的下拉式菜单或子菜单的句柄
//弹出菜单使用屏幕坐标,所以转换鼠标点击坐标到屏幕坐标
// TrackPopup uses screen coordinates, so convert the
// coordinates of the mouse click to screen coordinates.
//转换客户区坐标到屏幕坐标
ClientToScreen(hwnd, (LPPOINT) &pt); //该函数将指定点的用户坐标转换成屏幕坐标
// Draw and track the shortcut menu.
//绘画和跟踪快捷菜单
TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwnd, NULL); //该函数在指定位置显示快捷菜单,并跟踪菜单项的选择。快捷菜单可出现在屏幕上的任何位置
// 销毁菜单
DestroyMenu(hmenu); //该函数销毁指定的菜单,并释放此菜单占用的存储器
}
DWORD WINAPI videoThreadProc(LPVOID lParam)
{
//将录制按钮文本设为"停止录制"
// make the record button say "Stop Recording"
SetWindowText(hwndRecord, "停止录制");
// 捕获视频
capCaptureSequence(hwndVideo);//The capCaptureSequence macro initiates streaming video and audio capture to a file. You can use this macro or explicitly send the WM_CAP_SEQUENCE message
//该函数调控启动流视频和音频捕获到一个文件,你能使用这个功能发送消息
isRecording = true;
// don't exit the thread until the record button is pressed again
while (!threadEnd)
;
MessageBox(NULL, "正在关闭线程", "thread", NULL);
return 0;
}
评论