Web Camera
UPDATE: There is a newer version of this article here (new version support Bitmap object)
UPDATE: I opened a workspace for it
here.
For a long time now I was looking for a web camera class. All samples I found on google where not pure C# and if they were they used the clipboard to receive the frames, so I decided to write my own web camera class.
The WebCamera Class is very simple to use.
The Constructor accept 3 variables: the control's Handle, Width and Height.
A Public method to Start the webCamera.
A Public method to STop the webCamera.
A Public Event if you want to recieve the data bytes of the last captured frame.
The WebCamera class make use of an API32 Wrapper Class which contains all the API calls and constants.
NOTICE: The data[] bytes is not BMP or anything like it. It needs to be converted. I can add another event that returns the System.Drawing.Image object, let me know if you want it.
NOTICE: For now the class support only the #1 web camera, it is VERY simple to change it to multiple, lets see who have the answer to it ;-)
NOTICE: There is an API32 call to create the window to Config the web camera - resolution and video type. The code doesn't support it but again, if you need it, let me know and I will add it and post it here.
WARNING: You MUST keep the instance, otherwise it will be GC and the API32 calls will fail to callback)
How to use syntax:
Decleration and instance of WebCamera.
private WebCamera wc;
wc = new WebCamera( panelPreview.Handle,panelPreview.Width,panelPreview.Height);
Handle the ReceivedFrame Event
wc.RecievedFrame+=new WebCam.WebCamera.RecievedFrameEventHandler(wc_RecievedFrame);
private void wc_RecievedFrame(byte[] data)
{
// Do what ever you want here...
}
Any comments nor suggestions will be very appreciated.
Lets see some CODE!!!
API32 Wrapper Class
using System.Runtime.InteropServices;
public class API32
{
// API32 calls
[DllImport("avicap32.dll")] public static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
[DllImport("avicap32.dll")] public static extern bool capGetDriverDescriptionA(short wDriver, byte[] lpszName, int cbName, byte[] lpszVer, int cbVer);
[DllImport("User32.dll")] public static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam);
[DllImport("User32.dll")] public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, int lParam);
[DllImport("User32.dll")] public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, FrameEventHandler lParam);
[DllImport("User32.dll")] public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref BITMAPINFO lParam);
[DllImport("User32.dll")] public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
[DllImport("avicap32.dll")]public static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize );
// Constants
public const int WM_USER = 0x400;
public const int WS_CHILD = 0x40000000;
public const int WS_VISIBLE = 0x10000000;
public const int SWP_NOMOVE = 0x2;
public const int SWP_NOZORDER = 0x4;
public const int WM_CAP_DRIVER_CONNECT = WM_USER + 10;
public const int WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
public const int WM_CAP_SET_CALLBACK_FRAME = WM_USER + 5;
public const int WM_CAP_SET_PREVIEW = WM_USER + 50;
public const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
public const int WM_CAP_SET_VIDEOFORMAT = WM_USER + 45;
// Structures
[StructLayout(LayoutKind.Sequential)] public struct VIDEOHDR
{
[MarshalAs(UnmanagedType.I4)] public int lpData;
[MarshalAs(UnmanagedType.I4)] public int dwBufferLength;
[MarshalAs(UnmanagedType.I4)] public int dwBytesUsed;
[MarshalAs(UnmanagedType.I4)] public int dwTimeCaptured;
[MarshalAs(UnmanagedType.I4)] public int dwUser;
[MarshalAs(UnmanagedType.I4)] public int dwFlags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)] public int[] dwReserved;
}
[StructLayout(LayoutKind.Sequential)] public struct BITMAPINFOHEADER
{
[MarshalAs(UnmanagedType.I4)] public Int32 biSize ;
[MarshalAs(UnmanagedType.I4)] public Int32 biWidth ;
[MarshalAs(UnmanagedType.I4)] public Int32 biHeight ;
[MarshalAs(UnmanagedType.I2)] public short biPlanes;
[MarshalAs(UnmanagedType.I2)] public short biBitCount ;
[MarshalAs(UnmanagedType.I4)] public Int32 biCompression;
[MarshalAs(UnmanagedType.I4)] public Int32 biSizeImage;
[MarshalAs(UnmanagedType.I4)] public Int32 biXPelsPerMeter;
[MarshalAs(UnmanagedType.I4)] public Int32 biYPelsPerMeter;
[MarshalAs(UnmanagedType.I4)] public Int32 biClrUsed;
[MarshalAs(UnmanagedType.I4)] public Int32 biClrImportant;
}
[StructLayout(LayoutKind.Sequential)] public struct BITMAPINFO
{
[MarshalAs(UnmanagedType.Struct, SizeConst=40)] public BITMAPINFOHEADER bmiHeader;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=1024)] public Int32[] bmiColors;
}
public delegate void FrameEventHandler(IntPtr lwnd, IntPtr lpVHdr);
// Public methods
public static object GetStructure(IntPtr ptr,ValueType structure)
{
return Marshal.PtrToStructure(ptr,structure.GetType());
}
public static object GetStructure(int ptr,ValueType structure)
{
return GetStructure(new IntPtr(ptr),structure);
}
public static void Copy(IntPtr ptr,byte[] data)
{
Marshal.Copy(ptr,data,0,data.Length);
}
public static void Copy(int ptr,byte[] data)
{
Copy(new IntPtr(ptr),data);
}
public static int SizeOf(object structure)
{
return Marshal.SizeOf(structure);
}
}
Web Camera Class
public class WebCamera
{
// Constructur
public WebCamera(IntPtr handle, int width,int height)
{
mControlPtr = handle;
mWidth = width;
mHeight = height;
}
// delegate for frame callback
public delegate void RecievedFrameEventHandler(byte[] data);
public event RecievedFrameEventHandler RecievedFrame;
private IntPtr lwndC; // Holds the unmanaged handle of the control
private IntPtr mControlPtr; // Holds the managed pointer of the control
private int mWidth;
private int mHeight;
private API32.FrameEventHandler mFrameEventHandler; // Delegate instance for the frame callback - must keep alive! gc should NOT collect it
// Close the web camera
public void CloseWebcam()
{
this.capDriverDisconnect(this.lwndC);
}
// start the web camera
public void StartWebCam()
{
byte[] lpszName = new byte[100];
byte[] lpszVer = new byte[100];
API32.capGetDriverDescriptionA(0, lpszName, 100,lpszVer, 100);
this.lwndC = API32.capCreateCaptureWindowA(lpszName, API32.WS_VISIBLE + API32.WS_CHILD, 0, 0, mWidth, mHeight, mControlPtr, 0);
if (this.capDriverConnect(this.lwndC, 0))
{
this.capPreviewRate(this.lwndC, 66);
this.capPreview(this.lwndC, true);
API32.BITMAPINFO bitmapinfo = new API32.BITMAPINFO();
bitmapinfo.bmiHeader.biSize = API32.SizeOf(bitmapinfo.bmiHeader);
bitmapinfo.bmiHeader.biWidth = 352;
bitmapinfo.bmiHeader.biHeight = 288;
bitmapinfo.bmiHeader.biPlanes = 1;
bitmapinfo.bmiHeader.biBitCount = 24;
this.capSetVideoFormat(this.lwndC, ref bitmapinfo, API32.SizeOf(bitmapinfo));
this.mFrameEventHandler = new API32.FrameEventHandler(FrameCallBack);
this.capSetCallbackOnFrame(this.lwndC, this.mFrameEventHandler);
API32.SetWindowPos(this.lwndC, 0, 0, 0, mWidth , mHeight , 6);
}
}
// private functions
private bool capDriverConnect(IntPtr lwnd, short i)
{
return API32.SendMessage(lwnd, API32.WM_CAP_DRIVER_CONNECT, i, 0);
}
private bool capDriverDisconnect(IntPtr lwnd)
{
return API32.SendMessage(lwnd, API32.WM_CAP_DRIVER_DISCONNECT, 0, 0);
}
private bool capPreview(IntPtr lwnd, bool f)
{
return API32.SendMessage(lwnd, API32.WM_CAP_SET_PREVIEW , f, 0);
}
private bool capPreviewRate(IntPtr lwnd, short wMS)
{
return API32.SendMessage(lwnd, API32.WM_CAP_SET_PREVIEWRATE, wMS, 0);
}
private bool capSetCallbackOnFrame(IntPtr lwnd, API32.FrameEventHandler lpProc)
{
return API32.SendMessage(lwnd, API32.WM_CAP_SET_CALLBACK_FRAME, 0, lpProc);
}
private bool capSetVideoFormat(IntPtr hCapWnd, ref API32.BITMAPINFO BmpFormat, int CapFormatSize)
{
return API32.SendMessage(hCapWnd, API32.WM_CAP_SET_VIDEOFORMAT, CapFormatSize, ref BmpFormat);
}
private void FrameCallBack(IntPtr lwnd, IntPtr lpVHdr)
{
API32.VIDEOHDR videoHeader = new API32.VIDEOHDR();
byte[] VideoData;
videoHeader = (API32.VIDEOHDR)API32.GetStructure(lpVHdr,videoHeader);
VideoData = new byte[videoHeader.dwBytesUsed];
API32.Copy(videoHeader.lpData ,VideoData);
if (this.RecievedFrame != null)
this.RecievedFrame (VideoData);
}
}