Lazy.Lib系列 - 摄像头图像采集、录像

前阵子与家里视频的时候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方法把图像保存到文件或剪切版,然后再读出来。。。


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
    }
}



关于视频聊天软件中对图像、文字等的传输、优化及界面这一块有兴趣可继续关注我的博客:)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值