C#.net編寫攝像頭驅動程序,用avicap32.dll


作者:zzmdegm

轉自:http://blog.csdn.net/zzmdegm/article/details/1915364


裝了攝像頭后一般會有 avicap32.dll文件,沒有一樣可以用這些代碼。

不需要在解決方案里面引用這個.dll文件。

下面有二種寫法的例子:

例一:

 

using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;

namespace WinVideo
{
    
/// <summary>
    
/// webcam 的摘要說明。
    
/// </summary>

    public class webcam
    
{
        
private const int WM_USER=0x400;
        
private const int WS_CHILD=0x40000000;
        
private const int WS_VISIBLE=0x10000000;
        
private const int WM_CAP_START=WM_USER;
        
private const int WM_CAP_STOP=WM_CAP_START + 68;
        
private const int WM_CAP_DRIVER_CONNECT=WM_CAP_START + 10;
        
private const int WM_CAP_DRIVER_DISCONNECT=WM_CAP_START + 11;
        
private const int WM_CAP_SAVEDIB=WM_CAP_START + 25;
        
private const int WM_CAP_GRAB_FRAME=WM_CAP_START + 60;
        
private const int WM_CAP_SEQUENCE=WM_CAP_START + 62;
        
private const int WM_CAP_FILE_SET_CAPTURE_FILEA=WM_CAP_START + 20;
        
private const int WM_CAP_SEQUENCE_NOFILE=WM_CAP_START+ 63;
        
private const int WM_CAP_SET_OVERLAY=WM_CAP_START+ 51;
        
private const int WM_CAP_SET_PREVIEW=WM_CAP_START+ 50;
        
private const int WM_CAP_SET_CALLBACK_VIDEOSTREAM=WM_CAP_START +6;
        
private const int WM_CAP_SET_CALLBACK_ERROR=WM_CAP_START +2;
        
private const int WM_CAP_SET_CALLBACK_STATUSA=WM_CAP_START +3;
        
private const int WM_CAP_SET_CALLBACK_FRAME=WM_CAP_START +5;
        
private const int WM_CAP_SET_SCALE=WM_CAP_START+ 53;
        
private const int WM_CAP_SET_PREVIEWRATE=WM_CAP_START+ 52;
        
private IntPtr hWndC;
        
private bool bStat = false;

        
private IntPtr mControlPtr;
        
private int mWidth;
        
private int mHeight;
        
private int mLeft;
        
private int mTop;
        
private string GrabImagePath="";
        
private string KinescopePath="";

        
/// <summary>
        
/// 初始化攝像頭
        
/// </summary>
        
/// <param name="handle">控件的句柄</param>
        
/// <param name="left">開始顯示的左邊距</param>
        
/// <param name="top">開始顯示的上邊距</param>
        
/// <param name="width">要顯示的寬度</param>
        
/// <param name="height">要顯示的長度</param>

        public webcam(IntPtr handle,int left,int top,int width,int height)
        
{
            mControlPtr
=handle;
            mWidth
=width;
            mHeight
=height;
            mLeft
=left;
            mTop
=top;
        }

        
"屬性設置"

        [DllImport(
"avicap32.dll")]
        
private static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName,int dwStyle,int x,int y,int nWidth,int nHeight,IntPtr hWndParent,int nID);

        [DllImport(
"avicap32.dll")]
        
private static extern int capGetVideoFormat(IntPtr hWnd,IntPtr psVideoFormat,int wSize );
        [DllImport(
"User32.dll")]
        
private static extern bool SendMessage(IntPtr hWnd,int wMsg,int wParam,long lParam);

        
/// <summary>
        
/// 開始顯示圖像
        
/// </summary>

        public void Start()
        
{
            
if(bStat)
                
return;

            bStat
=true;
            
byte[] lpszName=new byte[100];

            hWndC
=capCreateCaptureWindowA(lpszName,WS_CHILD|WS_VISIBLE ,mLeft,mTop,mWidth,mHeight,mControlPtr,0);

            
if (hWndC.ToInt32()!=0)
            
{
                SendMessage(hWndC,WM_CAP_SET_CALLBACK_VIDEOSTREAM,
0,0);
                SendMessage(hWndC,WM_CAP_SET_CALLBACK_ERROR,
0,0);
                SendMessage(hWndC,WM_CAP_SET_CALLBACK_STATUSA,
0,0);
                SendMessage(hWndC,WM_CAP_DRIVER_CONNECT,
0,0);
                SendMessage(hWndC,WM_CAP_SET_SCALE,
1,0);
                SendMessage(hWndC,WM_CAP_SET_PREVIEWRATE,
66,0);
                SendMessage(hWndC,WM_CAP_SET_OVERLAY,
1,0);
                SendMessage(hWndC,WM_CAP_SET_PREVIEW,
1,0);
            }

            
return;
        }


        
/// <summary>
        
/// 停止顯示
        
/// </summary>

        public void Stop()
        
{
            SendMessage(hWndC,WM_CAP_DRIVER_DISCONNECT,
0,0);
            bStat
=false;
        }


        
/// <summary>
        
/// 抓圖
        
/// </summary>
        
/// <param name="path">要保存bmp文件的路徑</param>

        public void GrabImage()
        
{
            IntPtr hBmp
=Marshal.StringToHGlobalAnsi(GrabImagePath);
            SendMessage(hWndC,WM_CAP_SAVEDIB,
0,hBmp.ToInt64());

        }


        
/// <summary>
        
/// 錄像
        
/// </summary>
        
/// <param name="path">要保存avi文件的路徑</param>

        public void Kinescope()
        
{
            IntPtr hBmp
=Marshal.StringToHGlobalAnsi(KinescopePath);
            SendMessage(hWndC,WM_CAP_FILE_SET_CAPTURE_FILEA,
0,hBmp.ToInt64());
            SendMessage(hWndC,WM_CAP_SEQUENCE,
0,0);
        }


        
/// <summary>
        
/// 停止錄像
        
/// </summary>

        public void StopKinescope()
        
{
            SendMessage(hWndC,WM_CAP_STOP,
0,0);
        }

    }

}

對該類的調用:

 

        public webcam wcam=null;
        
private void Form1_Load(object sender, System.EventArgs e)
        
{    
            start();
            
this.btnKinescopeStop.Enabled=false;
        }


        
private void btnStar_Click(object sender, System.EventArgs e)
        
{        
            wcam.Start();
        }


        
private void btnStop_Click(object sender, System.EventArgs e)
        
{
            wcam.Stop();
        }


        
private void btnSnapPic_Click(object sender, System.EventArgs e)
        
{
            
//得到路徑。例:d:/a.bmp
            string myPath=this.txtPath.Text;
            
if(myPath=="")
            
{
                MessageBox.Show(
"必須填寫路徑!");
                
return;
            }

            
else
            
{
                wcam.grabImagePath
=myPath;
                wcam.GrabImage();
                MessageBox.Show(
"截圖成功!");
            }

        }

        
public void start()
        
{
            
//以panel1為容器顯示視頻內容
            wcam=new webcam(panel1.Handle,0,0,this.panel1.Width,this.panel1.Height);        
            wcam.Start();
        }


        
private void panel1_SizeChanged(object sender, System.EventArgs e)
        
{
            wcam.Stop();
            wcam.Height
=this.panel1.Height;
            wcam.Width
=this.panel1.Width;
            wcam.Start();
        }

        
        
private delegate void delegateKinescope();
        
private void btnKinescopeBegin_Click(object sender, System.EventArgs e)
        
{
            
//得到路徑。例:d:/a.avi
            string myPath=this.txtPath.Text;
            
if(myPath=="")
            
{
                MessageBox.Show(
"必須填寫路徑!");
                
return;
            }

            
else
            
{
                wcam.kinescopePath
=myPath;
                delegateKinescope myK
=new delegateKinescope(wcam.Kinescope);
                Thread threadKinescope
=new Thread(new ThreadStart(myK));
                threadKinescope.Start();
                
this.btnKinescopeBegin.Enabled=false;
                
this.btnKinescopeStop.Enabled=true;
            }
        
        }

        
public void starKinescope()
        
{
            delegateKinescope myK
=new delegateKinescope(wcam.Kinescope);
        }


        
private void btnKinescopeStop_Click(object sender, System.EventArgs e)
        
{
            wcam.StopKinescope();
            
this.btnKinescopeBegin.Enabled=true;
            
this.btnKinescopeStop.Enabled=false;            
        }

例二:

 

using System;
using System.Runtime.InteropServices;

namespace webcam
{
    
/**//// 
    
/// avicap 的摘要說明。
    
/// 

    public class showVideo
    
{
        
// showVideo 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 showVideo.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];
 
            showVideo.capGetDriverDescriptionA(
0, lpszName, 100,lpszVer, 100);
            
this.lwndC = showVideo.capCreateCaptureWindowA(lpszName, showVideo.WS_VISIBLE + showVideo.WS_CHILD, 00, mWidth, mHeight, mControlPtr, 0);
 
            
if (this.capDriverConnect(this.lwndC, 0))
            
{
                
this.capPreviewRate(this.lwndC, 66);
                
this.capPreview(this.lwndC, true);
                showVideo.BITMAPINFO bitmapinfo 
= new showVideo.BITMAPINFO(); 
                bitmapinfo.bmiHeader.biSize 
= showVideo.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, showVideo.SizeOf(bitmapinfo));
                
this.mFrameEventHandler = new showVideo.FrameEventHandler(FrameCallBack);
                
this.capSetCallbackOnFrame(this.lwndC, this.mFrameEventHandler);
                showVideo.SetWindowPos(
this.lwndC, 000, mWidth , mHeight , 6);
            }
 
        }


        
// private functions
        private bool capDriverConnect(IntPtr lwnd, short i)
        
{
            
return showVideo.SendMessage(lwnd, showVideo.WM_CAP_DRIVER_CONNECT, i, 0);
        }

        
private bool capDriverDisconnect(IntPtr lwnd)
        
{
            
return showVideo.SendMessage(lwnd, showVideo.WM_CAP_DRIVER_DISCONNECT, 00);
        }

        
private bool capPreview(IntPtr lwnd, bool f)
        
{return showVideo.SendMessage(lwnd, showVideo.WM_CAP_SET_PREVIEW , f, 0);}

        
private bool capPreviewRate(IntPtr lwnd, short wMS)
        
{
            
return showVideo.SendMessage(lwnd, showVideo.WM_CAP_SET_PREVIEWRATE, wMS, 0);
        }

 
        
private bool capSetCallbackOnFrame(IntPtr lwnd, showVideo.FrameEventHandler lpProc)
        
{
            
return showVideo.SendMessage(lwnd, showVideo.WM_CAP_SET_CALLBACK_FRAME, 0, lpProc);
        }


        
private bool capSetVideoFormat(IntPtr hCapWnd, ref showVideo.BITMAPINFO BmpFormat, int CapFormatSize)
        
{
            
return showVideo.SendMessage(hCapWnd, showVideo.WM_CAP_SET_VIDEOFORMAT, CapFormatSize, ref BmpFormat);
        }


        
private void FrameCallBack(IntPtr lwnd, IntPtr lpVHdr)
        
{
            showVideo.VIDEOHDR videoHeader 
= new showVideo.VIDEOHDR();
            
byte[] VideoData;
            videoHeader 
= (showVideo.VIDEOHDR)showVideo.GetStructure(lpVHdr,videoHeader);
            VideoData 
= new byte[videoHeader.dwBytesUsed];
            showVideo.Copy(videoHeader.lpData ,VideoData);
            
if (this.RecievedFrame != null)
                
this.RecievedFrame (VideoData);
        }

    }


}

調用代碼:

 

        WebCamera wc;

        
private void Form1_Load(object sender, System.EventArgs e)
        
{
            b_play.Enabled 
= false;
            b_stop.Enabled 
= true;
            panelPreview.Size 
= new Size(330,330);
            wc 
= new WebCamera( panelPreview.Handle,panelPreview.Width,panelPreview.Height);
            wc.StartWebCam();
        }


        
private void button1_Click(object sender, System.EventArgs e)
        
{
            b_play.Enabled 
= false;
            b_stop.Enabled 
= true;
            panelPreview.Size 
= new Size(330,330);
            wc 
= new WebCamera( panelPreview.Handle,panelPreview.Width,panelPreview.Height);
            wc.StartWebCam();
        }


        
private void b_stop_Click(object sender, System.EventArgs e)
        
{
            b_play.Enabled 
= true;
            b_stop.Enabled 
= false;
            wc.CloseWebcam();
        }


另提供一種把顯示的視頻轉成Image對象的方法:
using System.Windows.Forms;
//捕獲視頻成Image對象
public Image CatchVideo()
{
//SendMessage(this.hHwnd, 0x41e, 0, 0);
SendMessage(this.hWndC, 0x41e, 0, 0);

IDataObject obj1 = Clipboard.GetDataObject();
Image getIma = null;
if (obj1.GetDataPresent(typeof(Bitmap)))
{
Image image1 = (Image)obj1.GetData(typeof(Bitmap));
getIma = image1;
}
return getIma;
}


例一在轉到2005后會報錯:
檢測到 PInvokeStackImbalance
Message: 對 PInvoke 函數“WindowsApplication1!UserLib.Device.PCCamera::SendMessage”的調用導致堆棧不對稱。原因可能是托管的 PInvoke 簽名與非托管的目標簽名不匹配。請檢查 PInvoke 簽名的調用約定和參數與非托管的目標簽名是否匹配。
錯誤首次發生在這一行代碼: SendMessage(hWndC, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
經過分析發現所有調用SendMessage函數的地方都會出現以上錯誤
於是查看DLLImport:
[DllImport("User32.dll")]
private static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, long lParam);
原來是因為WinAPI的long類型是32位的,而C#的long是64位的,這就導致堆棧不對稱,引發錯誤.

原因:(http://discuss.develop.com/archives/wa.exe?A2=ind0512c&amp;L=dotnet-winforms&amp;D=0&amp;T=0&amp;P=8094)
在.NET2.0中加入了MDA(managed debugging assistant), 在平台調用時后會檢查棧的指針, 如果發現不平衡, 就會拋出PInvokeStackImbalance異常; 而在.NET1.1中不會檢查, 所以不會捕獲到異常, 但在運行時會導致不穩定.

解決方法:
將最后一個”long lParam” 改為 “int wParam”, 因為C#中int是32位的. 並且將之后有涉及到SendMessage函數的參數適當地轉成int型就可以了.
但是, 之后查閱了資料http://www.pinvoke.net/default.aspx/user32/SendMessage.html
發現先前的解決方案還有不合適的地方, 應該將其中的 ”wParm” 和 ”lParm” 參數的類型都轉成IntPtr類型,並且將后面涉及到的參數的 ”0” 改為 “IntPtr.Zero”. 因為如果使用int類型,那么這段代碼在64位的Windows上面將會無法正常運行.

總結:
我們在調用WinAPI時要特別小心, 因為WinAPI和C#的數據類型不是完全一樣, 就好像在WinAPI中的long類型在C#中就是int類型, 如果沒有處理好類型問題, 就很可能會導致堆棧的不平衡,引發PInvokeStackImbalance錯誤, 但是這類錯誤在.NET1.1下不會被暴露出來, 所以在從.NET1.1升級到.NET2.0時要特別注意此類問題.

另提供一種把顯示的視頻轉成Image對象的方法:
using System.Windows.Forms;
//捕獲視頻成Image對象
public Image CatchVideo()
{


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com