Windows C Programming projects
Accessing the web cam
We're going to create 2 C files and 1 header: main.c, video.c, video.h
Here's the code for main.c: (Note: #include <vfw.h> and #include "video.h"
// main.c
#include <windows.h> // the usual includes
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <process.h>
#include <malloc.h>
#include <io.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <direct.h>
#include <EXCPT.H>
#include <commctrl.h>
#include <vfw.h> // this holds all the media prototypes etc
#include "video.h" // this is our new include file
long FAR PASCAL cam01WndProc(HWND hwnd, UINT message, WPARAM wParam, LONG lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch(message)
{
case WM_TIMER:
PollVideo(); // this call makes the cam driver get a frame
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
KillTimer(hwnd, 1); // bye bye timer
StopVideoCapture(); // stop the capture
StopVideo(); // stop the cam driver
PostQuitMessage(0); // quit the message loop
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0L;
}
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // pointer to command line
int nShowCmd // show state of window
)
{
char *p;
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
p = "CamWindow-01";
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = cam01WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = p;
RegisterClass(&wndclass);
hwnd = CreateWindow(p, // window class name
p, // window caption
WS_OVERLAPPEDWINDOW | WS_MINIMIZE, // window style
300, // x pos
300, // y pos
700, // x size
380, // y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // prog instance handle
NULL); // creation parameters
ShowWindow(hwnd, SW_SHOW); // window to screen
UpdateWindow(hwnd); // repaint thyself
StartVideo(hwnd, 1); // start up the cam
StartVideoCapture(hwnd, 4, 100); // tell where to put the capture image
SetTimer(hwnd, 1, 1000/25, NULL); // set a timer to keep calling
while (GetMessage(&msg, NULL, 0, 0)) // message loop for all windows
{ // belonging to this thread
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Now, here's the code for video.h. Just a few prototypes.
// video.h void StartVideoCapture(HWND hwShow, int xpos, int ypos); void StopVideoCapture(); void StartVideo(HWND hwParent, int visible); void StopVideo(); void PollVideo(); int VideoDevs();
Now the cam code video.c.
// video.c
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <process.h>
#include <malloc.h>
#include <io.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <direct.h>
#include <EXCPT.H>
#include <vfw.h>
#include "video.h"
// a debugging caste I use
#define AddStrToVizWindow(x) MessageBox(NULL, x, "Video", MB_OK)
// VIDEO_FRAME target values
#define VF_USER 0 // update user_names video image
// VIDEO_FRAME compression values
#define VF_NO_COMPRESSION 0
static HWND hwVidParent = NULL;
static HWND hWndC = NULL;
static int gdwFrameNum = 0;
static int capOn = 0;
static CAPDRIVERCAPS CapDrvCaps;
static CAPSTATUS CapStatus;
static int useDialog = 0;
static int csInited = 0;
static CRITICAL_SECTION csVideo;
#define PREF_WIDTH 320
#define PREF_HEIGHT ((PREF_WIDTH / 4) * 3)
#define DISPLAY_WIDTH 80
#define DISPLAY_HEIGHT ((DISPLAY_WIDTH / 4) * 3)
static int v_prev = 0;
static int v_width = PREF_WIDTH;
static int v_height = PREF_HEIGHT;
static BITMAPINFO bmVidFormat = // prefered format
{
{ sizeof(BITMAPINFOHEADER), // size
PREF_WIDTH, // LONG biWidth;
PREF_HEIGHT, // LONG biHeight;
1, // WORD biPlanes;
24, // WORD biBitCount;
BI_RGB, // DWORD biCompression;
PREF_WIDTH*PREF_HEIGHT*3, // DWORD biSizeImage;
0, // LONG biXPelsPerMeter;
0, // LONG biYPelsPerMeter;
0, // DWORD biClrUsed;
0, // DWORD biClrImportant;
},
{255, 255, 255, 0} // color intensity
};
static BITMAPINFO *pVidFormat = NULL; // will point to final format accepted
static int format_len = 0; // by driver
static HWND hwDisplayWin = NULL;
static short dwXpos;
static short dwYpos;
// **********************************************************************
#define VID_STACK_SIZE 2 // stack of image data waiting to be read - keep small
static unsigned char *vid_data_stack[VID_STACK_SIZE];
static int vid_data_siz[VID_STACK_SIZE];
static int vid_head = 0;
static int vid_tail = 0;
// 2 push and pop funcs.
// I like to save the frames - you don't have to
static void PushVidData(unsigned char *data, int siz)
{
if (!csInited)
return;
EnterCriticalSection(&csVideo);
if (vid_data_stack[vid_head] != NULL)
{
free(vid_data_stack[vid_head]);
vid_data_stack[vid_head] = NULL;
}
vid_data_stack[vid_head] = malloc(siz);
if (vid_data_stack[vid_head])
{
memcpy(vid_data_stack[vid_head], data, siz);
vid_data_siz[vid_head] = siz;
vid_head = (vid_head + 1) % VID_STACK_SIZE;
if (vid_head == vid_tail) // caught up
vid_tail = (vid_tail + 1) % VID_STACK_SIZE;
}
LeaveCriticalSection(&csVideo);
}
static unsigned char *PopVidData(int *siz)
{
unsigned char *p;
if (!csInited)
return NULL;
EnterCriticalSection(&csVideo);
if (vid_tail != vid_head)
{
*siz = vid_data_siz[vid_tail];
p = malloc(*siz); // have to move it! Push may try to free it
if (p)
memcpy(p, vid_data_stack[vid_tail], *siz);
else
*siz = 0;
vid_tail = (vid_tail + 1) % VID_STACK_SIZE;
} else
{
*siz = 0;
p = NULL;
}
LeaveCriticalSection(&csVideo);
return p;
}
// **********************************************************************************
static void DrawBitmap(HDC hdc, // draw bit map func to display image
short xstart,
short ystart,
HBITMAP hBitmap)
{
BITMAP bm;
HDC hMemDC;
POINT pt;
if (hBitmap)
{
hMemDC = CreateCompatibleDC(hdc);
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (LPSTR) &bm);
pt.x = bm.bmWidth;
pt.y = bm.bmHeight;
BitBlt(hdc, xstart, ystart, pt.x, pt.y, hMemDC, 0,0, SRCCOPY);
DeleteDC(hMemDC);
}
}
/*
LRESULT CALLBACK capVideoStreamCallback(HWND hWnd, LPVIDEOHDR lpVHdr);
Callback function used with streaming capture to optionally process a frame of
captured / previewed video.
*/
LRESULT CALLBACK capVideoStreamCallback(HWND hWnd, LPVIDEOHDR lpVHdr)
{
if (!hwVidParent)
return FALSE;
if (capOn)
{
if (hwDisplayWin)
{
HBITMAP hb;
HDC hdc;
hdc = GetDC(hwDisplayWin);
hb = CreateCompatibleBitmap(hdc,
pVidFormat->bmiHeader.biWidth,
pVidFormat->bmiHeader.biHeight);
SetDIBits( hdc, // handle of device context
hb, // handle of bitmap
0, // starting scan line
pVidFormat->bmiHeader.biHeight, // number of scan lines
lpVHdr->lpData, // array of bitmap bits
pVidFormat, // address of structure with bitmap data
DIB_RGB_COLORS);
DrawBitmap(hdc, dwXpos,dwYpos,hb); // big version in window
ReleaseDC(hwDisplayWin, hdc);
DeleteObject(hb);
}
}
PushVidData(lpVHdr->lpData, lpVHdr->dwBufferLength);
return (LRESULT) TRUE ;
}
LRESULT CALLBACK capControlCallback( HWND hWnd, int nState )
{
return TRUE;
}
LRESULT CALLBACK capVideoYield(HWND hWnd)
{
MSG msg;
BOOL ret;
/* get the next message, if any */
ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
/* if we got one, process it */
if (ret)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* TRUE if we got a message */
return ret;
}
// ********************************************************************
// ********************************************************************
// ********************************************************************
// ********************************************************************
void StartVideoCapture(HWND hwShow, int xpos, int ypos)
{
capOn = 1;
hwDisplayWin = hwShow;
dwXpos = xpos;
dwYpos = ypos;
}
void StopVideoCapture()
{
capOn = 0;
}
void StartVideo(HWND hwParent, int Visible)
{
int l, x;
char name[128], ver[128];
CAPTUREPARMS cp;
BITMAPINFO *pdebug; // for viewing under debug only
DWORD dwStyle;
memset(name,0, sizeof(name));
memset(ver,0, sizeof(ver));
hwVidParent = hWndC = NULL;
pVidFormat = NULL;
format_len = 0;
if (csInited == 0)
{
InitializeCriticalSection(&csVideo);
csInited = 1;
}
if (!capGetDriverDescription(0, name, sizeof(name), ver, sizeof(ver)))
{
MessageBox(hwParent, "No video-cam driver", "Video", MB_OK);
return;
}
if (strlen(name) == 0)
{
MessageBox(hwParent, "No video-cam driver named", "Video", MB_OK);
return;
}
//AddStrToVizWindow("Using video-cam driver %s", name);
hwVidParent = hwParent;
gdwFrameNum = 1;
dwStyle = WS_CHILD | WS_EX_NOPARENTNOTIFY | WS_EX_TOPMOST | WS_BORDER ;
if (Visible)
dwStyle |= WS_VISIBLE;
hWndC = capCreateCaptureWindow (
"Broadcasting",
dwStyle,
4,
4,
DISPLAY_WIDTH ,
DISPLAY_HEIGHT,
hwVidParent,
1);
if (hWndC)
{
capDriverConnect(hWndC, 0);
capDriverGetCaps(hWndC, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
/* **************
if (useDialog) // left in and commented out for you to play with
{
// Video source dialog box.
if (CapDrvCaps.fHasDlgVideoSource)
capDlgVideoSource(hWndC);
// Video format dialog box.
if (CapDrvCaps.fHasDlgVideoFormat)
{
capDlgVideoFormat(hWndC);
// Are there new image dimensions?
capGetStatus(hWndC, &CapStatus, sizeof (CAPSTATUS));
// If so, notify the parent of
// a size change.
}
// Video display dialog box.
if (CapDrvCaps.fHasDlgVideoDisplay)
capDlgVideoDisplay(hWndC);
}
**************** */
if (capSetVideoFormat(hWndC, &bmVidFormat, sizeof(bmVidFormat))
== FALSE)
{
AddStrToVizWindow("Video format rejected");
l = capGetVideoFormatSize(hWndC);
if (l > 0)
{
pVidFormat = calloc(1, l);
if (pVidFormat)
{
format_len = l;
x = capGetVideoFormat(hWndC, pVidFormat, l);
if (x <= 0)
{
free(pVidFormat);
pVidFormat = NULL;
format_len = 0;
AddStrToVizWindow("Video format NOT retrieved");
} else
AddStrToVizWindow("Video format retrieved");
} else
AddStrToVizWindow("Video format calloc() failed");
} else
AddStrToVizWindow("Video format size return 0");
} else
{
pVidFormat = &bmVidFormat;
format_len = sizeof(bmVidFormat);
//AddStrToVizWindow("Prefered video format accepted");
}
pdebug = &bmVidFormat;
pdebug = pVidFormat;
capSetCallbackOnVideoStream(hWndC, capVideoStreamCallback);
capSetCallbackOnFrame(hWndC, capVideoStreamCallback);
capSetCallbackOnYield(hWndC, capVideoYield);
capSetCallbackOnCapControl(hWndC, capControlCallback);
capPreviewRate(hWndC, 1000);
capPreview(hWndC, FALSE);
capOverlay(hWndC, FALSE); // enabling overlay disables preview
capPreviewScale(hWndC, TRUE); // images scaled
capCaptureGetSetup(hWndC, &cp, sizeof(cp));
// cp.dwRequestMicroSecPerFrame = 100000; // 10 frames per sec
cp.dwRequestMicroSecPerFrame = 66667; // 15 frames per sec
// cp.dwRequestMicroSecPerFrame = 40000; // 25 frames per sec
cp.wPercentDropForError = 100;
cp.fYield = FALSE;
cp.fCaptureAudio = FALSE;
cp.vKeyAbort = 0;
cp.fAbortLeftMouse = FALSE;
cp.fAbortRightMouse = FALSE;
cp.fLimitEnabled = FALSE;
capCaptureSetSetup(hWndC, &cp, sizeof(cp));
}
}
void PollVideo()
{
capGrabFrame(hWndC); // gets the frame in the stack which
// causes the callback function to do its thing
}
void StopVideo()
{
DWORD t = GetTickCount() + 1000; // wait a sec
if (hWndC)
{
capCaptureStop(hWndC);
PostMessage(hWndC, WM_CLOSE, 0, 0);
hWndC = NULL;
while (capVideoYield(NULL) && GetTickCount() < t)
;
}
hwVidParent = NULL;
}
int VideoDevs()
{
return (hWndC != NULL);
}
Compile those and you should get something like:

Except I hope for your sake you're prettier.
|