Gary Baker is a C and assembler programmer with a vast amount of experience on a myriad of Windows and embedded systems. If you have any bespoke projects or contracts you wish to discuss please email g_b001@yahoo.co.uk.co.uk

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.




  • Copyright (c) Gary Baker 2009 some rights reserved.
  • If you redistribute Gary Baker's content, please follow these conditions:
    • You must attribute the work to Gary Baker by prominently linking back to the source pages on Brambling Books. An ideal example would be: Article or tutorial provided by Gary Baker, BramblingBooks.co.uk/Software/
    • You must not use the content for commercial purposes. However, commercial republishing requests are often granted if you email info@bramblingbooks.co.uk and ask.
    • If you alter, remix, transform, or build upon work found on Brambling Books, you may distribute the resulting work only under the same or similar conditions.