C# 使用AForge调用笔记本摄像头拍照及录像

发布时间 2023-05-05 17:05:45作者: 流纹

1、添加引用

1、官网下载链接:http://www.aforgenet.com/framework/downloads.html
2、通过管理Nuget程序包可直接添加以下引用,当然这里并没有全部用上,而只是用上了一部分

在添加完这些引用之后VS的工具箱当中会多出AForge.NET相关的一些控件,而实现调用摄像头就需要使用到这些控件

2、AForge相关类库介绍

AForge.dll 是框架的核心基础类库,为其他类库提供服务。
AForge.Controls.dll 包含AForge.Net的UI控件,主要用于页面显示。
AForge.Imaging.dll 主要是框架中用于图像处理的类库,主要负责图像的处理
AForge.Video.dll 主要是框架中对视频处理的类库。
AForge.Video.DirectShow.dll 主要是通过DirectShow接口访问视频资源的类库。
AForge.Video.FFMPEG.dll 是一个还未正式发布的类库,通过FFMPEG类库对视频进行读写。

3、WinForm界面实现

这里解释以下界面组成及控件使用
此界面分上下两部分
上中半部分由一个GroupBox包裹一个AForge.NET工具包下VideoSourcePlayer控件命名为vsp_Panel组成
中间部分由一个Label,一个ComBox下拉框cb_CameraSelect,两个Button按钮btn_Connectbtn_Close组成
下半部分由分左右俩部分
下左部分由一个GroupBox包裹一个AForge.NET工具包下PictureBox控件命名为pb_BoxPanel组成
下右部分由由一个GroupBox包裹5个Button按钮btn_StartVideo、btn_PauseVideo、btn_EndVideo、btn_Capture、btn_Save。以及两个label,其中录制时间: 这个label命名无所谓后续用不到,而另外一个默认显示为00:00:00的label命名为lab_Time

4、属性

       #region 属性
        /// <summary>
        /// 设备源
        /// 用来操作摄像头
        /// </summary>
        private AForge.Video.DirectShow.VideoCaptureDevice videoCapture;
        /// <summary>
        /// 摄像头设备集合
        /// </summary>
        private AForge.Video.DirectShow.FilterInfoCollection infoCollection;
        /// <summary>
        /// 图片
        /// </summary>
        private System.Drawing.Bitmap imgMap;
        /// <summary>
        /// 文件保存路径
        /// </summary>
        private readonly string filePath = $"D:\\VS主题\\图片\\";
        /// <summary>
        /// 文件名称
        /// </summary>
        private readonly string fileName = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}";
        /// <summary>
        /// 用与把每一帧图像写入到视频文件
        /// </summary>
        private AForge.Video.FFMPEG.VideoFileWriter videoWriter;
        /// <summary>
        /// 是否开始录像
        /// </summary>
        private bool IsStartVideo = false;
        /// <summary>
        /// 写入次数
        /// </summary>
        private int tick_num = 0;
        /// <summary>
        /// 录制多少小时,只是为了定时间计算录制时间使用
        /// </summary>
        private int Hour = 0;
        /// <summary>
        /// 计时器
        /// </summary>
        private System.Timers.Timer timer_count;
        #endregion
View Code

 

5、窗体加载与关闭事件

       #region 窗口加载与关闭
        /// <summary>
        /// 讲题加载事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            InitCamera();
            InitCameraSelect();
            this.btn_Capture.Enabled = false;
            this.btn_Save.Enabled = false;
            this.pb_BoxPanel.SizeMode = PictureBoxSizeMode.Zoom;//图片大小按比例适应控件,不加的话图片显示问题太大
            this.gb_CapturePanel.Visible = false;//默认不显示照片区域,拍照时在显示
                                                 //秒表
            timer_count = new System.Timers.Timer();   //实例化Timer类,设置间隔时间为1000毫秒;
            timer_count.Elapsed += new System.Timers.ElapsedEventHandler(tick_count);   //到达时间的时候执行事件;
            timer_count.AutoReset = true;   //设置是执行一次(false)还是一直执行(true);
            timer_count.Interval = 1000;
        }
        /// <summary>
        /// 窗口关闭时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            EndCamera();
        }
        #endregion
View Code

 

6、实例化

        #region 实例化
        /// <summary>
        /// 实例化摄像头
        /// </summary>
        public void InitCamera()
        {
            try
            {
                //检测电脑上的摄像头设备
                infoCollection = new AForge.Video.DirectShow.FilterInfoCollection(AForge.Video.DirectShow.FilterCategory.VideoInputDevice);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"没有找到设备:{ex.Message}", "Error");
            }

        }
        //加载摄像头选择
        public void InitCameraSelect()
        {
            this.cb_CameraSelect.DropDownStyle = ComboBoxStyle.DropDownList;
            //根据读取到的摄像头加载选择项
            foreach (AForge.Video.DirectShow.FilterInfo item in infoCollection)
            {
                this.cb_CameraSelect.Items.Add(item.Name);
            }
            //for (int i = 1; i <= infoCollection.Count; i++)
            //{
            //    this.cb_CameraSelect.Items.Add("摄像头" + i);
            //}
        }
        #endregion
View Code

 

7、摄像头的连接与关闭

     #region  摄像头的连接读取与关闭
        /// <summary>
        /// 连接按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            var CameraSelect = this.cb_CameraSelect.Text;
            var selectIndex = this.cb_CameraSelect.SelectedIndex;
            //已有连接的摄像头时先关闭
            if (videoCapture != null)
            {
                EndCamera();
            }
            videoCapture = new AForge.Video.DirectShow.VideoCaptureDevice(infoCollection[selectIndex].MonikerString);
            //启动摄像头
            this.vsp_Panel.VideoSource = videoCapture;
            this.vsp_Panel.Start();
            this.btn_Capture.Enabled = true;
            this.btn_Save.Enabled = false;
        }
        /// <summary>
        /// 关闭按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Close_Click(object sender, EventArgs e)
        {
            EndCamera();
        }
        /// <summary>
        /// 关闭摄像头
        /// </summary>
        public void EndCamera()
        {
            if (this.vsp_Panel.VideoSource != null)
            {
                //也可以用 Stop() 方法关闭
                //指示灯停止且等待
                this.vsp_Panel.SignalToStop();
                //停止且等待
                this.vsp_Panel.WaitForStop();
                this.vsp_Panel.VideoSource = null;
            }
        }
        #endregion
View Code

 

8、拍照与保存

        #region 拍照与保存
        /// <summary>
        /// 拍照
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Capture_Click(object sender, EventArgs e)
        {
            imgMap = this.vsp_Panel.GetCurrentVideoFrame();
            this.gb_CapturePanel.Visible = true;
            this.pb_BoxPanel.Image = imgMap;
            this.btn_Save.Enabled = true;
        }
        /// <summary>
        /// 保存事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Save_Click(object sender, EventArgs e)
        {
            var saveName = $"{filePath}{fileName}.jpg";
            imgMap.Save(saveName);
            this.btn_Save.Enabled = false;
            MessageBox.Show($"保存成功:{saveName}");
        }
        #endregion
        
 
View Code

 

9、录像

     #region 录像
        /// <summary>
        /// 开始录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartVideo_Click(object sender, EventArgs e)
        {
            string videoName = $"{filePath}{fileName}.MP4";
            IsStartVideo = true;
            //开始计时
            timer_count.Enabled = true;
            //配置录像参数(宽,高,帧率,比特率等参数)VideoCapabilities这个属性会返回摄像头支持哪些配置,从这里面选一个赋值接即可
            videoCapture.VideoResolution = videoCapture.VideoCapabilities[0];
            //设置回调,aforge会不断从这个回调推出图像数据
            videoCapture.NewFrame += Camera_NewFrame;
            videoWriter = new AForge.Video.FFMPEG.VideoFileWriter();
            //打开摄像头
            //videoCapture.Start();
            //打开录像文件(如果没有则创建,如果有也会清空),这里还有关于视频宽高、帧率、格式、比特率
            videoWriter.Open(
               videoName,
               videoCapture.VideoResolution.FrameSize.Width,
               videoCapture.VideoResolution.FrameSize.Height,
               videoCapture.VideoResolution.AverageFrameRate,
               AForge.Video.FFMPEG.VideoCodec.MPEG4,
               videoCapture.VideoResolution.BitCount
            );
        }
        //this.lab_Time
        //摄像头输出回调
        private void Camera_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            imgMap = eventArgs.Frame;
            lock (imgMap)
            {
                if (eventArgs != null && eventArgs.Frame != null)
                {
                    try
                    {
                        //写到文件
                        videoWriter.WriteVideoFrame(eventArgs.Frame);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }
        /// <summary>
        /// 暂停录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_PauseVideo_Click(object sender, EventArgs e)
        {
            var stopBtnName = this.btn_PauseVideo.Text;
            if (stopBtnName == "暂停录像")
            {
                this.btn_PauseVideo.Text = "恢复录像";
                //暂停计时
                timer_count.Enabled = false;
                IsStartVideo = false;
            }
            if (stopBtnName == "恢复录像")
            {
                this.btn_PauseVideo.Text = "暂停录像";
                //恢复计时
                timer_count.Enabled = true;
                IsStartVideo = true;
            }
        }
        /// <summary>
        /// 停止录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_EndVideo_Click(object sender, EventArgs e)
        {
            IsStartVideo = false;
            timer_count.Enabled = false;
            videoWriter.Close();
            MessageBox.Show("停止录像");
        }
        #endregion
View Code

需要特别注意
videoWriter.Open(string fileName, int width, int height, int frameRate, VideoCodec codec, int bitRate);
width与height必须为偶数,否则报错Video file resolution must be a multiple of two.

10、计时器

    #region 计时器响应函数
        /// <summary>
        /// 计时器
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        public void tick_count(object source, System.Timers.ElapsedEventArgs e)
        {
            tick_num++;
            int Temp = tick_num;
            int Second = Temp % 60;
            int Minute = Temp / 60;
            if (Minute % 60 == 0)
            {
                Hour = Minute / 60;
                Minute = 0;
            }
            else
            {
                Minute = Minute - Hour * 60;
            }
            string HourStr = Hour < 10 ? $"0{Hour}" : Hour.ToString();
            string MinuteStr = Minute < 10 ? $"0{Minute}" : Minute.ToString();
            string SecondStr = Second < 10 ? $"0{Second}" : Second.ToString();
            String tick = $"{HourStr}:{MinuteStr}:{SecondStr}";
            this.Invoke((EventHandler)(delegate
            {
                this.lab_Time.Text = tick;
            }));
        }
        #endregion
View Code

 

11、完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Forms;

namespace AForge拍照及录像
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        #region 属性
        /// <summary>
        /// 设备源
        /// 用来操作摄像头
        /// </summary>
        private AForge.Video.DirectShow.VideoCaptureDevice videoCapture;
        /// <summary>
        /// 摄像头设备集合
        /// </summary>
        private AForge.Video.DirectShow.FilterInfoCollection infoCollection;
        /// <summary>
        /// 图片
        /// </summary>
        private System.Drawing.Bitmap imgMap;
        /// <summary>
        /// 文件保存路径
        /// </summary>
        private readonly string filePath = $"D:\\VS主题\\图片\\";
        /// <summary>
        /// 文件名称
        /// </summary>
        private readonly string fileName = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}";
        /// <summary>
        /// 用与把每一帧图像写入到视频文件
        /// </summary>
        private AForge.Video.FFMPEG.VideoFileWriter videoWriter;
        /// <summary>
        /// 是否开始录像
        /// </summary>
        private bool IsStartVideo = false;
        /// <summary>
        /// 写入次数
        /// </summary>
        private int tick_num = 0;
        /// <summary>
        /// 录制多少小时,只是为了定时间计算录制时间使用
        /// </summary>
        private int Hour = 0;
        /// <summary>
        /// 计时器
        /// </summary>
        private System.Timers.Timer timer_count;
        #endregion

        #region 窗口加载与关闭
        /// <summary>
        /// 讲题加载事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            InitCamera();
            InitCameraSelect();
            this.btn_Capture.Enabled = false;
            this.btn_Save.Enabled = false;
            this.pb_BoxPanel.SizeMode = PictureBoxSizeMode.Zoom;//图片大小按比例适应控件,不加的话图片显示问题太大
            this.gb_CapturePanel.Visible = false;//默认不显示照片区域,拍照时在显示
                                                 //秒表
            timer_count = new System.Timers.Timer();   //实例化Timer类,设置间隔时间为1000毫秒;
            timer_count.Elapsed += new System.Timers.ElapsedEventHandler(tick_count);   //到达时间的时候执行事件;
            timer_count.AutoReset = true;   //设置是执行一次(false)还是一直执行(true);
            timer_count.Interval = 1000;
        }
        /// <summary>
        /// 窗口关闭时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            EndCamera();
        }
        #endregion

        #region 实例化
        /// <summary>
        /// 实例化摄像头
        /// </summary>
        public void InitCamera()
        {
            try
            {
                //检测电脑上的摄像头设备
                infoCollection = new AForge.Video.DirectShow.FilterInfoCollection(AForge.Video.DirectShow.FilterCategory.VideoInputDevice);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"没有找到设备:{ex.Message}", "Error");
            }

        }
        //加载摄像头选择
        public void InitCameraSelect()
        {
            this.cb_CameraSelect.DropDownStyle = ComboBoxStyle.DropDownList;
            //根据读取到的摄像头加载选择项
            foreach (AForge.Video.DirectShow.FilterInfo item in infoCollection)
            {
                this.cb_CameraSelect.Items.Add(item.Name);
            }
            //for (int i = 1; i <= infoCollection.Count; i++)
            //{
            //    this.cb_CameraSelect.Items.Add("摄像头" + i);
            //}
        }
        #endregion

        #region  摄像头的连接读取与关闭
        /// <summary>
        /// 连接按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            var CameraSelect = this.cb_CameraSelect.Text;
            var selectIndex = this.cb_CameraSelect.SelectedIndex;
            //已有连接的摄像头时先关闭
            if (videoCapture != null)
            {
                EndCamera();
            }
            videoCapture = new AForge.Video.DirectShow.VideoCaptureDevice(infoCollection[selectIndex].MonikerString);
            //启动摄像头
            this.vsp_Panel.VideoSource = videoCapture;
            this.vsp_Panel.Start();
            this.btn_Capture.Enabled = true;
            this.btn_Save.Enabled = false;
        }
        /// <summary>
        /// 关闭按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Close_Click(object sender, EventArgs e)
        {
            EndCamera();
        }
        /// <summary>
        /// 关闭摄像头
        /// </summary>
        public void EndCamera()
        {
            if (this.vsp_Panel.VideoSource != null)
            {
                //也可以用 Stop() 方法关闭
                //指示灯停止且等待
                this.vsp_Panel.SignalToStop();
                //停止且等待
                this.vsp_Panel.WaitForStop();
                this.vsp_Panel.VideoSource = null;
            }
        }
        #endregion

        #region 拍照与保存
        /// <summary>
        /// 拍照
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Capture_Click(object sender, EventArgs e)
        {
            imgMap = this.vsp_Panel.GetCurrentVideoFrame();
            this.gb_CapturePanel.Visible = true;
            this.pb_BoxPanel.Image = imgMap;
            this.btn_Save.Enabled = true;
        }
        /// <summary>
        /// 保存事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Save_Click(object sender, EventArgs e)
        {
            var saveName = $"{filePath}{fileName}.jpg";
            imgMap.Save(saveName);
            this.btn_Save.Enabled = false;
            MessageBox.Show($"保存成功:{saveName}");
        }
        #endregion

        #region 录像
        /// <summary>
        /// 开始录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartVideo_Click(object sender, EventArgs e)
        {
            string videoName = $"{filePath}{fileName}.MP4";
            IsStartVideo = true;
            //开始计时
            timer_count.Enabled = true;
            //配置录像参数(宽,高,帧率,比特率等参数)VideoCapabilities这个属性会返回摄像头支持哪些配置,从这里面选一个赋值接即可
            videoCapture.VideoResolution = videoCapture.VideoCapabilities[0];
            //设置回调,aforge会不断从这个回调推出图像数据
            videoCapture.NewFrame += Camera_NewFrame;
            videoWriter = new AForge.Video.FFMPEG.VideoFileWriter();
            //打开摄像头
            //videoCapture.Start();
            //打开录像文件(如果没有则创建,如果有也会清空),这里还有关于视频宽高、帧率、格式、比特率
            videoWriter.Open(
               videoName,
               videoCapture.VideoResolution.FrameSize.Width,
               videoCapture.VideoResolution.FrameSize.Height,
               videoCapture.VideoResolution.AverageFrameRate,
               AForge.Video.FFMPEG.VideoCodec.MPEG4,
               videoCapture.VideoResolution.BitCount
            );
        }
        //this.lab_Time
        //摄像头输出回调
        private void Camera_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            imgMap = eventArgs.Frame;
            lock (imgMap)
            {
                if (eventArgs != null && eventArgs.Frame != null)
                {
                    try
                    {
                        //写到文件
                        videoWriter.WriteVideoFrame(eventArgs.Frame);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }
        /// <summary>
        /// 暂停录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_PauseVideo_Click(object sender, EventArgs e)
        {
            var stopBtnName = this.btn_PauseVideo.Text;
            if (stopBtnName == "暂停录像")
            {
                this.btn_PauseVideo.Text = "恢复录像";
                //暂停计时
                timer_count.Enabled = false;
                IsStartVideo = false;
            }
            if (stopBtnName == "恢复录像")
            {
                this.btn_PauseVideo.Text = "暂停录像";
                //恢复计时
                timer_count.Enabled = true;
                IsStartVideo = true;
            }
        }
        /// <summary>
        /// 停止录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_EndVideo_Click(object sender, EventArgs e)
        {
            IsStartVideo = false;
            timer_count.Enabled = false;
            videoWriter.Close();
            MessageBox.Show("停止录像");
        }
        #endregion

        #region 计时器响应函数
        /// <summary>
        /// 计时器
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        public void tick_count(object source, System.Timers.ElapsedEventArgs e)
        {
            tick_num++;
            int Temp = tick_num;
            int Second = Temp % 60;
            int Minute = Temp / 60;
            if (Minute % 60 == 0)
            {
                Hour = Minute / 60;
                Minute = 0;
            }
            else
            {
                Minute = Minute - Hour * 60;
            }
            string HourStr = Hour < 10 ? $"0{Hour}" : Hour.ToString();
            string MinuteStr = Minute < 10 ? $"0{Minute}" : Minute.ToString();
            string SecondStr = Second < 10 ? $"0{Second}" : Second.ToString();
            String tick = $"{HourStr}:{MinuteStr}:{SecondStr}";
            this.Invoke((EventHandler)(delegate
            {
                this.lab_Time.Text = tick;
            }));
        }
        #endregion
    }
}
View Code

 

12、测试结果

拍照结果如下:

 录像结果如下:

12、Demo下载

完整Demo下载: C# WinForm使用AForge拍照及录像

13、踩坑经历及解决方案

参考:细数AForge.Video.FFMPEG使用过程中踩的坑及解决方案