前阵子与家里视频的时候QQ有点问题,故而自己写了一个远程视频聊天的小软件,其中封装了一个类Lazy.Lib.IO.Camera,用于通过摄像头进行图像采集、录像。主要包含以下公共属性及方法。
公共属性及事件
FrameReceived:摄像头返回帧事件。
PreviewRate:摄像头预览频率。毫秒。
BitmapInfo:返回关于摄像头成像的一些信息,比如图像大小等。
公共方法
Start():开始采集。
Stop():停止采集。
Image2File(string aImagePath):保存图像到文件。
Image2Clipboard():保存图像到剪贴板。
StartRecord(string aVideoPath):开始录像。
StopRecord():停止录像。
嵌套类
FrameReceivedEventArgs:摄像头返回帧事件的参数。
其中返回帧的事件中有一个公共属性Data,包含了图像的像素信息,我们需要根据这些像素信息进行成像,这个过程比较麻烦,因为不同摄像头返回的图像格式可能不一样,成像的方法也不一样,这里不涉及这方面的讨论,有兴趣的童鞋可以google或百度一下biCompression,看看关于图像的不同编码规则。有时间后面再整理出一个对返回数据解码成图像的函数。
如果不知道怎么根据帧事件中的Data进行成像,一个workaround的方法是使用Camera类中的Image2File或Image2Clipboard方法把图像保存到文件或剪切版,然后再读出来。。。
公共属性及事件
FrameReceived:摄像头返回帧事件。
PreviewRate:摄像头预览频率。毫秒。
BitmapInfo:返回关于摄像头成像的一些信息,比如图像大小等。
公共方法
Start():开始采集。
Stop():停止采集。
Image2File(string aImagePath):保存图像到文件。
Image2Clipboard():保存图像到剪贴板。
StartRecord(string aVideoPath):开始录像。
StopRecord():停止录像。
嵌套类
FrameReceivedEventArgs:摄像头返回帧事件的参数。
其中返回帧的事件中有一个公共属性Data,包含了图像的像素信息,我们需要根据这些像素信息进行成像,这个过程比较麻烦,因为不同摄像头返回的图像格式可能不一样,成像的方法也不一样,这里不涉及这方面的讨论,有兴趣的童鞋可以google或百度一下biCompression,看看关于图像的不同编码规则。有时间后面再整理出一个对返回数据解码成图像的函数。
如果不知道怎么根据帧事件中的Data进行成像,一个workaround的方法是使用Camera类中的Image2File或Image2Clipboard方法把图像保存到文件或剪切版,然后再读出来。。。
Camera类源代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Lazy.Lib.IO
{
/// <summary>
/// 通过摄像头进行图像采集、录像。
/// </summary>
public class Camera
{
#region Field
private Form _frmScreen;
private IntPtr _lpCamera;
private int _width = 640;
private int _height = 480;
private FrameEventHandler _frameEventHandler = null;
/// <summary>
/// 摄像头返回帧事件。
/// </summary>
public event EventHandler<FrameReceivedEventArgs> FrameReceived;
#endregion
#region Properties
private short _previewRate = 200;
/// <summary>
/// 摄像头预览频率。毫秒。
/// </summary>
public short PreviewRate
{
get
{
return _previewRate;
}
set
{
if (_lpCamera != IntPtr.Zero && _previewRate != value)
SendMessage(_lpCamera, WM_CAP_SET_PREVIEWRATE, value, 0);
_previewRate = value;
}
}
private BITMAPINFO _bitmapInfo;
public BITMAPINFO BitmapInfo { get { return _bitmapInfo; } }
#endregion
#region Constructor
public Camera()
{
}
#endregion
#region Camera Buttons
/// <summary>
/// 开始采集。
/// </summary>
public void Start()
{
//
if (_frmScreen == null)
{
_frmScreen = new Form();
_frmScreen.FormBorderStyle = FormBorderStyle.None;
_frmScreen.MaximizeBox = false;
_frmScreen.MinimizeBox = false;
_frmScreen.ShowInTaskbar = false;
_frmScreen.BackColor = System.Drawing.Color.White;
_frmScreen.TopMost = true;
_frmScreen.ClientSize = new System.Drawing.Size(1, 1);
_frmScreen.Show();
_frmScreen.ClientSize = new System.Drawing.Size(1, 1);
_frmScreen.Location = new System.Drawing.Point(2, 2);
}
//
byte[] lpszName = new byte[100];
byte[] lpszVer = new byte[100];
CapGetDriverDescriptionA(0, lpszName, 100, lpszVer, 100);
_lpCamera = CapCreateCaptureWindowA(lpszName, WS_CHILD | WS_VISIBLE, 0, 0, _width, _height, _frmScreen.Handle, 0);
if (SendMessage(_lpCamera, WM_CAP_DRIVER_CONNECT, 0, 0))
{
bool returnValue = false;
//
_bitmapInfo = new BITMAPINFO();
//
returnValue = SendMessage(_lpCamera, WM_CAP_SET_PREVIEWRATE, _previewRate, 0);
returnValue = SendMessage(_lpCamera, WM_CAP_SET_PREVIEW, true, 0);
returnValue = SendMessage(_lpCamera, WM_CAP_GET_VIDEOFORMAT, Marshal.SizeOf(_bitmapInfo), ref _bitmapInfo);
#region Try Best Quality
//bitmapInfo.bmiHeader.biWidth = 640;
//bitmapInfo.bmiHeader.biHeight = 480;
//bitmapInfo.bmiHeader.biSizeImage = bitmapInfo.bmiHeader.biWidth * bitmapInfo.bmiHeader.biHeight * 2;
//bool success = SendMessage(_lpCamera, WM_CAP_SET_VIDEOFORMAT, Marshal.SizeOf(bitmapInfo), ref bitmapInfo);
//if (!success)
//{
// bitmapInfo.bmiHeader.biWidth = 640;
// bitmapInfo.bmiHeader.biHeight = 480;
// bitmapInfo.bmiHeader.biSizeImage = bitmapInfo.bmiHeader.biWidth * bitmapInfo.bmiHeader.biHeight * 3;
// success = SendMessage(_lpCamera, WM_CAP_SET_VIDEOFORMAT, Marshal.SizeOf(bitmapInfo), ref bitmapInfo);
//}
//if (!success)
//{
// bitmapInfo.bmiHeader.biWidth = 640;
// bitmapInfo.bmiHeader.biHeight = 480;
// bitmapInfo.bmiHeader.biSizeImage = 0;
// success = SendMessage(_lpCamera, WM_CAP_SET_VIDEOFORMAT, Marshal.SizeOf(bitmapInfo), ref bitmapInfo);
//}
#endregion
_frameEventHandler = new FrameEventHandler(FrameCallBack);
returnValue = SendMessage(_lpCamera, WM_CAP_SET_CALLBACK_FRAME, 0, _frameEventHandler);
}
}
/// <summary>
/// 停止采集。
/// </summary>
public void Stop()
{
SendMessage(_lpCamera, WM_CAP_DRIVER_DISCONNECT, 0, 0);
}
/// <summary>
/// 保存图像到文件。
/// </summary>
/// <param name="aImagePath"></param>
public void Image2File(string aImagePath)
{
IntPtr lpFilePath = Marshal.StringToHGlobalAnsi(aImagePath);
SendMessage(_lpCamera, WM_CAP_SAVEDIB, 0, lpFilePath.ToInt32());
}
/// <summary>
/// 保存图像到剪贴板。
/// </summary>
public void Image2Clipboard()
{
SendMessage(_lpCamera, WM_CAP_EDIT_COPY, 0, 0);
}
/// <summary>
/// 开始录像。
/// </summary>
/// <param name="aVideoPath"></param>
public void StartRecord(string aVideoPath)
{
IntPtr lpFilePath = Marshal.StringToHGlobalAnsi(aVideoPath);
SendMessage(_lpCamera, WM_CAP_FILE_SET_CAPTURE_FILEA, 0, lpFilePath.ToInt32());
SendMessage(_lpCamera, WM_CAP_SEQUENCE, 0, 0);
}
/// <summary>
/// 停止录像。
/// </summary>
public void StopRecord()
{
SendMessage(_lpCamera, WM_CAP_STOP, 0, 0);
}
#endregion
#region Debuging Buttons
internal void Debug()
{
}
#endregion
#region Private Method
private void FrameCallBack(IntPtr aHandle, ref VIDEOHDR aVideoHeader)
{
byte[] videoData = new byte[aVideoHeader.dwBytesUsed];
Marshal.Copy(new IntPtr(aVideoHeader.lpData), videoData, 0, videoData.Length);
if (this.FrameReceived != null)
this.FrameReceived(this, new FrameReceivedEventArgs(videoData));
}
#endregion
#region PInvoke
#region Constant N Type
internal const int SWP_NOMOVE = 0x2;
internal const int SWP_NOZORDER = 0x4;
internal const int WS_CHILD = 0x40000000;
internal const int WS_VISIBLE = 0x10000000;
internal const int WM_USER = 0x400;
internal const int WM_CAP_DRIVER_CONNECT = WM_USER + 10;
internal const int WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
internal const int WM_CAP_SET_CALLBACK_FRAME = WM_USER + 5;
internal const int WM_CAP_SET_PREVIEW = WM_USER + 50;
internal const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
internal const int WM_CAP_SET_VIDEOFORMAT = WM_USER + 45;
internal const int WM_CAP_GET_VIDEOFORMAT = WM_CAP_START + 44;
internal const int WM_CAP_START = WM_USER;
internal const int WM_CAP_SAVEDIB = WM_CAP_START + 25;
internal const int WM_CAP_STOP = WM_CAP_START + 68;
internal const int WM_CAP_SEQUENCE = WM_CAP_START + 62;
internal const int WM_CAP_FILE_SET_CAPTURE_FILEA = WM_CAP_START + 20;
internal const int WM_CAP_EDIT_COPY = WM_CAP_START + 30;
internal delegate void FrameEventHandler(IntPtr lwnd, ref VIDEOHDR videoHeader);
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
[MarshalAs(UnmanagedType.Struct, SizeConst = 40)]
public BITMAPINFOHEADER bmiHeader;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
public Int32[] bmiColors;
}
[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 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;
}
#endregion
[DllImport("avicap32.dll", EntryPoint = "capGetDriverDescriptionA")]
internal static extern bool CapGetDriverDescriptionA(short wDriver, byte[] lpszName, int cbName, byte[] lpszVer, int cbVer);
[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
internal static extern IntPtr CapCreateCaptureWindowA(
byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
[DllImport("avicap32.dll", EntryPoint = "capGetVideoFormat")]
internal static extern int CapGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);
[DllImport("User32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam);
[DllImport("User32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, int lParam);
[DllImport("User32.dll")]
internal static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
[DllImport("User32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, FrameEventHandler lParam);
[DllImport("User32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref BITMAPINFO lParam);
#endregion
#region Nested Types
/// <summary>
/// 摄像头返回帧事件的参数。
/// </summary>
public class FrameReceivedEventArgs : EventArgs
{
private byte[] _data;
public byte[] Data { get { return _data; } }
public FrameReceivedEventArgs(byte[] aData) { _data = aData; }
}
#endregion
}
}