c#屏幕录制(经典)(含源码和AForge.Video.FFMPEG.DLL)及填坑办法
? ? ? ? 一直覺得.net在多媒體處理方面渣得不行。最近需要做一個(gè)攝像頭的程序,為了方便,用了AForge這個(gè)開源項(xiàng)目。AForge項(xiàng)目中有AForge.Video和AForge.Video. DirectShow這兩個(gè)子項(xiàng)目,可以方便的調(diào)用攝像頭。但是這兩個(gè)項(xiàng)目最終只能取得視頻幀,并不能保存為視頻文件。經(jīng)高人指點(diǎn),AForge還有一個(gè)子項(xiàng)目AForge.Video.FFMPEG,它可以將圖片壓制成Avi視頻格式。不過這個(gè)AForge.Video.FFMPEG在實(shí)際使用的時(shí)候會(huì)遇到不少坑,下面我將我在這次使用中遇到的坑分享給大家。
?? ??AForge.NET是一個(gè)專門為開發(fā)者和研究者基于C#框架設(shè)計(jì)的,該庫是一個(gè)開源項(xiàng)目,他包括計(jì)算機(jī)視覺與人工智能,圖像處理,神經(jīng)網(wǎng)絡(luò),遺傳算法,機(jī)器學(xué)習(xí),模糊系統(tǒng),機(jī)器人控制等領(lǐng)域,提供很多圖像的處理,和視頻處理功能
? ? ? ??這個(gè)框架由一系列的類庫組成。主要包括有:
?AForge.Imaging —— 一些日常的圖像處理和過濾器
?AForge.Vision —— 計(jì)算機(jī)視覺應(yīng)用類庫
?AForge.Neuro —— 神經(jīng)網(wǎng)絡(luò)計(jì)算庫AForge.Genetic -進(jìn)化算法編程庫
?AForge.MachineLearning —— 機(jī)器學(xué)習(xí)類庫
?AForge.Robotics —— 提供一些機(jī)器人的工具類庫
?AForge.Video —— 一系列的視頻處理類庫
?AForge.Fuzzy —— 模糊推理系統(tǒng)類庫
?AForge.Controls—— 圖像,三維,圖表顯示控件
官網(wǎng):http://www.aforgenet.com/
Aforge.Net子項(xiàng)目有個(gè)AForge.Video.VFW提供了對(duì)Avi文件的操作,AForge后面加入了子項(xiàng)目?AForge.Video.FFMPEG?通過FFmpeg庫,提供了對(duì)大量視頻格式的支持,我們都知道,FFmpeg是一個(gè)非常強(qiáng)大的視頻處理類庫,同樣也是開源的,不過?AForge.Video.FFMPEG?還處于實(shí)驗(yàn)階段,目標(biāo)是用 FFmpeg?取代?AForge.Video.VFW?提供一個(gè)更好的對(duì)視頻文件操作的庫,但是該庫值目前提供了對(duì)視頻數(shù)據(jù)的讀寫,不支持對(duì)音頻文件的讀寫,可能以后會(huì)支持?
第一坑:引用
你要用AForge.Video.FFMPEG,當(dāng)然第一步是引用啦。但這個(gè)AForge.Video.FFMPEG并不能像AForge其他項(xiàng)目一樣可以用Visual Studio自帶的NuGet去獲得,你會(huì)發(fā)現(xiàn)NuGet上根本找不到這個(gè)項(xiàng)目。
找不到么,那我就去官網(wǎng)找好了,咱們可以去AForge項(xiàng)目官網(wǎng)下載AForge項(xiàng)目的源碼和已編譯文件。不過這里有倆問題:
AForge項(xiàng)目官網(wǎng)打開速度非常非常非常慢,你可以點(diǎn)鏈接打開官網(wǎng),然后打開游戲玩一會(huì)兒。(這里我就給各位放個(gè)AForge下載頁直鏈:http://www.aforgenet.com/framework/downloads.html)
AForge項(xiàng)目的源碼和生成文件最終都是放在GoogleCode上的,國內(nèi)你懂得。不過這邊我們就可以用的小花招就是用迅雷之類的下載器下載,他們的離線下載是可以翻墻的。
我是選擇了“Download Installer”,右鍵選擇“復(fù)制鏈接地址”,然后放進(jìn)迅雷下載。
下載下來之后是一個(gè)壓縮包,AForge.Video.FFMPEG.dll就放在壓縮包的Release文件夾中。
第二坑:調(diào)用
剛剛我們從官網(wǎng)下載下來了AForge.Video.FFMPEG.dll,接下來調(diào)用就好了對(duì)吧。
然而并不是,你只是一腳踏進(jìn)了一個(gè)深坑罷了,為什么說是深坑呢?因?yàn)檫@個(gè)dll調(diào)用非常非常的惡心。
我們來看一下有多惡心,首先我們假設(shè)我們已經(jīng)在項(xiàng)目中已經(jīng)添加了AForge.Video和AForge.Video.FFMPEG這二個(gè)類庫。
然后修改Main函數(shù):
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調(diào)試,瞬間爆炸:
發(fā)生這個(gè)問題的原因比較簡(jiǎn)單,因?yàn)檫@個(gè)AForge.Video.FFMPEG使用VC++寫的,編譯的時(shí)候已經(jīng)被編譯成本地代碼,而我們現(xiàn)在C#一般目標(biāo)平臺(tái)都是“Any CPU”,所以會(huì)發(fā)生這個(gè)問題。
解決方案就是不再選擇使用“Any CPU”作為目標(biāo)平臺(tái),改成“x86”或者“x64”。因?yàn)閤86可以跑在x64上,而x64不能在x86上跑,所以我選擇了x86。
現(xiàn)在再按F5啟動(dòng)調(diào)試,再一次瞬間爆炸【冷漠臉】。
怎么說呢,起碼出錯(cuò)提示換了對(duì)吧【冷漠臉】。
那么這次又是出什么問題了呢。
咱們現(xiàn)在用的是AForge.Video.FFMPEG對(duì)吧。我們都知道FFMPEG是一個(gè)著名的開源多媒體處理項(xiàng)目對(duì)吧,這個(gè)AForge.Video.FFMPEG其實(shí)是在內(nèi)部調(diào)用FFMPEG來工作的。所以這個(gè)FileNotFoundException其實(shí)是AForge.Video.FFMPEG找不到FFMPEG的文件所以拋出來的。AForge.Video.FFMPEG依賴的FFMPEG組件其實(shí)已經(jīng)放在了剛剛下載下來的壓縮包的\Externals\ffmpeg\bin目錄下:
我們把這個(gè)8個(gè)文件復(fù)制到程序目錄下,注意我們剛剛改過目標(biāo)平臺(tái)了,現(xiàn)在程序編譯輸出的目錄已經(jīng)是\bin\x86\Debug,不要復(fù)制錯(cuò)了。
復(fù)制好之后我們繼續(xù)按F5調(diào)試程序。
嗯,爆炸了,我已經(jīng)習(xí)慣了【冷漠臉】
這次問題的原因是什么呢……
其實(shí)是因?yàn)槲业捻?xiàng)目目標(biāo)框架是.net Framework 4.0,而AForge官方在編譯AForge.Video.FFMPEG.dll的時(shí)候,目標(biāo)框架選的是.net Framework 2.0……
在.net Framework 4.0以前,由于程序運(yùn)行環(huán)境本質(zhì)還是.net Framework 2.0,并且.net Framework 2.0兼容.net Framework 1.0和1.1,但在升級(jí)到.net Framework 4.0時(shí),.NET的內(nèi)核作了重大調(diào)整,以前在.net Framework 2.0或.net3.5中生成的程序集,如果要在.net Framework 4.0下運(yùn)行,需要在配置文件中指定此應(yīng)用程序支持的公共語言運(yùn)行時(shí)版本和啟用.net Framework 2.0運(yùn)行時(shí)激活策略。
解決方案有三種:
降低自己項(xiàng)目的目標(biāo).net Framework版本;
修改Config文件;
重新編譯Video.FFMPEG。
這里我就講一下方法二,
在Visual Studio中按Ctrl+Shift+A,打開“添加新項(xiàng)”窗口,選擇“應(yīng)用程序配置文件”,再點(diǎn)擊“添加”(vs2017創(chuàng)建的時(shí)候已經(jīng)自帶了App.config無需再次添加)
打開新建的App.Config文件,在<configuration>和</configuration>標(biāo)簽中加入以下內(nèi)容:
<startup useLegacyV2RuntimeActivationPolicy="true"><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup>添加完成后,按F5啟動(dòng)調(diào)試。
終于一切正常。
錄制的視頻在文件運(yùn)行目錄下:
項(xiàng)目的引用項(xiàng):
使用的開源的視頻處理組件AForge,當(dāng)然它所包含的功能遠(yuǎn)不止于此,想了解更多到官網(wǎng)上去看吧。一下代碼主要是錄制桌面屏幕,每20秒存入一個(gè)視頻文件,可以為有類似需要的通知提供一點(diǎn)幫助。
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>/// 錄屏開始時(shí)間/// </summary>public DateTime? StartDateTime { get; set; }/// <summary>/// 錄屏結(jié)束時(shí)間/// </summary>public DateTime? StopDateTime { get; set; }/// <summary>/// 是否為開機(jī)啟動(dòng)/// </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;//默認(rèn)幀率為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("超過指定時(shí)間,寫入文件");StopRecording();}}else{videoStreamer.SignalToStop();videoWriter.Close();videoWriter.Dispose();//GC.Collect();Console.WriteLine("停止錄制");if (recorderTmp.IsStartUp)//開機(jī)錄制{StartRecording();}else{if (DateTime.Now <= recorderTmp.StopDateTime.Value){Console.WriteLine("記錄重啟錄制");StartRecording();}else{Console.WriteLine("時(shí)間到不再錄制");}}}}/// <summary>/// 設(shè)置必要參數(shù)打開視頻寫入工具/// </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>/// 創(chuàng)建目錄/// </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>/// 設(shè)置屏幕錄制區(qū)域?yàn)槿?// </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>/// 舊文件清理(避免文件大小超標(biāo))/// </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} }示例調(diào)用:
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();}} }補(bǔ)充:
直接運(yùn)行的話有問題,frameRate = 5;//默認(rèn)幀率為5,實(shí)際上寫在視頻里是30秒! 問題出在幀率與截屏間隔不對(duì)。`//this.videoStreamer = new ScreenCaptureStream(this.screenArea);//要加錄制間隔時(shí)間this.videoStreamer = new ScreenCaptureStream(this.screenArea, 1000 / frameRate); `另外 this.stopWatch.Elapsed.Seconds >= 20要改成21,因?yàn)榇笥诘扔?0的話就停止的話實(shí)際上就只錄了19秒,所有要改為21 `if (this.stopWatch.Elapsed.Seconds >= 21)源碼、播放器、AForge.NET Framework-2.2.5.exe下載地址:
鏈接:https://pan.baidu.com/s/11O8z8Fj4JyMqgQ3ybxZ3ZQ
提取碼:5fxo
參考鏈接:
?http://www.diqisoft.com/technical/20087.htm
https://www.cnblogs.com/yyq745201/p/5334294.html
技術(shù)群:?需要進(jìn)技術(shù)群學(xué)習(xí)交流的請(qǐng)?zhí)砑有【幬⑿?#xff0c;切記備注:加群,對(duì)以上內(nèi)容有什么疑問也可以直接和小編直接溝通交流!? ???
小編微信:mm1552923 ??
公眾號(hào):dotNet編程大全? ? ??
總結(jié)
以上是生活随笔為你收集整理的c#屏幕录制(经典)(含源码和AForge.Video.FFMPEG.DLL)及填坑办法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 推荐:Flowchart 一种通过文本方
- 下一篇: C# 扩展集合ObservableCol