星火微课系统客户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading;
  8. using VisioForge.Shared.NAudio.CoreAudioApi;
  9. using VisioForge.Shared.NAudio.Wave;
  10. namespace Common.system
  11. {
  12. /// <summary>
  13. /// ffmpeg帮助类
  14. /// 创建人:赵耀
  15. /// 创建时间::2020年8月22日
  16. /// 需要安装\ffmpeg\bin\Setup Screen Capturer Recorder v0.12.10.exe
  17. /// 本地调试需要配置环境变量 将ffmpeg.exe位置配置到环境变量的path中
  18. /// </summary>
  19. public class FFMpeg
  20. {
  21. Process myProcess = null;
  22. /// <summary>
  23. /// ffmpeg输出日志文件地址 每个文件2MB左右
  24. /// </summary>
  25. string LogPath = "";
  26. /// <summary>
  27. /// 录制屏幕
  28. /// </summary>
  29. /// <param name="FilePath">文件存储路径</param>
  30. public void StartRecordingVideo(string FilePath)
  31. {
  32. //路径
  33. string Path = FileToolsCommon.GetDirectoryName(FilePath) + @"\";
  34. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  35. //文件保存路径
  36. string PathName = GetFilePathName(FilePath);
  37. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  38. Debug.WriteLine(KillProcessArray.Length.ToString());
  39. foreach (var KillProcess in KillProcessArray)
  40. {
  41. KillProcess.Kill();
  42. }
  43. myProcess = new Process();
  44. LogPath = CreateffmpegLog();
  45. string MicrophoneName = GetMicrophone();
  46. myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  47. switch (Extension.ToUpper())
  48. {
  49. case "MP4":
  50. if (string.IsNullOrWhiteSpace(MicrophoneName))
  51. {
  52. myProcess.StartInfo.Arguments = "-f dshow -i video=\"screen-capture-recorder\" -f dshow -i audio=\"virtual-audio-capturer\" -vcodec libx264 -acodec libmp3lame -r 15 -crf 22 -f avi " + PathName; //ffmpeg的参数
  53. }
  54. else
  55. {
  56. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  57. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -f dshow -i video=\"screen-capture-recorder\" -pix_fmt yuv420p -vcodec h264 -preset:v ultrafast -tune:v zerolatency -acodec aac -ar 44100 -ac 2 " + PathName; //ffmpeg的参数
  58. }
  59. break;
  60. case "AVI":
  61. case "FLV":
  62. if (string.IsNullOrWhiteSpace(MicrophoneName))
  63. {
  64. myProcess.StartInfo.Arguments = "-f dshow -i video=\"screen-capture-recorder\" -f dshow -i audio=\"virtual-audio-capturer\" -vcodec libx264 -acodec libmp3lame -r 15 -crf 22 -f " + Extension.ToLower() + " " + PathName; //ffmpeg的参数
  65. }
  66. else
  67. {
  68. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  69. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -f dshow -i video=\"screen-capture-recorder\" -pix_fmt yuv420p -vcodec h264 -preset:v ultrafast -tune:v zerolatency -acodec aac -ar 44100 -ac 2 -f " + Extension.ToLower() + " " + PathName; //ffmpeg的参数
  70. }
  71. break;
  72. default:
  73. if (string.IsNullOrWhiteSpace(MicrophoneName))
  74. {
  75. myProcess.StartInfo.Arguments = "-f dshow -i video=\"screen-capture-recorder\" -f dshow -i audio=\"virtual-audio-capturer\" -vcodec libx264 -acodec libmp3lame -r 15 -crf 22 -f avi " + PathName; //ffmpeg的参数
  76. }
  77. else
  78. {
  79. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  80. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -f dshow -i video=\"screen-capture-recorder\" -pix_fmt yuv420p -vcodec h264 -preset:v ultrafast -tune:v zerolatency -acodec aac -ar 44100 -ac 2 " + PathName; //ffmpeg的参数
  81. }
  82. break;
  83. }
  84. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  85. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  86. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  87. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  88. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  89. myProcess.Start();
  90. myProcess.BeginErrorReadLine();
  91. }
  92. /// <summary>
  93. /// 录制音频
  94. /// </summary>
  95. /// <param name="Mp3Path">MP3音频位置</param>
  96. private void StartRecordingAudio(string Mp3Path)
  97. {
  98. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  99. Debug.WriteLine(KillProcessArray.Length.ToString());
  100. foreach (var KillProcess in KillProcessArray)
  101. {
  102. KillProcess.Kill();
  103. }
  104. myProcess = new Process();
  105. LogPath = CreateffmpegLog();
  106. this.myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  107. string MicrophoneName = GetMicrophone();
  108. if (string.IsNullOrWhiteSpace(MicrophoneName))
  109. {
  110. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" " + Mp3Path;
  111. }
  112. else
  113. {
  114. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  115. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 " + Mp3Path;
  116. }
  117. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  118. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  119. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  120. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  121. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  122. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  123. myProcess.Start(); //启动线程
  124. myProcess.BeginErrorReadLine(); //开始异步读取
  125. }
  126. /// <summary>
  127. /// 暂停录制
  128. /// </summary>
  129. public void SuspendFFmpeg()
  130. {
  131. if (myProcess == null)
  132. return;
  133. myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制
  134. myProcess.Close();//关闭进程
  135. myProcess.Dispose();//释放资源
  136. myProcess = null;
  137. //myProcess.Kill();
  138. }
  139. /// <summary>
  140. /// 停止录制并合成文件
  141. /// </summary>
  142. /// <param name="FilePath">文件存储路径 必须与开始时的路径完全一致</param>
  143. public void StopFFmpeg(string FilePath)
  144. {
  145. //文件完整路径
  146. if (myProcess == null)
  147. return;
  148. myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制
  149. myProcess.Close();//关闭进程
  150. myProcess.Dispose();//释放资源
  151. myProcess = null;
  152. Thread t1 = new Thread(MergeVideoOrAudio);
  153. t1.Start(FilePath);
  154. //文件合成
  155. }
  156. /// <summary>
  157. /// 图片音频合成视频
  158. /// </summary>
  159. /// <param name="ImageListPath">图片列表位置 命名为1.png 2.png</param>
  160. /// <param name="Mp3Path">MP3音频位置</param>
  161. /// <param name="VideoSavePath">视频保存位置</param>
  162. /// <param name="Frequency">每秒帧率</param>
  163. /// <param name="VideoWidth">视频宽度</param>
  164. /// <param name="VideoHeight">视频高度</param>
  165. private void SynthesisVideo(string ImageListPath, string Mp3Path, string VideoSavePath, int Frequency, int VideoWidth, int VideoHeight)
  166. {
  167. Thread thread = new Thread(delegate ()
  168. {
  169. //// 允许不同线程间的调用
  170. //Control.CheckForIllegalCrossThreadCalls = false;
  171. Thread.Sleep(500);
  172. string Extension = FileToolsCommon.GetIOExtension(VideoSavePath);//扩展名
  173. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  174. Debug.WriteLine(KillProcessArray.Length.ToString());
  175. foreach (var KillProcess in KillProcessArray)
  176. {
  177. KillProcess.Kill();
  178. }
  179. myProcess = new Process();
  180. LogPath = CreateffmpegLog();
  181. this.myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  182. switch (Extension.ToUpper())
  183. {
  184. case "MP4":
  185. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  186. ImageListPath + @"%d.png -i " +
  187. Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
  188. VideoSavePath;
  189. break;
  190. case "AVI":
  191. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  192. ImageListPath + @"%d.png -i " +
  193. Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -f AVI " +
  194. VideoSavePath;
  195. break;
  196. case "FLV":
  197. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  198. ImageListPath + @"%d.png -i " +
  199. Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -f FLV " +
  200. VideoSavePath;
  201. break;
  202. default:
  203. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  204. ImageListPath + @"%d.png -i " +
  205. Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
  206. VideoSavePath;
  207. break;
  208. }
  209. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  210. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  211. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  212. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  213. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  214. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  215. myProcess.Start(); //启动线程
  216. myProcess.BeginErrorReadLine(); //开始异步读取
  217. myProcess.WaitForExit(); //阻塞等待进程结束
  218. myProcess.Close(); //关闭进程
  219. myProcess.Dispose(); //释放资源
  220. //生成视频后删除图片保存列表
  221. FileToolsCommon.DeleteDirectory(ImageListPath);
  222. });
  223. }
  224. /// <summary>
  225. /// 视频音频合成
  226. /// </summary>
  227. /// <param name="FilePath">存储路径</param>
  228. void MergeVideoOrAudio(object FilePathobj)
  229. {
  230. Thread.Sleep(500);
  231. //路径
  232. string FilePath = (string)FilePathobj;
  233. string Path = FileToolsCommon.GetDirectoryName(FilePath) + @"\";
  234. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  235. Debug.WriteLine(KillProcessArray.Length.ToString());
  236. foreach (var KillProcess in KillProcessArray)
  237. {
  238. KillProcess.Kill();
  239. }
  240. myProcess = new Process();
  241. LogPath = CreateffmpegLog();
  242. this.myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  243. myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "temp/filelist.d -c copy " + FilePath;
  244. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  245. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  246. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  247. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  248. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  249. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  250. myProcess.Start(); //启动线程
  251. myProcess.BeginErrorReadLine(); //开始异步读取
  252. myProcess.WaitForExit(); //阻塞等待进程结束
  253. myProcess.Close(); //关闭进程
  254. myProcess.Dispose(); //释放资源
  255. }
  256. /// <summary>
  257. /// 获取文件完整路径
  258. /// </summary>
  259. /// <param name="FilePath">文件路径</param>
  260. /// <returns></returns>
  261. string GetFilePathName(string FilePath)
  262. {
  263. //路径
  264. string Path = FileToolsCommon.GetDirectoryName(FilePath) + @"\";
  265. //Path.GetFileName(FilePath);//获取文件名
  266. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  267. string tempFilePath = Path + "temp/";
  268. //创建临时目录
  269. FileToolsCommon.CreateDirectory(tempFilePath);
  270. //创建记录文件
  271. if (!FileToolsCommon.IsExistFile(tempFilePath + "filelist.d"))
  272. {
  273. FileToolsCommon.CreateFile(tempFilePath + "filelist.d");
  274. }
  275. int num = 1;
  276. string CompleteFilePath = tempFilePath + num + Extension;
  277. while (FileToolsCommon.IsExistFile(CompleteFilePath))
  278. {
  279. num++;
  280. CompleteFilePath = tempFilePath + num + Extension;
  281. }
  282. FileToolsCommon.AppendText(tempFilePath + "filelist.d", "file ./" + num + Extension + "\r\n");
  283. return CompleteFilePath;
  284. }
  285. /// <summary>
  286. /// 生成缩略图
  287. /// </summary>
  288. /// <param name="VideoPath">视频地址</param>
  289. /// <param name="ImagePath">图片地址</param>
  290. void GenerateThumbnails(string VideoPath, string ImagePath)
  291. {
  292. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  293. Debug.WriteLine(KillProcessArray.Length.ToString());
  294. foreach (var KillProcess in KillProcessArray)
  295. {
  296. KillProcess.Kill();
  297. }
  298. myProcess = new Process();
  299. LogPath = CreateffmpegLog();
  300. this.myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  301. //myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2 -s " + thubWidth + "*" + thubHeight + " -f image2 \"" + ImagePath + "\"";
  302. myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2 -f image2 \"" + ImagePath + "\"";
  303. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  304. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  305. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  306. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  307. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  308. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  309. myProcess.Start(); //启动线程
  310. myProcess.BeginErrorReadLine(); //开始异步读取
  311. myProcess.WaitForExit(); //阻塞等待进程结束
  312. myProcess.Close(); //关闭进程
  313. myProcess.Dispose(); //释放资源
  314. }
  315. /// <summary>
  316. /// 创建日志文件
  317. /// </summary>
  318. /// <returns></returns>
  319. string CreateffmpegLog()
  320. {
  321. string LogFileName = DateTime.Now.ToString("yyyyMMdd");
  322. string LogFilePath = FileToolsCommon.GetFileAbsolutePath("/Log/FFMpegLog/");
  323. FileToolsCommon.CreateDirectory(LogFilePath);
  324. string LogName = LogFilePath + LogFileName + ".log";
  325. try
  326. {
  327. if (!FileToolsCommon.IsExistFile(LogName))
  328. {
  329. FileToolsCommon.CreateFile(LogName);
  330. FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" +
  331. "****************************************************************************************************\r\n");
  332. return LogName;
  333. }
  334. else
  335. {
  336. int num = 0;
  337. while (FileToolsCommon.GetFileSizeByMB(LogName) > 2)
  338. {
  339. num++;
  340. LogName = LogFilePath + LogFileName + "_" + num + ".log";
  341. FileToolsCommon.CreateFile(LogName);
  342. }
  343. FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" +
  344. "****************************************************************************************************\r\n");
  345. return LogName;
  346. }
  347. }
  348. catch (Exception)
  349. {
  350. return LogName;
  351. }
  352. }
  353. /// <summary>
  354. /// 输出结果
  355. /// </summary>
  356. /// <param name="sendProcess"></param>
  357. /// <param name="output"></param>
  358. private void Output(object sendProcess, DataReceivedEventArgs output)
  359. {
  360. if (!String.IsNullOrEmpty(output.Data))
  361. {
  362. FileToolsCommon.AppendText(LogPath, output.Data);
  363. }
  364. }
  365. //private void P_ErrorDataReceived(object sender, DataReceivedEventArgs e)
  366. //{
  367. // //+= new DataReceivedEventHandler((s, message) => { Console.WriteLine(message.Data); })
  368. // using (StreamWriter fs = new StreamWriter("E:\\项目\\测试\\Wpf测试\\bin\\Debug\\ffmpeg\\log.txt", true))
  369. // {
  370. // fs.WriteLine(e.Data);
  371. // Debug.WriteLine(output.Data.ToString());
  372. // }
  373. //}
  374. /// <summary>
  375. /// 获取麦克风
  376. /// </summary>
  377. string GetMicrophone()
  378. {
  379. List<string> devs = new List<string>();
  380. MMDeviceEnumerator enumberator = new MMDeviceEnumerator();
  381. MMDeviceCollection deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
  382. for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++)
  383. {
  384. WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
  385. foreach (MMDevice device in deviceCollection)
  386. {
  387. try
  388. {
  389. if (device.FriendlyName.StartsWith(deviceInfo.ProductName))
  390. {
  391. devs.Add(device.FriendlyName);
  392. break;
  393. }
  394. }
  395. catch (Exception)
  396. {
  397. }
  398. }
  399. }
  400. if (devs.Count > 0)
  401. {
  402. return devs[0];
  403. }
  404. else
  405. {
  406. return "";
  407. }
  408. }
  409. }
  410. }