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_Connect、btn_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、属性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; #endregionView Code
5、窗体加载与关闭事件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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(); } #endregionView Code
6、实例化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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); //} } #endregionView Code
7、摄像头的连接与关闭
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; } } #endregionView Code
8、拍照与保存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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}"); } #endregionView Code
9、录像
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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("停止录像"); } #endregionView 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、计时器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; })); } #endregionView Code
11、完整代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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、踩坑经历及解决方案
标签:C#,void,private,AForge,Video,btn,摄像头 From: https://www.cnblogs.com/lwk9527/p/17374631.html