Ido Samuelson

Notes, thoughts and exceptions

<August 2008>
SuMoTuWeThFrSa
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456


Navigation

Blogs I read

Articles

My WorkSpaces

Subscriptions

News

Only 10 kind of people in the world. Those who understand binary and those who don't.

Post Categories

Article Categories



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);
     }
}

posted on Tuesday, May 18, 2004 8:19 AM by snick





Powered by Dot Net Junkies, by Telligent Systems