VFW实例程序。简单 “视频捕获”,没有压缩、线程、保存、等更多的功能
2011-08-25 18:47 沐海 阅读(2323) 评论(8) 收藏 举报using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace webcam
{
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 );
//下面这些都是系统USER32.DLL里面的函数 SendMessage使用的消息的类型。是指定的16进制值。
//这里的字母的含义是用来让我们明白的。重点在于,其后面的16进制值在系统默认中代表什么含义。要和系统默认表值对应。
// 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;
//VideoHdr 结构 定义了视频数据块的头信息,在编写回调函数时常用到其数据成员lpData(指向数据缓存的指针)和dwBufferLength(数据缓存的大小)。
//视频帧到缓存的捕获则需要应用回调函数和相应的数据块结构 VIDEOHDR
[StructLayout(LayoutKind.Sequential)] public struct VIDEOHDR
{
[MarshalAs(UnmanagedType.I4)]
public int lpData; /* 指向数据缓存的指针 */
[MarshalAs(UnmanagedType.I4)]
public int dwBufferLength; /* 数据缓存的大小 */
[MarshalAs(UnmanagedType.I4)]
public int dwBytesUsed; /* Bytes actually used */
[MarshalAs(UnmanagedType.I4)]
public int dwTimeCaptured; /* Milliseconds from start of stream */
[MarshalAs(UnmanagedType.I4)]
public int dwUser; /* for client's use */
[MarshalAs(UnmanagedType.I4)]
public int dwFlags; /* assorted flags (see defines) */
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public int[] dwReserved; /* reserved for driver */
}
//BitmapInfoHeader定义了位图的头部信息
[StructLayout(LayoutKind.Sequential)] public struct BITMAPINFOHEADER //MS定义的结构类型。
{
[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;
}
//BitmapInfo 位图信息
[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());
}
//提供值类型的基类。
// ValueType 用更合适的值类型实现重写 Object 中的虚方法。请参见从 ValueType 继承的 Enum。
//数据类型分隔为值类型和引用类型。值类型要么是堆栈分配的,要么是在结构中以内联方式分配的。
//引用类型是堆分配的。引用类型和值类型都是从最终的基类 Object 派生出来的。
//当值类型需要充当对象时,就在堆上分配一个包装(该包装能使值类型看上去像引用对象一样),
//并且将该值类型的值复制给它。该包装被加上标记,以便系统知道它包含一个值类型。这个进程称为装箱,其反向进程称为取消装箱。//装箱和取消装箱能够使任何类型像对象一样进行处理。
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; //保存无符号句柄
private IntPtr mControlPtr; //保存管理指示器
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);//创建缓冲区//取得视频采集设备描述信息
//参数:其中,参数0指明设备的索引号,参数lpszName指明设备的名称缓冲区的地址,参数100指明设备的名称缓冲区的大小,//参数lpszVer指明设备的描述缓冲区的地址,参数100指明设备的描述缓冲区的大小。
this.lwndC = showVideo.capCreateCaptureWindowA(lpszName, showVideo.WS_VISIBLE + showVideo.WS_CHILD, 0, 0, mWidth, mHeight, mControlPtr, 0);
//,参数lpszWindowName指明窗口的名称,参数dwStyle指明窗口的风格,参数x、y、nWidth和nHeight指明窗口的位置和大小,//参数hwndParent指明父窗口的句柄,参数nID指明窗口标识。
//函数返回值Hwnd类型,即摄像设备的句柄,以后的操作基本上都是针对设备句柄进行的.
//:= CapCreateCaptureWindow
//( PChar(‘KruwoSoft'), //捕获窗口的名字
//WS_CHILD or WS_VISIBLE,//窗口样式
//0, //X坐标
//0, //Y坐标
//gCapVideoArea.Width, //窗口宽
//gCapVideoArea.Height, //窗口高
//gCapVideoArea.Handle, //窗口句柄
//0); //一般为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;//Specifies the number of planes for the target device. This value must be set to 1
//为目标设备指定的平面值,不存在多维度。
bitmapinfo.bmiHeader.biBitCount = 24;
//biBitCount成员的结构决定了BITMAPINFOHEADER的比特数定义的最大数量,每一个像素色彩的位图。
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, 0, 0, 0, mWidth , mHeight , 6);
}
}
// private functions
private bool capDriverConnect(IntPtr lwnd, short i)
{
return showVideo.SendMessage(lwnd, showVideo.WM_CAP_DRIVER_CONNECT, i, 0);//参数 句柄,1024+100,0,0,
}
// hWnd:窗口句柄。 wMsg:将要发送的消息。 wParam、lParam:消息的参数,每个消息都有两个参数,参数设置由发送的消息而定。
//SendMessage(hHwnd, 0x40a, 0, 0),第一个参数是句柄,第二个参数表示的是一个消息,是哪个消息呢?后两个参数是到底为消息提供什么样的东西呢?
//回答:0x40a 表示 WM_CAP_DRIVER_CONNECT消息,后面两个参数表示消息附带的两个参数,每个消息都有两个参数的。按照VFW的规定,//第一个参数是捕获设备的索引。这儿是0,表示你的第一个捕获设备。第二参数恒为0。为什么?VFW就是这么规定的。
//具体参见MSND中Enumerating Installed Capture Drivers相关章节。就OK了。
private bool capDriverDisconnect(IntPtr lwnd)
{
return showVideo.SendMessage(lwnd, showVideo.WM_CAP_DRIVER_DISCONNECT, 0, 0);
}
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);
}
//设置预览时的帧频率
// hWnd:窗口句柄。 wMsg:将要发送的消息。 帧率86,0
//设置在PREVIEW模式下设定视频窗口的刷新率 设置每40毫秒显示一帧,即显示帧速为每秒25帧
private bool capSetCallbackOnFrame(IntPtr lwnd, showVideo.FrameEventHandler lpProc)
{
return showVideo.SendMessage(lwnd, showVideo.WM_CAP_SET_CALLBACK_FRAME, 0, lpProc);
}
//回调函数。当在这个句柄中数据接收到一帧时。WM_CAP_SET_CALLBACK_FRAME时。发消息给当前句柄。调用回调函数。
// this.capSetVideoFormat(this.lwndC, ref bitmapinfo, showVideo.SizeOf(bitmapinfo));
//返回对象的非托管大小(以字节为单位)
//这里使用 REF保证图片改变时全部相关信息对应改变。
//ref 关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。//若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。
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();//VideoHdr 结构定义
byte[] VideoData;
videoHeader = (showVideo.VIDEOHDR)showVideo.GetStructure(lpVHdr,videoHeader);//得到这样的一个结构 内容 参数:句柄,值类型(结构)
//调用Marshal.PtrToStructure(ptr,structure.GetType());方法
//将数据从非托管内存块封送到托管对象。
//指向非托管内存块的指针。lpVHdr
//将数据复制到其中的对象。这必须是格式化类的实例。videoHeader
VideoData = new byte[videoHeader.dwBytesUsed];//实际使用的大小,初始化Byte缓冲大小定义
showVideo.Copy(videoHeader.lpData, VideoData);//从非托管内存指针复制到托管数组。
//指向数据缓存的指针
if (this.RecievedFrame != null)
this.RecievedFrame (VideoData);
}
}
}
本人声明:
个人主页:沐海(http://www.cnblogs.com/mahaisong)
以上文章都是经过本人设计实践和阅读其他文档得出。如果需要探讨或指教可以留言或加我QQ!欢迎交流!
浙公网安备 33010602011771号