星火微课系统客户端
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.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading;
  7. using VisioForge.Shared.NAudio.CoreAudioApi;
  8. using VisioForge.Shared.NAudio.Wave;
  9. namespace Common.system
  10. {
  11. /// <summary>
  12. /// ffmpeg帮助类
  13. /// 创建人:赵耀
  14. /// 创建时间::2020年8月22日
  15. /// 需要安装\ffmpeg\bin\Setup Screen Capturer Recorder v0.12.10.exe
  16. /// 本地调试需要配置环境变量 将ffmpeg.exe位置配置到环境变量的path中
  17. /// </summary>
  18. public class FFMpeg
  19. {
  20. public Process myProcess = null;
  21. /// <summary>
  22. /// 是否输出录课录屏日志
  23. /// </summary>
  24. public bool OutputVideoLog = FileToolsCommon.GetConfigValue("OutputVideoLog") != "0";
  25. /// <summary>
  26. /// ffmpeg输出日志文件地址 每个文件2MB左右
  27. /// </summary>
  28. private string LogPath = "";
  29. /// <summary>
  30. /// 是否可以录制扬声器
  31. /// </summary>
  32. private bool IsRecordSpeaker = true;
  33. /// <summary>
  34. /// 是否可以录制麦克风
  35. /// </summary>
  36. private bool IsRecordMicrophone = true;
  37. /// <summary>
  38. /// 录制屏幕
  39. /// </summary>
  40. /// <param name="FilePath">文件存储路径</param>
  41. public void StartRecordingVideo(string FilePath)
  42. {
  43. while (myProcess != null)
  44. {
  45. Thread.Sleep(100);
  46. }
  47. //路径
  48. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  49. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  50. //文件保存路径
  51. string PathName = GetRSFilePathName(FilePath);
  52. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  53. foreach (Process KillProcess in KillProcessArray)
  54. {
  55. KillProcess.Kill();
  56. }
  57. myProcess = new Process();
  58. LogPath = CreateffmpegLog();
  59. string MicrophoneName = null;
  60. #region 检测麦克风扬声器
  61. string audioSpeakerPath = FileToolsCommon.GetFileAbsolutePath("adoS.m");
  62. string audioMicrophonePath = FileToolsCommon.GetFileAbsolutePath("adoM.m");
  63. try
  64. {
  65. FileToolsCommon.DeleteFile(audioSpeakerPath);
  66. FileToolsCommon.DeleteFile(audioMicrophonePath);
  67. }
  68. catch (Exception)
  69. {
  70. }
  71. if (StartRecordSpeakerAudio(audioSpeakerPath))
  72. {
  73. IsRecordSpeaker = true;
  74. }
  75. else
  76. {
  77. //无法录制扬声器音频
  78. IsRecordSpeaker = false;
  79. }
  80. StopRecordAudio(2);
  81. Thread.Sleep(100);
  82. if (StartRecordAudio(audioMicrophonePath))
  83. {
  84. IsRecordMicrophone = true;
  85. }
  86. else
  87. {
  88. //无法录制麦克风
  89. IsRecordMicrophone = false;
  90. }
  91. StopRecordAudio(1);
  92. #endregion
  93. myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  94. string SpeakerStr = "";
  95. string MicrophoneStr = "";
  96. if (IsRecordSpeaker)
  97. {
  98. SpeakerStr = "-f dshow -i audio=\"virtual-audio-capturer\" ";
  99. }
  100. if (IsRecordMicrophone)
  101. {
  102. MicrophoneName = GetInDeviceName();
  103. if (!string.IsNullOrWhiteSpace(MicrophoneName))
  104. {
  105. MicrophoneStr = "-f dshow -i audio=\"" + MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 ";
  106. }
  107. }
  108. switch (Extension.ToUpper())
  109. {
  110. case ".MP4":
  111. myProcess.StartInfo.Arguments = SpeakerStr + MicrophoneStr + " -f gdigrab -i desktop -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec aac " + PathName;
  112. //myProcess.StartInfo.Arguments = SpeakerStr + MicrophoneStr + " -f dshow -i video=\"screen-capture-recorder\" -pix_fmt yuv420p -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec aac " + PathName;
  113. //if (string.IsNullOrWhiteSpace(MicrophoneName))
  114. //{
  115. // 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 " + PathName; //ffmpeg的参数
  116. //}
  117. //else
  118. //{
  119. // myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  120. // 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的参数
  121. //}
  122. break;
  123. case ".AVI":
  124. case ".FLV":
  125. myProcess.StartInfo.Arguments = SpeakerStr + MicrophoneStr + " -f gdigrab -i desktop -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec aac -f " + Extension.ToLower().Replace(".", "") + " " + PathName;
  126. //if (string.IsNullOrWhiteSpace(MicrophoneName))
  127. //{
  128. // 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的参数
  129. //}
  130. //else
  131. //{
  132. // myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  133. // 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的参数
  134. //}
  135. break;
  136. default:
  137. myProcess.StartInfo.Arguments = SpeakerStr + MicrophoneStr + " -f gdigrab -i desktop -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec aac " + PathName;
  138. break;
  139. }
  140. if (OutputVideoLog)
  141. {
  142. LogHelper.WriteInfoLog("【录屏】:" + myProcess.StartInfo.Arguments);
  143. }
  144. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  145. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  146. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  147. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  148. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  149. myProcess.Start();
  150. myProcess.BeginErrorReadLine();
  151. }
  152. /// <summary>
  153. /// 录制音频
  154. /// </summary>
  155. /// <param name="Mp3Path">MP3音频位置</param>
  156. public bool StartRecordingAudio(string Mp3Path)
  157. {
  158. while (myProcess != null)
  159. {
  160. Thread.Sleep(100);
  161. }
  162. //路径
  163. string Path = FileToolsCommon.GetDirectoryName(Mp3Path);
  164. //文件保存路径
  165. string PathName = GetFilePathName(Mp3Path);
  166. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  167. foreach (Process KillProcess in KillProcessArray)
  168. {
  169. KillProcess.Kill();
  170. }
  171. string MicrophoneName = null;
  172. #region 检测麦克风扬声器
  173. string audioSpeakerPath = FileToolsCommon.GetFileAbsolutePath("adoS.m");
  174. string audioMicrophonePath = FileToolsCommon.GetFileAbsolutePath("adoM.m");
  175. try
  176. {
  177. FileToolsCommon.DeleteFile(audioSpeakerPath);
  178. FileToolsCommon.DeleteFile(audioMicrophonePath);
  179. }
  180. catch (Exception)
  181. {
  182. }
  183. if (StartRecordSpeakerAudio(audioSpeakerPath))
  184. {
  185. IsRecordSpeaker = true;
  186. }
  187. else
  188. {
  189. //无法录制扬声器音频
  190. IsRecordSpeaker = false;
  191. }
  192. StopRecordAudio(2);
  193. Thread.Sleep(100);
  194. if (StartRecordAudio(audioMicrophonePath))
  195. {
  196. IsRecordMicrophone = true;
  197. MicrophoneName = GetInDeviceName();
  198. }
  199. else
  200. {
  201. //无法录制麦克风
  202. IsRecordMicrophone = false;
  203. }
  204. StopRecordAudio(1);
  205. if (!IsRecordSpeaker && !IsRecordMicrophone)
  206. {
  207. return false;
  208. }
  209. #endregion
  210. myProcess = new Process();
  211. LogPath = CreateffmpegLog();
  212. myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  213. if (IsRecordSpeaker && IsRecordMicrophone)
  214. {
  215. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  216. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 " + PathName;
  217. }
  218. else if (IsRecordSpeaker)
  219. {
  220. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" " + PathName;
  221. }
  222. else if (IsRecordMicrophone)
  223. {
  224. //录制麦克风
  225. if (!string.IsNullOrWhiteSpace(MicrophoneName))
  226. {
  227. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"" +
  228. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 " + PathName;
  229. }
  230. }
  231. if (OutputVideoLog)
  232. LogHelper.WriteInfoLog("【录音】:" + myProcess.StartInfo.Arguments);
  233. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  234. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  235. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  236. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  237. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  238. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  239. myProcess.Start(); //启动线程
  240. myProcess.BeginErrorReadLine(); //开始异步读取
  241. return true;
  242. }
  243. /// <summary>
  244. /// 暂停录制
  245. /// </summary>
  246. public bool SuspendFFmpeg()
  247. {
  248. if (myProcess != null)
  249. {
  250. myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制
  251. myProcess.Close();//关闭进程
  252. myProcess.Dispose();//释放资源
  253. }
  254. #region 进程是否已经释放
  255. bool IsRunning = true;
  256. while (IsRunning)
  257. {
  258. IsRunning = false;
  259. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  260. foreach (Process KillProcess in ProcessArray)
  261. {
  262. IsRunning = true;
  263. Thread.Sleep(100);
  264. }
  265. }
  266. #endregion
  267. myProcess = null;
  268. return true;
  269. //myProcess.Kill();
  270. }
  271. /// <summary>
  272. /// 停止录制并合成文件
  273. /// </summary>
  274. /// <param name="FilePath">文件存储路径 必须与开始时的路径完全一致</param>
  275. public bool StopFFmpeg(string FilePath)
  276. {
  277. //文件完整路径
  278. if (myProcess != null)
  279. {
  280. myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制
  281. myProcess.Close();//关闭进程
  282. myProcess.Dispose();//释放资源
  283. }
  284. #region 进程是否已经释放
  285. bool IsRunning = true;
  286. while (IsRunning)
  287. {
  288. IsRunning = false;
  289. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  290. foreach (Process KillProcess in ProcessArray)
  291. {
  292. IsRunning = true;
  293. Thread.Sleep(100);
  294. }
  295. }
  296. #endregion
  297. myProcess = null;
  298. Thread t1 = new Thread(MergeVideoOrAudio);
  299. t1.Start(FilePath);
  300. return true;
  301. //文件合成
  302. }
  303. /// <summary>
  304. /// 图片音频合成视频
  305. /// </summary>
  306. /// <param name="ImageListPath">图片列表位置 命名为1.png 2.png</param>
  307. /// <param name="Mp3Path">MP3音频位置</param>
  308. /// <param name="VideoSavePath">视频保存位置</param>
  309. /// <param name="Frequency">每秒帧率</param>
  310. /// <param name="VideoWidth">视频宽度</param>
  311. /// <param name="VideoHeight">视频高度</param>
  312. public bool SynthesisVideo(string ImageListPath, string Mp3Path, string VideoSavePath, int Frequency, int VideoWidth, int VideoHeight)
  313. {
  314. //new Thread(new ThreadStart(new Action(() =>
  315. //{
  316. //}))).Start();
  317. while (myProcess != null)
  318. {
  319. Thread.Sleep(100);
  320. }
  321. string Extension = FileToolsCommon.GetIOExtension(VideoSavePath);//扩展名
  322. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  323. //Debug.WriteLine(KillProcessArray.Length.ToString());
  324. foreach (Process KillProcess in KillProcessArray)
  325. {
  326. KillProcess.Kill();
  327. }
  328. myProcess = new Process();
  329. try
  330. {
  331. LogPath = CreateffmpegLog();
  332. string mp3Str = "";
  333. #region 判断音频是否存在
  334. if (!string.IsNullOrWhiteSpace(Mp3Path))
  335. {
  336. if (FileToolsCommon.IsExistFile(Mp3Path))
  337. {
  338. mp3Str = "-i " + Mp3Path;
  339. }
  340. }
  341. #endregion
  342. myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  343. switch (Extension.ToUpper())
  344. {
  345. case ".MP4":
  346. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  347. ImageListPath + @"%d.png " +
  348. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
  349. VideoSavePath;
  350. break;
  351. case ".AVI":
  352. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  353. ImageListPath + @"%d.png " +
  354. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -f AVI " +
  355. VideoSavePath;
  356. break;
  357. case ".FLV":
  358. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  359. ImageListPath + @"%d.png " +
  360. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -f FLV " +
  361. VideoSavePath;
  362. break;
  363. default:
  364. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  365. ImageListPath + @"%d.png " +
  366. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
  367. VideoSavePath;
  368. break;
  369. }
  370. if (OutputVideoLog)
  371. LogHelper.WriteInfoLog("【图片音频合成视频】:" + myProcess.StartInfo.Arguments);
  372. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  373. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  374. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  375. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  376. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  377. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  378. myProcess.Start(); //启动线程
  379. myProcess.BeginErrorReadLine(); //开始异步读取
  380. myProcess.WaitForExit(); //阻塞等待进程结束
  381. myProcess.Close(); //关闭进程
  382. myProcess.Dispose(); //释放资源
  383. #region 进程是否已经释放
  384. bool IsRunning = true;
  385. while (IsRunning)
  386. {
  387. IsRunning = false;
  388. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  389. foreach (Process KillProcess in ProcessArray)
  390. {
  391. IsRunning = true;
  392. Thread.Sleep(100);
  393. }
  394. }
  395. #endregion
  396. myProcess = null;
  397. //生成视频后删除图片和音频保存列表
  398. //FileToolsCommon.DeleteDirectory(ImageListPath);
  399. //FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(Mp3Path));
  400. Thread.Sleep(100);
  401. FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(VideoSavePath) + "temp");
  402. return true;
  403. }
  404. catch (Exception ex)
  405. {
  406. LogHelper.WriteErrLog("【视频合成】(SynthesisVideo)视频生成失败," + ex.Message, ex);
  407. return false;
  408. }
  409. //Dispatcher.Invoke(() =>
  410. //{
  411. //});
  412. //// 允许不同线程间的调用
  413. //Control.CheckForIllegalCrossThreadCalls = false;
  414. }
  415. /// <summary>
  416. /// 视频音频合成
  417. /// </summary>
  418. /// <param name="FilePath">存储路径</param>
  419. public void MergeVideoOrAudio(object FilePathobj)
  420. {
  421. //new Thread(new ThreadStart(new Action(() =>
  422. //{
  423. //Dispatcher.Invoke(() =>
  424. //{
  425. //});
  426. //}))).Start();
  427. while (myProcess != null)
  428. {
  429. Thread.Sleep(100);
  430. }
  431. //路径
  432. string FilePath = (string)FilePathobj;
  433. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  434. //扩展名
  435. string Extension = FileToolsCommon.GetIOExtension(FilePath);
  436. //Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  437. //foreach (var KillProcess in KillProcessArray)
  438. //{
  439. // KillProcess.Kill();
  440. //}
  441. #region 判断文件是否存在
  442. if (Extension.ToUpper() == ".MP3")
  443. {
  444. if (!FileToolsCommon.IsExistFile(Path + "temp/filelist.d"))
  445. {
  446. return;
  447. }
  448. }
  449. else
  450. {
  451. if (!FileToolsCommon.IsExistFile(Path + "temprs/filelist.d"))
  452. {
  453. return;
  454. }
  455. }
  456. #endregion
  457. myProcess = new Process();
  458. LogPath = CreateffmpegLog();
  459. myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  460. if (Extension.ToUpper() == ".MP3")
  461. {
  462. myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "temp/filelist.d -c copy " + FilePath;
  463. }
  464. else
  465. {
  466. myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "temprs/filelist.d -c copy " + FilePath;
  467. }
  468. if (OutputVideoLog)
  469. LogHelper.WriteInfoLog("【音视频合成】:" + myProcess.StartInfo.Arguments);
  470. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  471. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  472. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  473. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  474. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  475. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  476. myProcess.Start(); //启动线程
  477. myProcess.BeginErrorReadLine(); //开始异步读取
  478. myProcess.WaitForExit(); //阻塞等待进程结束
  479. myProcess.Close(); //关闭进程
  480. myProcess.Dispose(); //释放资源
  481. #region 进程是否已经释放
  482. bool IsRunning = true;
  483. while (IsRunning)
  484. {
  485. IsRunning = false;
  486. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  487. foreach (Process KillProcess in ProcessArray)
  488. {
  489. IsRunning = true;
  490. Thread.Sleep(100);
  491. }
  492. }
  493. #endregion
  494. myProcess = null;
  495. if (Extension.ToUpper() == ".MP3")
  496. {
  497. FileToolsCommon.DeleteDirectory(Path + "temp/");
  498. }
  499. else
  500. {
  501. FileToolsCommon.DeleteDirectory(Path + "temprs/");
  502. }
  503. }
  504. /// <summary>
  505. /// 获取文件完整路径 音频
  506. /// </summary>
  507. /// <param name="FilePath">文件路径</param>
  508. /// <returns></returns>
  509. private string GetFilePathName(string FilePath)
  510. {
  511. //路径
  512. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  513. //Path.GetFileName(FilePath);//获取文件名
  514. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  515. string tempFilePath = Path + "temp/";
  516. //创建临时目录
  517. FileToolsCommon.CreateDirectory(tempFilePath);
  518. //创建记录文件
  519. if (!FileToolsCommon.IsExistFile(tempFilePath + "filelist.d"))
  520. {
  521. FileToolsCommon.CreateFile(tempFilePath + "filelist.d");
  522. }
  523. int num = 1;
  524. string CompleteFilePath = tempFilePath + num + Extension;
  525. while (FileToolsCommon.IsExistFile(CompleteFilePath))
  526. {
  527. num++;
  528. CompleteFilePath = tempFilePath + num + Extension;
  529. }
  530. FileToolsCommon.AppendText(tempFilePath + "filelist.d", "file ./" + num + Extension + "\r\n");
  531. return CompleteFilePath;
  532. }
  533. /// <summary>
  534. /// 获取文件完整路径 录屏
  535. /// </summary>
  536. /// <param name="FilePath">文件路径</param>
  537. /// <returns></returns>
  538. private string GetRSFilePathName(string FilePath)
  539. {
  540. //路径
  541. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  542. //Path.GetFileName(FilePath);//获取文件名
  543. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  544. string tempFilePath = Path + "temprs/";
  545. //创建临时目录
  546. FileToolsCommon.CreateDirectory(tempFilePath);
  547. //创建记录文件
  548. if (!FileToolsCommon.IsExistFile(tempFilePath + "filelist.d"))
  549. {
  550. FileToolsCommon.CreateFile(tempFilePath + "filelist.d");
  551. }
  552. int num = 1;
  553. string CompleteFilePath = tempFilePath + num + Extension;
  554. while (FileToolsCommon.IsExistFile(CompleteFilePath))
  555. {
  556. num++;
  557. CompleteFilePath = tempFilePath + num + Extension;
  558. }
  559. FileToolsCommon.AppendText(tempFilePath + "filelist.d", "file ./" + num + Extension + "\r\n");
  560. return CompleteFilePath;
  561. }
  562. /// <summary>
  563. /// 生成缩略图
  564. /// </summary>
  565. /// <param name="VideoPath">视频地址</param>
  566. /// <param name="ImagePath">图片地址</param>
  567. /// <param name="Width">图片大小</param>
  568. /// <param name="Height">图片大小</param>
  569. public bool GenerateThumbnails(string VideoPath, string ImagePath,int Width=0,int Height=0)
  570. {
  571. while (myProcess != null)
  572. {
  573. Thread.Sleep(100);
  574. }
  575. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  576. foreach (Process KillProcess in KillProcessArray)
  577. {
  578. KillProcess.Kill();
  579. }
  580. string WHStr = "";
  581. if(Width>0)
  582. {
  583. WHStr = " -s "+ Width + "x"+ Height;
  584. }
  585. try
  586. {
  587. myProcess = new Process();
  588. LogPath = CreateffmpegLog();
  589. myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  590. //myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2 -s " + thubWidth + "*" + thubHeight + " -f image2 \"" + ImagePath + "\"";
  591. myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2"+ WHStr + " -f image2 \"" + ImagePath + "\"";
  592. if (OutputVideoLog)
  593. LogHelper.WriteInfoLog("【生成缩略图】:" + myProcess.StartInfo.Arguments);
  594. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  595. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  596. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  597. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  598. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  599. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  600. myProcess.Start(); //启动线程
  601. myProcess.BeginErrorReadLine(); //开始异步读取
  602. myProcess.WaitForExit(); //阻塞等待进程结束
  603. myProcess.Close(); //关闭进程
  604. myProcess.Dispose(); //释放资源
  605. #region 进程是否已经释放
  606. bool IsRunning = true;
  607. while (IsRunning)
  608. {
  609. IsRunning = false;
  610. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  611. foreach (Process KillProcess in ProcessArray)
  612. {
  613. IsRunning = true;
  614. Thread.Sleep(100);
  615. }
  616. }
  617. #endregion
  618. myProcess = null;
  619. return true;
  620. }
  621. catch (Exception ex)
  622. {
  623. LogHelper.WriteErrLog("【生成缩略图】(GenerateThumbnails)缩略图生成失败," + ex.Message, ex);
  624. return false;
  625. }
  626. }
  627. /// <summary>
  628. /// 视频转码
  629. /// </summary>
  630. /// <param name="VideoPathName">源视频</param>
  631. /// <param name="VideoSavePathName">目标视频存储路径</param>
  632. /// <param name="Width">宽</param>
  633. /// <param name="Height">高</param>
  634. /// <returns></returns>
  635. public bool VideoTranscode(string VideoPathName, string VideoSavePathName, int Width, int Height)
  636. {
  637. while (myProcess != null)
  638. {
  639. Thread.Sleep(100);
  640. }
  641. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  642. foreach (Process KillProcess in KillProcessArray)
  643. {
  644. KillProcess.Kill();
  645. }
  646. try
  647. {
  648. myProcess = new Process();
  649. LogPath = CreateffmpegLog();
  650. myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
  651. myProcess.StartInfo.Arguments = "-i " + VideoPathName + " -acodec copy -vcodec libx264 -s " + Width + "*" + Height + " " + VideoSavePathName;
  652. if (OutputVideoLog)
  653. LogHelper.WriteInfoLog("【视频转码】:" + myProcess.StartInfo.Arguments);
  654. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  655. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  656. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  657. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  658. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  659. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  660. myProcess.Start(); //启动线程
  661. myProcess.BeginErrorReadLine(); //开始异步读取
  662. myProcess.WaitForExit(); //阻塞等待进程结束
  663. myProcess.Close(); //关闭进程
  664. myProcess.Dispose(); //释放资源
  665. #region 进程是否已经释放
  666. bool IsRunning = true;
  667. while (IsRunning)
  668. {
  669. IsRunning = false;
  670. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  671. foreach (Process KillProcess in ProcessArray)
  672. {
  673. IsRunning = true;
  674. Thread.Sleep(100);
  675. }
  676. }
  677. #endregion
  678. myProcess = null;
  679. Thread.Sleep(200);
  680. FileToolsCommon.DeleteFile(VideoPathName);
  681. return true;
  682. }
  683. catch (Exception ex)
  684. {
  685. LogHelper.WriteErrLog("【视频转码】(VideoTranscode)视频转码失败," + ex.Message, ex);
  686. return false;
  687. }
  688. }
  689. /// <summary>
  690. /// 创建日志文件
  691. /// </summary>
  692. /// <returns></returns>
  693. private string CreateffmpegLog()
  694. {
  695. string LogFileName = DateTime.Now.ToString("yyyyMMdd");
  696. string LogFilePath = FileToolsCommon.GetFileAbsolutePath("/Log/FFMpegLog/");
  697. FileToolsCommon.CreateDirectory(LogFilePath);
  698. string LogName = LogFilePath + LogFileName + ".log";
  699. try
  700. {
  701. if (!FileToolsCommon.IsExistFile(LogName))
  702. {
  703. FileToolsCommon.CreateFile(LogName);
  704. FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" +
  705. "****************************************************************************************************\r\n");
  706. return LogName;
  707. }
  708. else
  709. {
  710. int num = 0;
  711. while (FileToolsCommon.GetFileSizeByMB(LogName) > 2)
  712. {
  713. num++;
  714. LogName = LogFilePath + LogFileName + "_" + num + ".log";
  715. FileToolsCommon.CreateFile(LogName);
  716. }
  717. FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" +
  718. "****************************************************************************************************\r\n");
  719. return LogName;
  720. }
  721. }
  722. catch (Exception)
  723. {
  724. return LogName;
  725. }
  726. }
  727. /// <summary>
  728. /// 输出结果
  729. /// </summary>
  730. /// <param name="sendProcess"></param>
  731. /// <param name="output"></param>
  732. private void Output(object sendProcess, DataReceivedEventArgs output)
  733. {
  734. if (!string.IsNullOrEmpty(output.Data))
  735. {
  736. FileToolsCommon.AppendText(LogPath, output.Data);
  737. }
  738. }
  739. /// <summary>
  740. /// 获取麦克风
  741. /// </summary>
  742. private string GetMicrophone()
  743. {
  744. List<string> devs = new List<string>();
  745. MMDeviceEnumerator enumberator = new MMDeviceEnumerator();
  746. MMDeviceCollection deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
  747. for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++)
  748. {
  749. WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
  750. foreach (MMDevice device in deviceCollection)
  751. {
  752. try
  753. {
  754. if (device.FriendlyName.StartsWith(deviceInfo.ProductName))
  755. {
  756. devs.Add(device.FriendlyName);
  757. break;
  758. }
  759. }
  760. catch (Exception)
  761. {
  762. }
  763. }
  764. }
  765. if (devs.Count > 0)
  766. {
  767. return devs[0];
  768. }
  769. else
  770. {
  771. return "";
  772. }
  773. }
  774. /// <summary>
  775. /// 获取声音输入设备
  776. /// </summary>
  777. /// <returns></returns>
  778. public static string GetInDeviceName()
  779. {
  780. var devices = new List<WaveInCapabilities>();
  781. int waveInDevices = WaveIn.DeviceCount;
  782. for (int i = 0; i < waveInDevices; i++)
  783. {
  784. devices.Add(WaveIn.GetCapabilities(i));
  785. }
  786. List<string> devs = new List<string>();
  787. devs = devices.Select(item => item.ProductName).ToList();
  788. if (devs.Count > 0)
  789. {
  790. byte[] buffer = Encoding.UTF8.GetBytes(devs[0]);
  791. string MicrophoneName = Encoding.UTF8.GetString(buffer); // 统一使用UTF-8
  792. return MicrophoneName;
  793. }
  794. else
  795. {
  796. return "";
  797. }
  798. }
  799. /// <summary>
  800. /// 获取声音输入设备完整名称
  801. /// </summary>
  802. /// <returns></returns>
  803. public static string GetInDeviceFull()
  804. {
  805. List<string> devices = new List<string>();
  806. var enumberator = new MMDeviceEnumerator();
  807. var deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
  808. for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++)
  809. {
  810. WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
  811. foreach (MMDevice device in deviceCollection)
  812. {
  813. try
  814. {
  815. if (device.FriendlyName.StartsWith(deviceInfo.ProductName))
  816. {
  817. devices.Add(device.FriendlyName);
  818. break;
  819. }
  820. }
  821. catch (Exception ex)
  822. {
  823. continue;
  824. }
  825. }
  826. }
  827. if (devices.Count > 0)
  828. {
  829. byte[] buffer = Encoding.UTF8.GetBytes(devices[0]);
  830. string MicrophoneName = Encoding.UTF8.GetString(buffer); // 统一使用UTF-8
  831. return MicrophoneName;
  832. }
  833. else
  834. {
  835. return "";
  836. }
  837. }
  838. /// <summary>
  839. /// 获取声音输入设备完整名称
  840. /// </summary>
  841. /// <returns></returns>
  842. public List<string> GetInDeviceListFull()
  843. {
  844. List<string> devices = new List<string>();
  845. var enumberator = new MMDeviceEnumerator();
  846. var deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
  847. for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++)
  848. {
  849. WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
  850. foreach (MMDevice device in deviceCollection)
  851. {
  852. try
  853. {
  854. if (device.FriendlyName.StartsWith(deviceInfo.ProductName))
  855. {
  856. devices.Add(device.FriendlyName);
  857. break;
  858. }
  859. }
  860. catch (Exception ex)
  861. {
  862. continue;
  863. }
  864. }
  865. }
  866. return devices;
  867. }
  868. /// <summary>
  869. /// 录制麦克风的声音
  870. /// </summary>
  871. private WaveInEvent waveIn = null; //new WaveInEvent();
  872. /// <summary>
  873. /// 开始录制麦克风
  874. /// </summary>
  875. public bool StartRecordAudio(string audioFile)
  876. {
  877. try
  878. {
  879. waveIn = new WaveInEvent();
  880. //生成音频文件的对象
  881. WaveFileWriter writer = new WaveFileWriter(audioFile, waveIn.WaveFormat);
  882. //开始录音,写数据
  883. waveIn.DataAvailable += (s, a) =>
  884. {
  885. writer.Write(a.Buffer, 0, a.BytesRecorded);
  886. };
  887. //结束录音
  888. waveIn.RecordingStopped += (s, a) =>
  889. {
  890. writer.Dispose();
  891. writer = null;
  892. waveIn.Dispose();
  893. waveIn = null;
  894. try
  895. {
  896. FileToolsCommon.DeleteFile(audioFile);
  897. }
  898. catch (Exception)
  899. {
  900. }
  901. };
  902. waveIn.StartRecording();
  903. return true;
  904. }
  905. catch (Exception ex)
  906. {
  907. LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex);
  908. return false;
  909. }
  910. }
  911. /// <summary>
  912. /// 结束录制音频
  913. /// </summary>
  914. /// <param name="type">1麦克风 2扬声器</param>
  915. public void StopRecordAudio(int type)
  916. {
  917. try
  918. {
  919. if (type == 1)
  920. {
  921. if (waveIn != null)
  922. {
  923. try
  924. {
  925. waveIn.StopRecording();
  926. }
  927. catch (Exception)
  928. {
  929. waveIn = null;
  930. }
  931. }
  932. }
  933. else if (type == 2)
  934. {
  935. if (capture != null)
  936. {
  937. try
  938. {
  939. capture.StopRecording();
  940. }
  941. catch (Exception)
  942. {
  943. capture = null;
  944. }
  945. }
  946. }
  947. }
  948. catch (Exception ex)
  949. {
  950. LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex);
  951. }
  952. }
  953. /// <summary>
  954. /// 录制扬声器的声音
  955. /// </summary>
  956. private WasapiLoopbackCapture capture = null; //new WasapiLoopbackCapture();
  957. /// <summary>
  958. /// 开始录制扬声器
  959. /// </summary>
  960. public bool StartRecordSpeakerAudio(string audioFile)
  961. {
  962. try
  963. {
  964. capture = new WasapiLoopbackCapture();
  965. //生成音频文件的对象
  966. WaveFileWriter writer = new WaveFileWriter(audioFile, capture.WaveFormat);
  967. capture.DataAvailable += (s, a) =>
  968. {
  969. writer.Write(a.Buffer, 0, a.BytesRecorded);
  970. };
  971. //结束录音
  972. capture.RecordingStopped += (s, a) =>
  973. {
  974. writer.Dispose();
  975. writer = null;
  976. capture.Dispose();
  977. capture = null;
  978. try
  979. {
  980. FileToolsCommon.DeleteFile(audioFile);
  981. }
  982. catch (Exception)
  983. {
  984. }
  985. };
  986. capture.StartRecording();
  987. return true;
  988. }
  989. catch (Exception ex)
  990. {
  991. LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex);
  992. return false;
  993. }
  994. }
  995. }
  996. }