'C# Camera Capture with AviCap32.dll
I'm trying to capture images from my webcam by using avicap32.dll. An event should be called when a new image is available. The problem is that it only shows a black image with a few rows of colored pixels at the bottom (Sample image).
I DON'T want to use any other libraries like Aforge, DirectShow, OpenCV or something similar.
Here is my code:
public class CameraCapture2
{
[DllImport("user32", EntryPoint = "SendMessage")]
static extern bool SendMessage(int hWnd, uint wMsg, int wParam, int lParam);
[DllImport("user32", EntryPoint = "SendMessage")]
static extern int SendBitmapMessage(int hWnd, uint wMsg, int wParam, ref BITMAPINFO lParam);
[DllImport("user32", EntryPoint = "SendMessage")]
static extern int SendHeaderMessage(int hWnd, uint wMsg, int wParam, CallBackDelegate lParam);
[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindow")]
static extern int capCreateCaptureWindow(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);
delegate void CallBackDelegate(IntPtr hwnd, ref VIDEOHEADER hdr);
CallBackDelegate delegateFrameCallBack;
AutoResetEvent autoEvent = new AutoResetEvent(false);
Thread frameThread;
public event EventHandler<NewFrameEventArgs> NewFrame;
public int FPS = 0;
public const int BitsPerPixel = 24; //RGB (by changing this value, change the other PixelFormats)
public int frameWidth = 0;
public int frameHeight = 0;
public int camID = 0;
int camHwnd, parentHwnd;
bool bStart = false;
object threadLock = new object();
int preferredFPSms;
public CameraCapture2(int frameWidth, int frameHeight, int preferredFPS, int camID, int parentHwnd)
{
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
this.parentHwnd = parentHwnd;
this.camID = camID;
PreferredFPS = preferredFPS;
delegateFrameCallBack = FrameCallBack;
}
public void Start()
{
try
{
camHwnd = capCreateCaptureWindow("WebCam", 0, 0, 0, frameWidth, frameHeight, parentHwnd, camID);
// connect to the device
if (SendMessage(camHwnd, WM_CAP.WM_CAP_DRIVER_CONNECT, 0, 0))
{
BITMAPINFO bInfo = new BITMAPINFO();
bInfo.bmiHeader = new BITMAPINFOHEADER();
bInfo.bmiHeader.biSize = (uint)Marshal.SizeOf(bInfo.bmiHeader);
bInfo.bmiHeader.biWidth = frameWidth;
bInfo.bmiHeader.biHeight = frameHeight;
bInfo.bmiHeader.biPlanes = 1;
bInfo.bmiHeader.biBitCount = BitsPerPixel; // bits per pixel, 24 - RGB
//Enable preview mode. In preview mode, frames are transferred from the
//capture hardware to system memory and then displayed in the capture
//window using GDI functions.
SendMessage(camHwnd, WM_CAP.WM_CAP_SET_PREVIEW, 1, 0);
SendMessage(camHwnd, WM_CAP.WM_CAP_SET_PREVIEWRATE, 34, 0); // sets the frame display rate in preview mode
SendBitmapMessage(camHwnd, WM_CAP.WM_CAP_SET_VIDEOFORMAT, Marshal.SizeOf(bInfo), ref bInfo);
frameThread = new Thread(new ThreadStart(this.FrameGrabber));
bStart = true; // First, set variable
frameThread.Priority = ThreadPriority.Lowest;
frameThread.Start(); // Only then put thread to the queue
}
else
throw new Exception("Cannot connect to device");
}
catch (Exception e)
{
Stop();
throw new Exception("Error: " + e.Message);
}
}
public void Stop()
{
try
{
bStart = false;
Set();
SendMessage(camHwnd, WM_CAP.WM_CAP_DRIVER_DISCONNECT, 0, 0);
}
catch { }
}
private void FrameGrabber()
{
while (bStart) // if worker active thread is still required
{
try
{
// get the next frame. This is the SLOWEST part of the program
SendMessage(camHwnd, WM_CAP.WM_CAP_GRAB_FRAME_NOSTOP, 0, 0);
SendHeaderMessage(camHwnd, WM_CAP.WM_CAP_SET_CALLBACK_FRAME, 0, delegateFrameCallBack);
}
catch (Exception excep)
{
this.Stop(); // stop the process
throw new Exception("Capturing error:\r\n" + excep.Message);
}
}
}
/// <summary>
/// Allow waiting worker (FrameGrabber) thread to proceed
/// </summary>
public void Set()
{
autoEvent.Set();
}
private void FrameCallBack(IntPtr hwnd, ref VIDEOHEADER hdr)
{
if (NewFrame != null)
{
//Bitmap bmp = new Bitmap(frameWidth, frameHeight, frameWidth * 3, System.Drawing.Imaging.PixelFormat.Format24bppRgb, hdr.lpData);
byte[] _imageTemp = new byte[hdr.dwBufferLength];
Marshal.Copy(hdr.lpData, _imageTemp, 0, (int)hdr.dwBufferLength);
Bitmap bmp = new Bitmap(new System.IO.MemoryStream(WriteBitmapFile(frameWidth, frameHeight, _imageTemp)));
NewFrame(this, new NewFrameEventArgs(bmp));
}
// block thread for preferred milleseconds
if (preferredFPSms == 0)
autoEvent.WaitOne();
else
autoEvent.WaitOne(preferredFPSms, false);
}
private static byte[] WriteBitmapFile(int width, int height, byte[] imageData)
{
using (var stream = new System.IO.MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb))
{
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height)
, System.Drawing.Imaging.ImageLockMode.WriteOnly
, bmp.PixelFormat
);
Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);
bmp.UnlockBits(bmpData);
if (bmp == null)
return null;
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); // get bitmap bytes
return ms.ToArray();
} // End Using stream
}
}
public void ShowVideoDialog()
{
SendMessage(camHwnd, WM_CAP.WM_CAP_DLG_VIDEODISPLAY, 0, 0);
}
public int PreferredFPS
{
get { return 1000 / preferredFPSms; }
set
{
if (value == 0)
preferredFPSms = 0;
else if (value > 0 && value <= 30)
{
preferredFPSms = 1000 / value;
}
}
}
}
public class NewFrameEventArgs
{
public Bitmap Frame;
public NewFrameEventArgs(Bitmap frame)
{
Frame = frame;
}
}
Most of this code is from Codeproject
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
