c#屏幕录制(经典)(含源码和AForge.Video.FFMPEG.DLL)及填坑办法

        一直觉得.net在多媒体处理方面渣得不行。最近需要做一个摄像头的程序,为了方便,用了AForge这个开源项目。AForge项目中有AForge.Video和AForge.Video. DirectShow这两个子项目,可以方便的调用摄像头。但是这两个项目最终只能取得视频帧,并不能保存为视频文件。经高人指点,AForge还有一个子项目AForge.Video.FFMPEG,它可以将图片压制成Avi视频格式。不过这个AForge.Video.FFMPEG在实际使用的时候会遇到不少坑,下面我将我在这次使用中遇到的坑分享给大家。

     AForge.NET是一个专门为开发者和研究者基于C#框架设计的,该库是一个开源项目,他包括计算机视觉与人工智能,图像处理,神经网络,遗传算法,机器学习,模糊系统,机器人控制等领域,提供很多图像的处理,和视频处理功能

        这个框架由一系列的类库组成。主要包括有:

 AForge.Imaging —— 一些日常的图像处理和过滤器
 AForge.Vision —— 计算机视觉应用类库
 AForge.Neuro —— 神经网络计算库AForge.Genetic -进化算法编程库
 AForge.MachineLearning —— 机器学习类库
 AForge.Robotics —— 提供一些机器人的工具类库
 AForge.Video —— 一系列的视频处理类库
 AForge.Fuzzy —— 模糊推理系统类库
 AForge.Controls—— 图像,三维,图表显示控件

官网:http://www.aforgenet.com/

  Aforge.Net子项目有个AForge.Video.VFW提供了对Avi文件的操作,AForge后面加入了子项目 AForge.Video.FFMPEG 通过FFmpeg库,提供了对大量视频格式的支持,我们都知道,FFmpeg是一个非常强大的视频处理类库,同样也是开源的,不过 AForge.Video.FFMPEG 还处于实验阶段,目标是用 FFmpeg 取代 AForge.Video.VFW 提供一个更好的对视频文件操作的库,但是该库值目前提供了对视频数据的读写,不支持对音频文件的读写,可能以后会支持 

第一坑:引用

你要用AForge.Video.FFMPEG,当然第一步是引用啦。但这个AForge.Video.FFMPEG并不能像AForge其他项目一样可以用Visual Studio自带的NuGet去获得,你会发现NuGet上根本找不到这个项目。

5ccb5cfa10f4c635213f7195f5dcb639.png

找不到么,那我就去官网找好了,咱们可以去AForge项目官网下载AForge项目的源码和已编译文件。不过这里有俩问题:

  1. AForge项目官网打开速度非常非常非常慢,你可以点链接打开官网,然后打开游戏玩一会儿。(这里我就给各位放个AForge下载页直链:http://www.aforgenet.com/framework/downloads.html)

  2. AForge项目的源码和生成文件最终都是放在GoogleCode上的,国内你懂得。不过这边我们就可以用的小花招就是用迅雷之类的下载器下载,他们的离线下载是可以翻墙的。

81544c9c138d811fb47645faeee48871.png

我是选择了“Download Installer”,右键选择“复制链接地址”,然后放进迅雷下载。

下载下来之后是一个压缩包,AForge.Video.FFMPEG.dll就放在压缩包的Release文件夹中。

5063456123e93d7db3fcbb5051cc3dda.png

第二坑:调用

刚刚我们从官网下载下来了AForge.Video.FFMPEG.dll,接下来调用就好了对吧。

然而并不是,你只是一脚踏进了一个深坑罢了,为什么说是深坑呢?因为这个dll调用非常非常的恶心。

我们来看一下有多恶心,首先我们假设我们已经在项目中已经添加了AForge.Video和AForge.Video.FFMPEG这二个类库。

然后修改Main函数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleRecoderTest
{class Program{static void Main(string[] args){ScreenRecorderTemplate tmp = new ScreenRecorderTemplate(){IsStartUp = false,StartDateTime = DateTime.Now,StopDateTime = DateTime.Now.AddMinutes(2)};new ScreenRecorderTool(tmp).StartRecording();Console.WriteLine("complete");Console.Read();}}
}

按F5调试,瞬间爆炸:

28b3f1417d43af61f129c4f1468ffc53.png

发生这个问题的原因比较简单,因为这个AForge.Video.FFMPEG使用VC++写的,编译的时候已经被编译成本地代码,而我们现在C#一般目标平台都是“Any CPU”,所以会发生这个问题。

解决方案就是不再选择使用“Any CPU”作为目标平台,改成“x86”或者“x64”。因为x86可以跑在x64上,而x64不能在x86上跑,所以我选择了x86。

83fb0500840072d37ee7c03d371d0009.png

现在再按F5启动调试,再一次瞬间爆炸【冷漠脸】。

099420966d7325e44697f7cade871194.png

怎么说呢,起码出错提示换了对吧【冷漠脸】。

那么这次又是出什么问题了呢。

咱们现在用的是AForge.Video.FFMPEG对吧。我们都知道FFMPEG是一个著名的开源多媒体处理项目对吧,这个AForge.Video.FFMPEG其实是在内部调用FFMPEG来工作的。所以这个FileNotFoundException其实是AForge.Video.FFMPEG找不到FFMPEG的文件所以抛出来的。AForge.Video.FFMPEG依赖的FFMPEG组件其实已经放在了刚刚下载下来的压缩包的\Externals\ffmpeg\bin目录下:

115f98ade35b308e2ce868037e6469be.png

我们把这个8个文件复制到程序目录下,注意我们刚刚改过目标平台了,现在程序编译输出的目录已经是\bin\x86\Debug,不要复制错了。

复制好之后我们继续按F5调试程序。

嗯,爆炸了,我已经习惯了【冷漠脸】

d3a5b64d1845f7effdc21f874a580252.png

这次问题的原因是什么呢……

其实是因为我的项目目标框架是.net Framework 4.0,而AForge官方在编译AForge.Video.FFMPEG.dll的时候,目标框架选的是.net Framework 2.0……

在.net Framework 4.0以前,由于程序运行环境本质还是.net Framework 2.0,并且.net Framework 2.0兼容.net Framework 1.0和1.1,但在升级到.net Framework 4.0时,.NET的内核作了重大调整,以前在.net Framework 2.0或.net3.5中生成的程序集,如果要在.net Framework 4.0下运行,需要在配置文件中指定此应用程序支持的公共语言运行时版本和启用.net Framework 2.0运行时激活策略。

解决方案有三种:

  1. 降低自己项目的目标.net Framework版本;

  2. 修改Config文件;

  3. 重新编译Video.FFMPEG。

这里我就讲一下方法二,

在Visual Studio中按Ctrl+Shift+A,打开“添加新项”窗口,选择“应用程序配置文件”,再点击“添加”(vs2017创建的时候已经自带了App.config无需再次添加)

打开新建的App.Config文件,在<configuration>和</configuration>标签中加入以下内容:

<startup useLegacyV2RuntimeActivationPolicy="true"><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup>

910ff40c9dc722a7cb808f20e5973e32.png

添加完成后,按F5启动调试。

378c50020c46f1a26a50eaaebbff0fe5.png

终于一切正常。

录制的视频在文件运行目录下:

1ca3b1ece22bf54281d11aae422772f4.png

项目的引用项:

5af5e0ba8f2e03f50f897576e29bad9a.png

使用的开源的视频处理组件AForge,当然它所包含的功能远不止于此,想了解更多到官网上去看吧。一下代码主要是录制桌面屏幕,每20秒存入一个视频文件,可以为有类似需要的通知提供一点帮助。

ScreenRecorderTool.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//using Accord.Video;
//using Accord.Video.FFMPEG;
using AForge.Video;
using AForge.Video.FFMPEG;namespace ConsoleRecoderTest
{/// <summary>/// 比特率/// </summary>public enum BitRate : int{_50kbit = 5000,_100kbit = 10000,_500kbit = 50000,_1000kbit = 1000000,_2000kbit = 2000000,_3000kbit = 3000000}/// <summary>/// 屏幕录制模板/// </summary>public class ScreenRecorderTemplate{/// <summary>/// 模板名称/// </summary>public string TmpName { get; set; }/// <summary>/// 录屏开始时间/// </summary>public DateTime? StartDateTime { get; set; }/// <summary>/// 录屏结束时间/// </summary>public DateTime? StopDateTime { get; set; }/// <summary>/// 是否为开机启动/// </summary>public bool IsStartUp { get; set; }}/// <summary>/// 屏幕录制工具类/// </summary>public class ScreenRecorderTool{#region Fieldsprivate int screenWidth;private int screenHight;private int bitRate = (int)BitRate._500kbit;private int frameRate = 5;//默认帧率为5private bool isRecording;private string saveFolderPath;private string fileName;private Stopwatch stopWatch;private Rectangle screenArea;private VideoFileWriter videoWriter;private ScreenCaptureStream videoStreamer;private VideoCodec videoCodec = VideoCodec.MSMPEG4v2;private ScreenRecorderTemplate recorderTmp;private static object key = new object();#endregion/// <summary>/// 是否正在录制/// </summary>private bool IsRecording{get{lock (key){return isRecording;}}set{lock (key){isRecording = value;}}}public ScreenRecorderTool(ScreenRecorderTemplate recorderTmp){this.recorderTmp = recorderTmp;this.screenWidth = SystemInformation.VirtualScreen.Width;this.screenHight = SystemInformation.VirtualScreen.Height;this.IsRecording = false;this.SaveFolderPath = AppDomain.CurrentDomain.BaseDirectory;this.stopWatch = new Stopwatch();this.screenArea = Rectangle.Empty;SetScreenArea();}/// <summary>/// 视频保存位置/// </summary>private string SaveFolderPath{get { return this.saveFolderPath; }set{if (string.IsNullOrEmpty(value)){throw new ArgumentNullException("saveFolderpath", "保存路径不能为空");}this.saveFolderPath = value;}}/// <summary>/// 视频文件名称/// </summary>private string FileName{get { return this.fileName; }set{if (string.IsNullOrEmpty(value)){throw new ArgumentNullException("fileName", "File name can not be empty or null");}this.fileName = value;}}/// <summary>/// 完成一帧录制的事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void video_NewFrame(object sender, NewFrameEventArgs e){if (this.IsRecording){if (videoWriter != null){this.videoWriter.WriteVideoFrame(e.Frame);}if (this.stopWatch.Elapsed.Seconds >= 20){Console.WriteLine("超过指定时间,写入文件");StopRecording();}}else{videoStreamer.SignalToStop();videoWriter.Close();videoWriter.Dispose();//GC.Collect();Console.WriteLine("停止录制");if (recorderTmp.IsStartUp)//开机录制{StartRecording();}else{if (DateTime.Now <= recorderTmp.StopDateTime.Value){Console.WriteLine("记录重启录制");StartRecording();}else{Console.WriteLine("时间到不再录制");}}}}/// <summary>/// 设置必要参数打开视频写入工具/// </summary>private void InitializeRecordingParameters(){if (!this.IsRecording){this.IsRecording = true;CreateCatalog();this.FileName = saveFolderPath + string.Format(@"{0}-{1}.avi","MSR",DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"));this.videoWriter.Open(this.FileName, this.screenWidth, this.screenHight, this.frameRate, this.videoCodec, this.bitRate);}}/// <summary>/// 创建目录/// </summary>private void CreateCatalog(){if (saveFolderPath == AppDomain.CurrentDomain.BaseDirectory){var catalog = SaveFolderPath + DateTime.Now.ToString("yyyy-MM-dd") + "\\";if (!System.IO.Directory.Exists(catalog)){System.IO.Directory.CreateDirectory(catalog);}SaveFolderPath = catalog;}}/// <summary>/// 设置屏幕录制区域为全屏/// </summary>private void SetScreenArea(){foreach (Screen screen in Screen.AllScreens){this.screenArea = Rectangle.Union(this.screenArea, screen.Bounds);}if (this.screenArea == Rectangle.Empty){//logger.Error("没有获取到屏幕信息");throw new InvalidOperationException("Screan area can not be set");}}/// <summary>/// 旧文件清理(避免文件大小超标)/// </summary>private void ClearOldVideo(){}#region public method/// <summary>/// 打开视频流开始录制/// </summary>public void StartRecording(){if (recorderTmp == null){Console.WriteLine("模板不能为空");return;}if (!recorderTmp.IsStartUp){if (!recorderTmp.StartDateTime.HasValue|| !recorderTmp.StopDateTime.HasValue|| recorderTmp.StartDateTime.Value > recorderTmp.StopDateTime.Value){Console.WriteLine("模板不正确");return;}}this.videoWriter = new VideoFileWriter();InitializeRecordingParameters();this.videoStreamer = new ScreenCaptureStream(this.screenArea);this.videoStreamer.NewFrame += new NewFrameEventHandler(video_NewFrame);this.videoStreamer.Start();this.stopWatch.Start();this.IsRecording = true;Console.WriteLine("开始录制...");}/// <summary>/// 停止录制/// </summary>public void StopRecording(){this.stopWatch.Reset();this.IsRecording = false;}#endregion}
}

示例调用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleRecoderTest
{class Program{static void Main(string[] args){ScreenRecorderTemplate tmp = new ScreenRecorderTemplate(){IsStartUp = false,StartDateTime = DateTime.Now,StopDateTime = DateTime.Now.AddMinutes(2)};new ScreenRecorderTool(tmp).StartRecording();Console.WriteLine("complete");Console.Read();}}
}

补充:

直接运行的话有问题,frameRate = 5;//默认帧率为5,实际上写在视频里是30秒!
问题出在帧率与截屏间隔不对。`//this.videoStreamer = new ScreenCaptureStream(this.screenArea);//要加录制间隔时间this.videoStreamer = new ScreenCaptureStream(this.screenArea, 1000 / frameRate);
`另外 this.stopWatch.Elapsed.Seconds >= 20要改成21,因为大于等于20的话就停止的话实际上就只录了19秒,所有要改为21
`if (this.stopWatch.Elapsed.Seconds >= 21)

源码、播放器、AForge.NET Framework-2.2.5.exe下载地址:

链接:https://pan.baidu.com/s/11O8z8Fj4JyMqgQ3ybxZ3ZQ

提取码:5fxo

参考链接:

  1.  http://www.diqisoft.com/technical/20087.htm

  2. https://www.cnblogs.com/yyq745201/p/5334294.html

  3. 技术群: 需要进技术群学习交流的请添加小编微信,切记备注:加群,对以上内容有什么疑问也可以直接和小编直接沟通交流!     

    小编微信:mm1552923   

    公众号:dotNet编程大全      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/293233.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

drawable文件怎么添加图片_怎么给PDF文件添加书签

现如今我们使用的电子文档逐步都被PDF取代&#xff0c;虽然PDF有很多好处&#xff0c;但相较Word文档打开就能随意修改不同&#xff0c;PDF并不能直接编辑。比如有时我们要给PDF添加书签&#xff0c;这样可以快速找到要的页面&#xff0c;要怎么操作呢&#xff1f;一说到PDF的任…

通过Rancher Desktop在桌面上运行K8s

Rancher 发行的操作系统新选择&#xff1a;Rancher Desktop for Windows&#xff0c;它可以帮助你在Windows桌面上管理Kubernetes和容器。当然他当然会支持Linux&#xff0c;Mac的。准备工作在我们探索全新的Rancher Desktop之前&#xff0c;我们需要准备以下内容&#xff1a;1…

数学家排名,高斯第二牛顿第三?!看完第一的简历,他果然比牛顿还牛逼.........

如果让你给数学家排名&#xff0c;你会怎么排&#xff1f;谁排第一&#xff1f;高斯&#xff1f;阿基米德&#xff1f;还是其他哪位数学神仙&#xff1f;今天早上超模君发现&#xff0c;在国内某排行网站上&#xff0c;由网友投票选出来“世界十大数学家”里&#xff0c;名列前…

oc引导windows蓝屏_跟电脑蓝屏say no!【亲测有效】

​ 01专业解释电脑蓝屏&#xff0c;又叫蓝屏死机&#xff08;Blue Screen of Death&#xff0c;简称BSOD&#xff09;&#xff0c;是微软的 Windows 系列操作系统在无法从一个系统错误中恢复过来时&#xff0c;为保护电脑数据文件不被破坏而强制显示的屏幕图像。 看到了吧&…

hdu 1800 (map)

链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1800 Flying to the Mars Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 10830 Accepted Submission(s): 3472 Problem DescriptionIn the year 8…

.NET 6 中的七个 System.Text.Json 特性

忽略循环引用在 .NET 5 中&#xff0c;如果存在循环依赖, 那么序列化的时候会抛出异常, 而在 .NET 6 中, 你可以选择忽略它。Category dotnet new() {Name ".NET 6", }; Category systemTextJson new() {Name "System.Text.Json",Parent dotnet }; do…

Redis整合Spring结合使用缓存实例

林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka 摘要&#xff1a;本文介绍了如何在Spring中配置redis&#xff0c;并通过Spring中AOP的思想&#xff0c;将缓存的方法切入到有需要进入缓存的类或方法前面。 一、Redis介绍 什么是Redis&#xff1f; redis…

读取无线手柄数据_xbox series x/s 手柄开箱

原标题&#xff1a;xbox series x/s 手柄开箱xbox series x/s 手柄开箱 2020-11-12 08:29:003点赞2收藏4评论小编注&#xff1a;此篇文章来自#原创新人#激励计划&#xff0c;新人发文前三篇文章&#xff0c;篇篇额外奖励50金币。参加超级新人计划活动&#xff0c;新人发文即可瓜…

豆瓣评分9.4!这一部纪录片,探秘中国人迹罕至的未至之境!

全世界只有3.14 % 的人关注了爆炸吧知识Bilibili 联合“美国国家地理”&#xff0c;悄悄出品了一部史诗级动物记录片&#xff0c;忍不住要推荐给大朋友小朋友们——《未至之境》。这部纪录片由B站和国家地理联合创作&#xff0c;从绵延万里的山脉高原到枝繁叶茂的雨林竹海&…

使用OpenTelemetry搭配Zipkin构建NetCore分布式链路跟踪 | WebAPI + gRPC

OpenTelemetry介绍OpenTelemetry是一组标准和工具的集合&#xff0c;旨在管理观测类数据&#xff0c;如 trace、metrics、logs 等。通过标准化不同的应用程序和框架如何收集和发出可观察性遥测数据&#xff0c;OpenTelemetry旨在解决这些环境带来的一些挑战。OpenTelemetry包括…

腾讯云cloudlite认证_【腾讯云】考个证...大数据开发工程师认证

作为一个大数据行业的从业者&#xff0c;考个腾讯云大数据开发工程师认证总比考个消防证 easy 吧…&#xff1f;关于考这个认证的意义其实主要在于全面复习一下大数据相关的知识点&#xff0c;另外有个腾讯云的认证&#xff0c;也许大概也会对你找工作有点帮助的吧&#xff1f;…

kernel shell bash简介

还记得我们在Linux启动的时候。首先会启动内核 (kernel)&#xff0c;内核是一段计算机程序&#xff0c;这个程序直接管理管理硬件&#xff0c;包括CPU、内存空间、硬盘接口、网络接口等等。所有的计算机操作都要通过内核传递给硬件。为了我们方便调用内核&#xff0c;我们将内核…

最高调恋爱方式,简直“公开处刑”......

1 我拿到了剑桥的offer...▼2 当空乘专业第一次穿高跟鞋...▼3 是我没错了▼4 笑出鹅叫▼5 现场表演一下&#xff0c;一秒落汤鸡&#xff01;▼6 那个红/黄毛起来一下&#xff01;▼7 最高调的恋爱方式▼7 数学可以多好玩▼你点的每个赞&#xff0c;我都认真当成了喜欢

C# 扩展集合ObservableCollection使集合在添加、删除、值变更后触发事件

01—概述ObservableCollection继承了INotifyPropertyChanged接口&#xff0c;在属性变更时可以通知界面&#xff0c;当我把ObservableCollection集合绑定到界面的DataGrid后&#xff0c;我希望在界面修改表格数值后&#xff0c;可以触发一个 事件来验证我界面设定数据的有效性&…

数据挖掘算法之决策树算法总结

机器学习中&#xff0c;决策树是一个预测模型&#xff1b;它代表的是对象属性值与对象值之间的一种映射关系。树中每个节点表示某个对象&#xff0c;每个分叉路径则代表的某个可能的属性值&#xff0c;而每个叶结点则对应具有上述属性值的子对象。决策树仅有单一输出&#xff1…

win7升级win10正式版_win7告退在即,如何升级到win10?

Win7即将停更&#xff0c;小伙伴们也差不多要进行自己系统的更新啦&#xff0c;今天小白就教大家如何将Win7升级到Win10第一步&#xff1a;首先在官网 www.xiaobaixitong.com 下载“小白软件”第二步&#xff1a;选好我们要下载的系统&#xff0c;然后安装即可。第三步&#xf…

在24小时内学完所有的数学是种什么体验?我们做了这个大胆的尝试……

全世界只有3.14 % 的人关注了爆炸吧知识人还好就是没了而已其实&#xff0c;在此时&#xff0c;还有一件事情发生了。22:00&#xff0c;我们发出了这篇文章&#xff0c;不仅将2600多年的数学简史浓缩成24小时&#xff0c;还将2600多年数学简史浓缩成54张卡牌。每张卡牌背后都有…

Server Develop (八) IOCP模型

IOCP模型 IOCP全称I/O Completion Port&#xff0c;中文译为I/O完成端口。IOCP是一个异步I/O的Windows API&#xff0c;它可以高效地将I/O事件通知给应用程序&#xff0c;类似于Linux中的Epoll。 简介 IOCP模型属于一种通讯模型&#xff0c;适用于Windows平台下高负载服务器的一…

理解RESTful架构

2019独角兽企业重金招聘Python工程师标准>>> 原文&#xff1a;http://www.ruanyifeng.com/blog/2011/09/restful.html?20160826000527 越来越多的人开始意识到&#xff0c;网站即软件&#xff0c;而且是一种新型的软件。 这种"互联网软件"采用客户端/服务…