VFW实例程序。简单 “视频捕获”,没有压缩、线程、保存、等更多的功能
2011-08-25 18:47 沐海 阅读(2293) 评论(8) 编辑 收藏 举报1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | 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!欢迎交流!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库