星火微课系统客户端
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

FFMpeg.cs 40KB

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