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

FFMpeg.cs 63KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572
  1. using AForge.Video.DirectShow;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using VisioForge.Shared.NAudio.CoreAudioApi;
  10. using VisioForge.Shared.NAudio.Wave;
  11. namespace Common.system
  12. {
  13. /// <summary>
  14. /// ffmpeg帮助类 创建人:赵耀 创建时间::2020年8月22日 需要安装\SSCR\Setup Screen Capturer Recorder v0.12.10.exe
  15. /// 本地调试需要配置环境变量 将ffmpeg.exe位置配置到环境变量的path中
  16. /// </summary>
  17. public class FFMpeg
  18. {
  19. #region 变量
  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. private string ffmpegPath = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg.exe");
  38. #endregion 变量
  39. /// <summary>
  40. /// 录制屏幕
  41. /// </summary>
  42. /// <param name="FilePath">文件存储路径</param>
  43. /// <param name="size">大小</param>
  44. /// <param name="RecordingSound">录制音频</param>
  45. /// <param name="RecordingMicrophone">录制麦克风</param>
  46. /// <param name="ErrMessage">错误信息</param>
  47. /// <param name="MicrophoneName">麦克风名</param>
  48. /// <returns></returns>
  49. public bool StartRecordingVideo(string FilePath, Size size, bool RecordingSound, bool RecordingMicrophone, out string ErrMessage, string MicrophoneName = null)
  50. {
  51. while (myProcess != null)
  52. {
  53. Thread.Sleep(100);
  54. }
  55. ErrMessage = null;
  56. //路径
  57. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  58. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  59. //文件保存路径
  60. string PathName = GetRSFilePathName(FilePath);
  61. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  62. foreach (Process KillProcess in KillProcessArray)
  63. {
  64. KillProcess.Kill();
  65. }
  66. myProcess = new Process();
  67. LogPath = CreateffmpegLog();
  68. if (string.IsNullOrWhiteSpace(MicrophoneName))
  69. {
  70. MicrophoneName = GetMicrophoneName();
  71. }
  72. #region 检测麦克风扬声器
  73. string AudioPath = FileToolsCommon.GetFileAbsolutePath("/temp/audio/");
  74. FileToolsCommon.CreateDirectory(AudioPath);
  75. string audioSpeakerPath = AudioPath + "adoS" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m";//FileToolsCommon.GetFileAbsolutePath("adoS.m");
  76. string audioMicrophonePath = AudioPath + "adoM" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m";//FileToolsCommon.GetFileAbsolutePath("adoM.m");
  77. try
  78. {
  79. FileToolsCommon.DeleteFile(audioSpeakerPath);
  80. FileToolsCommon.DeleteFile(audioMicrophonePath);
  81. }
  82. catch (Exception)
  83. {
  84. }
  85. //是否录制音频
  86. if (RecordingSound)
  87. {
  88. if (StartRecordSpeakerAudio(audioSpeakerPath))
  89. {
  90. IsRecordSpeaker = true;
  91. }
  92. else
  93. {
  94. //无法录制扬声器音频
  95. IsRecordSpeaker = false;
  96. }
  97. StopRecordAudio(2);
  98. Thread.Sleep(100);
  99. }
  100. //是否录制麦克风
  101. if (RecordingMicrophone)
  102. {
  103. if (StartRecordAudio(audioMicrophonePath))
  104. {
  105. IsRecordMicrophone = true;
  106. }
  107. else
  108. {
  109. //无法录制麦克风
  110. IsRecordMicrophone = false;
  111. }
  112. StopRecordAudio(1);
  113. }
  114. #endregion 检测麦克风扬声器
  115. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  116. string SpeakerStr = "";
  117. string MicrophoneStr = "";
  118. if (IsRecordSpeaker && RecordingSound)
  119. {
  120. SpeakerStr = "-f dshow -i audio=\"virtual-audio-capturer\" ";
  121. }
  122. if (IsRecordMicrophone && RecordingMicrophone)
  123. {
  124. if (!string.IsNullOrWhiteSpace(MicrophoneName))
  125. {
  126. MicrophoneStr = "-f dshow -i audio=\"" + MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 ";
  127. }
  128. }
  129. switch (Extension.ToUpper())
  130. {
  131. case ".MP4":
  132. myProcess.StartInfo.Arguments = SpeakerStr + MicrophoneStr + " -f gdigrab -framerate 6 -offset_x 0 -offset_y 0 -video_size " + size.Width + "x" + size.Height + " -i desktop -pix_fmt yuv420p -vf scale=trunc(iw/2)*2:trunc(ih/2)*2 -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec aac " + PathName;
  133. break;
  134. case ".AVI":
  135. case ".FLV":
  136. myProcess.StartInfo.Arguments = SpeakerStr + MicrophoneStr + " -f gdigrab -framerate 6 -offset_x 0 -offset_y 0 -video_size " + size.Width + "x" + size.Height + " -i desktop -pix_fmt yuv420p -vf scale=trunc(iw/2)*2:trunc(ih/2)*2 -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec aac -f " + Extension.ToLower().Replace(".", "") + " " + PathName;
  137. break;
  138. default:
  139. myProcess.StartInfo.Arguments = SpeakerStr + MicrophoneStr + " -f gdigrab -framerate 6 -offset_x 0 -offset_y 0 -video_size " + size.Width + "x" + size.Height + " -i desktop -pix_fmt yuv420p -vf scale=trunc(iw/2)*2:trunc(ih/2)*2 -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -acodec aac " + PathName;
  140. break;
  141. }
  142. if (OutputVideoLog)
  143. {
  144. LogHelper.WriteInfoLog("【录屏】:" + myProcess.StartInfo.Arguments);
  145. }
  146. FileToolsCommon.AppendText(LogPath, "【录屏】:" + myProcess.StartInfo.Arguments + "\r\n");
  147. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  148. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  149. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  150. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  151. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  152. try
  153. {
  154. myProcess.Start();
  155. myProcess.BeginErrorReadLine();
  156. }
  157. catch (Exception ex)
  158. {
  159. if (ex.Message.Contains("访问"))
  160. {
  161. ErrMessage = "访问被拒绝,请关闭杀毒软件或新任此软件后重试!";
  162. }
  163. else
  164. {
  165. ErrMessage = ex.Message + " 请关闭杀毒软件或信任此软件后重试!";
  166. }
  167. LogHelper.WriteErrLog("【录音】(StartRecordingAudio)" + ErrMessage, ex);
  168. myProcess.Dispose();
  169. myProcess = null;
  170. return false;
  171. }
  172. return true;
  173. }
  174. /// <summary>
  175. /// 录制音频
  176. /// </summary>
  177. /// <param name="Mp3Path">MP3音频位置</param>
  178. /// <param name="ErrMessage">错误信息</param>
  179. /// <param name="RecordingSound">录制音频</param>
  180. /// <param name="RecordingMicrophone">录制麦克风</param>
  181. /// <param name="MicrophoneName">麦克风名</param>
  182. public bool StartRecordingAudio(string Mp3Path, out string ErrMessage, bool RecordingSound, bool RecordingMicrophone, string MicrophoneName = null)
  183. {
  184. ErrMessage = null;
  185. while (myProcess != null)
  186. {
  187. Thread.Sleep(100);
  188. }
  189. //路径
  190. string Path = FileToolsCommon.GetDirectoryName(Mp3Path);
  191. //文件保存路径
  192. string PathName = GetFilePathName(Mp3Path);
  193. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  194. foreach (Process KillProcess in KillProcessArray)
  195. {
  196. KillProcess.Kill();
  197. }
  198. if (string.IsNullOrWhiteSpace(MicrophoneName))
  199. {
  200. MicrophoneName = GetMicrophoneName();
  201. }
  202. #region 检测麦克风扬声器
  203. string AudioPath = FileToolsCommon.GetFileAbsolutePath("/temp/audio/");
  204. FileToolsCommon.CreateDirectory(AudioPath);
  205. string audioSpeakerPath = AudioPath + "adoS" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m";
  206. string audioMicrophonePath = AudioPath + "adoM" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m";
  207. try
  208. {
  209. FileToolsCommon.DeleteFile(audioSpeakerPath);
  210. FileToolsCommon.DeleteFile(audioMicrophonePath);
  211. }
  212. catch (Exception)
  213. {
  214. }
  215. if (RecordingSound)
  216. {
  217. if (StartRecordSpeakerAudio(audioSpeakerPath))
  218. {
  219. IsRecordSpeaker = true;
  220. }
  221. else
  222. {
  223. //无法录制扬声器音频
  224. IsRecordSpeaker = false;
  225. }
  226. StopRecordAudio(2);
  227. Thread.Sleep(100);
  228. if (!IsRecordSpeaker)
  229. {
  230. ErrMessage = "无法录制声音,请关闭杀毒软件,确保扬声器处于可用状态!";
  231. //return false;
  232. }
  233. }
  234. if (RecordingMicrophone)
  235. {
  236. if (StartRecordAudio(audioMicrophonePath))
  237. {
  238. IsRecordMicrophone = true;
  239. }
  240. else
  241. {
  242. //无法录制麦克风
  243. IsRecordMicrophone = false;
  244. }
  245. StopRecordAudio(1);
  246. if (!IsRecordMicrophone)
  247. {
  248. ErrMessage = "无法录制麦克风,请关闭杀毒软件,确保麦克风处于可用状态!";
  249. //return false;
  250. }
  251. }
  252. #endregion 检测麦克风扬声器
  253. myProcess = new Process();
  254. LogPath = CreateffmpegLog();
  255. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  256. if (IsRecordSpeaker && IsRecordMicrophone && RecordingSound && RecordingMicrophone)
  257. {
  258. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" +
  259. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 " + PathName;
  260. }
  261. else if (IsRecordSpeaker && RecordingSound)
  262. {
  263. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" " + PathName;
  264. }
  265. else if (IsRecordMicrophone && RecordingMicrophone)
  266. {
  267. //录制麦克风
  268. if (!string.IsNullOrWhiteSpace(MicrophoneName))
  269. {
  270. myProcess.StartInfo.Arguments = "-f dshow -i audio=\"" +
  271. MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 " + PathName;
  272. }
  273. }
  274. if (OutputVideoLog)
  275. {
  276. LogHelper.WriteInfoLog("【录音】:" + myProcess.StartInfo.Arguments);
  277. }
  278. FileToolsCommon.AppendText(LogPath, "【录音】:" + myProcess.StartInfo.Arguments + "\r\n");
  279. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  280. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  281. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  282. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  283. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  284. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  285. try
  286. {
  287. myProcess.Start(); //启动线程
  288. myProcess.BeginErrorReadLine(); //开始异步读取
  289. }
  290. catch (Exception ex)
  291. {
  292. if (ex.Message.Contains("访问"))
  293. {
  294. ErrMessage = "无法录制声音,访问被拒绝,请关闭杀毒软件或信任此软件后重试!";
  295. }
  296. else
  297. {
  298. ErrMessage = ex.Message + " 请关闭杀毒软件或信任此软件后重试!";
  299. }
  300. LogHelper.WriteErrLog("【录音】(StartRecordingAudio)" + ErrMessage, ex);
  301. myProcess.Dispose();
  302. myProcess = null;
  303. return false;
  304. }
  305. return true;
  306. }
  307. /// <summary>
  308. /// 暂停录制
  309. /// </summary>
  310. public bool SuspendFFmpeg()
  311. {
  312. if (myProcess != null)
  313. {
  314. myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制
  315. myProcess.Close();//关闭进程
  316. myProcess.Dispose();//释放资源
  317. }
  318. #region 进程是否已经释放
  319. bool IsRunning = true;
  320. while (IsRunning)
  321. {
  322. IsRunning = false;
  323. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  324. foreach (Process KillProcess in ProcessArray)
  325. {
  326. IsRunning = true;
  327. Thread.Sleep(100);
  328. }
  329. }
  330. #endregion 进程是否已经释放
  331. myProcess = null;
  332. return true;
  333. //myProcess.Kill();
  334. }
  335. /// <summary>
  336. /// 停止录制并合成文件
  337. /// </summary>
  338. /// <param name="FilePath">文件存储路径 必须与开始时的路径完全一致</param>
  339. public bool StopFFmpeg(string FilePath)
  340. {
  341. try
  342. {
  343. //文件完整路径
  344. if (myProcess != null)
  345. {
  346. myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制
  347. myProcess.Close();//关闭进程
  348. myProcess.Dispose();//释放资源
  349. }
  350. }
  351. catch (Exception)
  352. {
  353. myProcess = null;
  354. }
  355. #region 进程是否已经释放
  356. bool IsRunning = true;
  357. while (IsRunning)
  358. {
  359. IsRunning = false;
  360. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  361. foreach (Process KillProcess in ProcessArray)
  362. {
  363. IsRunning = true;
  364. Thread.Sleep(100);
  365. }
  366. }
  367. #endregion 进程是否已经释放
  368. try
  369. {
  370. myProcess = null;
  371. Thread t1 = new Thread(MergeVideoOrAudio);
  372. t1.Start(FilePath);
  373. return true;
  374. }
  375. catch (Exception ex)
  376. {
  377. LogHelper.WriteErrLog("【合成文件】:" + FilePath + "合成失败," + ex.Message, ex);
  378. return false;
  379. }
  380. //文件合成
  381. }
  382. /// <summary>
  383. /// 图片音频合成视频
  384. /// </summary>
  385. /// <param name="ImageListPath">图片列表位置 命名为1.png 2.png</param>
  386. /// <param name="Mp3Path">MP3音频位置</param>
  387. /// <param name="VideoSavePath">视频保存位置</param>
  388. /// <param name="Frequency">每秒帧率</param>
  389. /// <param name="VideoWidth">视频宽度</param>
  390. /// <param name="VideoHeight">视频高度</param>
  391. public bool SynthesisVideo(string ImageListPath, string Mp3Path, string VideoSavePath, int Frequency, int VideoWidth, int VideoHeight, out string ErrMessage)
  392. {
  393. while (myProcess != null)
  394. {
  395. Thread.Sleep(100);
  396. }
  397. ErrMessage = null;
  398. string Extension = FileToolsCommon.GetIOExtension(VideoSavePath);//扩展名
  399. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  400. foreach (Process KillProcess in KillProcessArray)
  401. {
  402. KillProcess.Kill();
  403. }
  404. myProcess = new Process();
  405. try
  406. {
  407. LogPath = CreateffmpegLog();
  408. string mp3Str = "";
  409. #region 判断音频是否存在
  410. if (!string.IsNullOrWhiteSpace(Mp3Path))
  411. {
  412. if (FileToolsCommon.IsExistFile(Mp3Path))
  413. {
  414. mp3Str = "-i " + Mp3Path;
  415. }
  416. }
  417. #endregion 判断音频是否存在
  418. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  419. switch (Extension.ToUpper())
  420. {
  421. case ".MP4":
  422. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  423. ImageListPath + @"%d.png " +
  424. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
  425. VideoSavePath;
  426. break;
  427. case ".AVI":
  428. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  429. ImageListPath + @"%d.png " +
  430. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -f AVI " +
  431. VideoSavePath;
  432. break;
  433. case ".FLV":
  434. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  435. ImageListPath + @"%d.png " +
  436. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -f FLV " +
  437. VideoSavePath;
  438. break;
  439. default:
  440. myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
  441. ImageListPath + @"%d.png " +
  442. mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
  443. VideoSavePath;
  444. break;
  445. }
  446. if (OutputVideoLog)
  447. {
  448. LogHelper.WriteInfoLog("【图片音频合成视频】:" + myProcess.StartInfo.Arguments);
  449. }
  450. FileToolsCommon.AppendText(LogPath, "【图片音频合成视频】:" + myProcess.StartInfo.Arguments + "\r\n");
  451. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  452. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  453. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  454. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  455. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  456. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  457. try
  458. {
  459. myProcess.Start(); //启动线程
  460. myProcess.BeginErrorReadLine(); //开始异步读取
  461. myProcess.WaitForExit(); //阻塞等待进程结束
  462. myProcess.Close(); //关闭进程
  463. }
  464. catch (Exception ex)
  465. {
  466. if (ex.Message.Contains("访问"))
  467. {
  468. ErrMessage = "访问被拒绝,请关闭杀毒软件或新任此软件后重试!";
  469. }
  470. else
  471. {
  472. ErrMessage = ex.Message + " 请关闭杀毒软件或信任此软件后重试!";
  473. }
  474. LogHelper.WriteErrLog("【录音】(StartRecordingAudio)" + ErrMessage, ex);
  475. myProcess.Dispose();
  476. myProcess = null;
  477. return false;
  478. }
  479. myProcess.Dispose(); //释放资源
  480. #region 进程是否已经释放
  481. bool IsRunning = true;
  482. while (IsRunning)
  483. {
  484. IsRunning = false;
  485. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  486. foreach (Process KillProcess in ProcessArray)
  487. {
  488. IsRunning = true;
  489. Thread.Sleep(100);
  490. }
  491. }
  492. #endregion 进程是否已经释放
  493. myProcess = null;
  494. //生成视频后删除图片和音频保存列表
  495. //FileToolsCommon.DeleteDirectory(ImageListPath);
  496. //FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(Mp3Path));
  497. Thread.Sleep(100);
  498. FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(VideoSavePath) + "temp");
  499. return true;
  500. }
  501. catch (Exception ex)
  502. {
  503. LogHelper.WriteErrLog("【视频合成】(SynthesisVideo)视频生成失败," + ex.Message, ex);
  504. ErrMessage = ex.Message;
  505. return false;
  506. }
  507. //Dispatcher.Invoke(() =>
  508. //{
  509. //});
  510. //// 允许不同线程间的调用
  511. //Control.CheckForIllegalCrossThreadCalls = false;
  512. }
  513. /// <summary>
  514. /// 视频音频合成
  515. /// </summary>
  516. /// <param name="FilePath">存储路径</param>
  517. public void MergeVideoOrAudio(object FilePathobj)
  518. {
  519. while (myProcess != null)
  520. {
  521. Thread.Sleep(100);
  522. }
  523. //路径
  524. string FilePath = (string)FilePathobj;
  525. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  526. //扩展名
  527. string Extension = FileToolsCommon.GetIOExtension(FilePath);
  528. //Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  529. //foreach (var KillProcess in KillProcessArray)
  530. //{
  531. // KillProcess.Kill();
  532. //}
  533. #region 判断文件是否存在
  534. if (Extension.ToUpper() == ".MP3")
  535. {
  536. if (!FileToolsCommon.IsExistFile(Path + "temp/filelist.d"))
  537. {
  538. return;
  539. }
  540. }
  541. else
  542. {
  543. if (!FileToolsCommon.IsExistFile(Path + "_temppath/filelist.d"))
  544. {
  545. return;
  546. }
  547. }
  548. #endregion 判断文件是否存在
  549. myProcess = new Process();
  550. LogPath = CreateffmpegLog();
  551. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  552. if (Extension.ToUpper() == ".MP3")
  553. {
  554. myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "temp/filelist.d -c copy " + FilePath;
  555. }
  556. else
  557. {
  558. myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "_temppath/filelist.d -c copy " + FilePath;
  559. }
  560. if (OutputVideoLog)
  561. {
  562. LogHelper.WriteInfoLog("【音视频合成】:" + myProcess.StartInfo.Arguments);
  563. }
  564. FileToolsCommon.AppendText(LogPath, "【音视频合成】:" + myProcess.StartInfo.Arguments + "\r\n");
  565. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  566. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  567. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  568. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  569. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  570. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  571. myProcess.Start(); //启动线程
  572. myProcess.BeginErrorReadLine(); //开始异步读取
  573. myProcess.WaitForExit(); //阻塞等待进程结束
  574. myProcess.Close(); //关闭进程
  575. myProcess.Dispose(); //释放资源
  576. #region 进程是否已经释放
  577. bool IsRunning = true;
  578. while (IsRunning)
  579. {
  580. IsRunning = false;
  581. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  582. foreach (Process KillProcess in ProcessArray)
  583. {
  584. IsRunning = true;
  585. Thread.Sleep(100);
  586. }
  587. }
  588. #endregion 进程是否已经释放
  589. myProcess = null;
  590. if (Extension.ToUpper() == ".MP3")
  591. {
  592. FileToolsCommon.DeleteDirectory(Path + "temp/");
  593. }
  594. else
  595. {
  596. FileToolsCommon.DeleteDirectory(Path + "_temppath/");
  597. }
  598. }
  599. /// <summary>
  600. /// 视频剪辑
  601. /// </summary>
  602. /// <param name="VideoFilePath">视频路径</param>
  603. /// <param name="StartTime">开始时间 格式 HH:mm:ss</param>
  604. /// <param name="EndTime">结束时间 格式 HH:mm:ss</param>
  605. /// <param name="SaveFilePath">保存文件路径</param>
  606. public void ClipVideo(string VideoFilePath, string StartTime, string EndTime, string SaveFilePath)
  607. {
  608. while (myProcess != null)
  609. {
  610. Thread.Sleep(100);
  611. }
  612. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  613. foreach (var KillProcess in KillProcessArray)
  614. {
  615. KillProcess.Kill();
  616. }
  617. myProcess = new Process();
  618. LogPath = CreateffmpegLog();
  619. this.myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  620. //ffmpeg -ss 开始时间 -to 结束时间 -accurate_seek -i mp4输入路径 -c:v libx264 -c:a aac -strict experimental -vf scale = 宽:高 -b 500k 输出路径.mp4
  621. myProcess.StartInfo.Arguments = "-i " + VideoFilePath + " -vcodec copy -acodec copy -ss " + StartTime + " -to " + EndTime + " " + SaveFilePath; //-filter:a "volume=0.5"
  622. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  623. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  624. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  625. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  626. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  627. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  628. myProcess.Start(); //启动线程
  629. myProcess.BeginErrorReadLine(); //开始异步读取
  630. myProcess.WaitForExit(); //阻塞等待进程结束
  631. myProcess.Close(); //关闭进程
  632. myProcess.Dispose(); //释放资源
  633. #region 进程是否已经释放
  634. bool IsRunning = true;
  635. while (IsRunning)
  636. {
  637. IsRunning = false;
  638. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  639. foreach (var KillProcess in ProcessArray)
  640. {
  641. IsRunning = true;
  642. Thread.Sleep(100);
  643. }
  644. }
  645. #endregion 进程是否已经释放
  646. myProcess = null;
  647. }
  648. /// <summary>
  649. /// 获取文件完整路径 音频
  650. /// </summary>
  651. /// <param name="FilePath">文件路径</param>
  652. /// <returns></returns>
  653. private string GetFilePathName(string FilePath)
  654. {
  655. //路径
  656. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  657. //Path.GetFileName(FilePath);//获取文件名
  658. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  659. string tempFilePath = Path + "temp/";
  660. //创建临时目录
  661. FileToolsCommon.CreateDirectory(tempFilePath);
  662. //创建记录文件
  663. if (!FileToolsCommon.IsExistFile(tempFilePath + "filelist.d"))
  664. {
  665. FileToolsCommon.CreateFile(tempFilePath + "filelist.d");
  666. }
  667. int num = 1;
  668. string CompleteFilePath = tempFilePath + num + Extension;
  669. while (FileToolsCommon.IsExistFile(CompleteFilePath))
  670. {
  671. num++;
  672. CompleteFilePath = tempFilePath + num + Extension;
  673. }
  674. FileToolsCommon.AppendText(tempFilePath + "filelist.d", "file ./" + num + Extension + "\r\n");
  675. return CompleteFilePath;
  676. }
  677. /// <summary>
  678. /// 获取文件完整路径 录屏
  679. /// </summary>
  680. /// <param name="FilePath">文件路径</param>
  681. /// <returns></returns>
  682. private string GetRSFilePathName(string FilePath)
  683. {
  684. //路径
  685. string Path = FileToolsCommon.GetDirectoryName(FilePath);
  686. //Path.GetFileName(FilePath);//获取文件名
  687. string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
  688. string tempFilePath = Path + "_temppath/";
  689. //创建临时目录
  690. FileToolsCommon.CreateDirectory(tempFilePath);
  691. //创建记录文件
  692. if (!FileToolsCommon.IsExistFile(tempFilePath + "filelist.d"))
  693. {
  694. FileToolsCommon.CreateFile(tempFilePath + "filelist.d");
  695. }
  696. int num = 1;
  697. string CompleteFilePath = tempFilePath + num + Extension;
  698. while (FileToolsCommon.IsExistFile(CompleteFilePath))
  699. {
  700. num++;
  701. CompleteFilePath = tempFilePath + num + Extension;
  702. }
  703. FileToolsCommon.AppendText(tempFilePath + "filelist.d", "file ./" + num + Extension + "\r\n");
  704. return CompleteFilePath;
  705. }
  706. /// <summary>
  707. /// 生成缩略图
  708. /// </summary>
  709. /// <param name="VideoPath">视频地址</param>
  710. /// <param name="ImagePath">图片地址</param>
  711. /// <param name="Width">图片大小</param>
  712. /// <param name="Height">图片大小</param>
  713. public bool GenerateThumbnails(string VideoPath, string ImagePath, int Width = 0, int Height = 0)
  714. {
  715. while (myProcess != null)
  716. {
  717. Thread.Sleep(100);
  718. }
  719. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  720. foreach (Process KillProcess in KillProcessArray)
  721. {
  722. KillProcess.Kill();
  723. }
  724. string WHStr = "";
  725. if (Width > 0)
  726. {
  727. WHStr = " -s " + Width + "x" + Height;
  728. }
  729. try
  730. {
  731. myProcess = new Process();
  732. LogPath = CreateffmpegLog();
  733. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  734. //myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2 -s " + thubWidth + "*" + thubHeight + " -f image2 \"" + ImagePath + "\"";
  735. myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2" + WHStr + " -f image2 \"" + ImagePath + "\"";
  736. if (OutputVideoLog)
  737. {
  738. LogHelper.WriteInfoLog("【生成缩略图】:" + myProcess.StartInfo.Arguments);
  739. }
  740. FileToolsCommon.AppendText(LogPath, "【生成缩略图】:" + myProcess.StartInfo.Arguments + "\r\n");
  741. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  742. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  743. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  744. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  745. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  746. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  747. myProcess.Start(); //启动线程
  748. myProcess.BeginErrorReadLine(); //开始异步读取
  749. myProcess.WaitForExit(); //阻塞等待进程结束
  750. myProcess.Close(); //关闭进程
  751. myProcess.Dispose(); //释放资源
  752. #region 进程是否已经释放
  753. bool IsRunning = true;
  754. while (IsRunning)
  755. {
  756. IsRunning = false;
  757. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  758. foreach (Process KillProcess in ProcessArray)
  759. {
  760. IsRunning = true;
  761. Thread.Sleep(100);
  762. }
  763. }
  764. #endregion 进程是否已经释放
  765. myProcess = null;
  766. return true;
  767. }
  768. catch (Exception ex)
  769. {
  770. LogHelper.WriteErrLog("【生成缩略图】(GenerateThumbnails)缩略图生成失败," + ex.Message, ex);
  771. return false;
  772. }
  773. }
  774. /// <summary>
  775. /// 视频转码
  776. /// </summary>
  777. /// <param name="VideoPathName">源视频</param>
  778. /// <param name="VideoSavePathName">目标视频存储路径</param>
  779. /// <param name="Width">宽</param>
  780. /// <param name="Height">高</param>
  781. /// <returns></returns>
  782. public bool VideoTranscode(string VideoPathName, string VideoSavePathName, int Width, int Height)
  783. {
  784. while (myProcess != null)
  785. {
  786. Thread.Sleep(100);
  787. }
  788. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  789. foreach (Process KillProcess in KillProcessArray)
  790. {
  791. KillProcess.Kill();
  792. }
  793. try
  794. {
  795. myProcess = new Process();
  796. LogPath = CreateffmpegLog();
  797. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  798. myProcess.StartInfo.Arguments = "-i " + VideoPathName + " -acodec copy -vcodec libx264 -s " + Width + "*" + Height + " " + VideoSavePathName;
  799. if (OutputVideoLog)
  800. {
  801. LogHelper.WriteInfoLog("【视频转码】:" + myProcess.StartInfo.Arguments);
  802. }
  803. FileToolsCommon.AppendText(LogPath, "【视频转码】:" + myProcess.StartInfo.Arguments + "\r\n");
  804. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  805. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  806. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  807. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  808. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  809. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  810. myProcess.Start(); //启动线程
  811. myProcess.BeginErrorReadLine(); //开始异步读取
  812. myProcess.WaitForExit(); //阻塞等待进程结束
  813. myProcess.Close(); //关闭进程
  814. myProcess.Dispose(); //释放资源
  815. #region 进程是否已经释放
  816. bool IsRunning = true;
  817. while (IsRunning)
  818. {
  819. IsRunning = false;
  820. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  821. foreach (Process KillProcess in ProcessArray)
  822. {
  823. IsRunning = true;
  824. Thread.Sleep(100);
  825. }
  826. }
  827. #endregion 进程是否已经释放
  828. myProcess = null;
  829. Thread.Sleep(200);
  830. FileToolsCommon.DeleteFile(VideoPathName);
  831. return true;
  832. }
  833. catch (Exception ex)
  834. {
  835. LogHelper.WriteErrLog("【视频转码】(VideoTranscode)视频转码失败," + ex.Message, ex);
  836. return false;
  837. }
  838. }
  839. public bool VideoAudioMerge(string VideoPathName, string mp3path, string VideoSavePathName, int Width, int Height)
  840. {
  841. while (myProcess != null)
  842. {
  843. Thread.Sleep(100);
  844. }
  845. Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
  846. foreach (Process KillProcess in KillProcessArray)
  847. {
  848. KillProcess.Kill();
  849. }
  850. try
  851. {
  852. myProcess = new Process();
  853. LogPath = CreateffmpegLog();
  854. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  855. string ffmpegstr = "-i " + VideoPathName + " -i " + mp3path + " -acodec copy -vcodec libx264 " + "-s " + Width + "*" + Height + " " + VideoSavePathName;
  856. Console.WriteLine("ffmpegstr:" + ffmpegstr);
  857. myProcess.StartInfo.Arguments = ffmpegstr;
  858. if (OutputVideoLog)
  859. {
  860. LogHelper.WriteInfoLog("【视频转码】:" + myProcess.StartInfo.Arguments);
  861. }
  862. FileToolsCommon.AppendText(LogPath, "【视频转码】:" + myProcess.StartInfo.Arguments + "\r\n");
  863. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  864. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  865. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  866. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  867. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  868. myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
  869. myProcess.Start(); //启动线程
  870. myProcess.BeginErrorReadLine(); //开始异步读取
  871. myProcess.WaitForExit(); //阻塞等待进程结束
  872. myProcess.Close(); //关闭进程
  873. myProcess.Dispose(); //释放资源
  874. #region 进程是否已经释放
  875. bool IsRunning = true;
  876. while (IsRunning)
  877. {
  878. IsRunning = false;
  879. Process[] ProcessArray = Process.GetProcessesByName("ffmpeg");
  880. foreach (Process KillProcess in ProcessArray)
  881. {
  882. IsRunning = true;
  883. Thread.Sleep(100);
  884. }
  885. }
  886. #endregion 进程是否已经释放
  887. myProcess = null;
  888. return true;
  889. }
  890. catch (Exception ex)
  891. {
  892. LogHelper.WriteErrLog("【视频转码】(VideoTranscode)视频转码失败," + ex.Message, ex);
  893. return false;
  894. }
  895. }
  896. /// <summary>
  897. /// 创建日志文件
  898. /// </summary>
  899. /// <returns></returns>
  900. private string CreateffmpegLog()
  901. {
  902. string LogFileName = DateTime.Now.ToString("yyyyMMdd");
  903. string LogFilePath = FileToolsCommon.GetFileAbsolutePath("/Log/FFMpegLog/");
  904. FileToolsCommon.CreateDirectory(LogFilePath);
  905. string LogName = LogFilePath + LogFileName + ".log";
  906. try
  907. {
  908. if (!FileToolsCommon.IsExistFile(LogName))
  909. {
  910. FileToolsCommon.CreateFile(LogName);
  911. FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" +
  912. "****************************************************************************************************\r\n");
  913. return LogName;
  914. }
  915. else
  916. {
  917. int num = 0;
  918. while (FileToolsCommon.GetFileSizeByMB(LogName) > 2)
  919. {
  920. num++;
  921. LogName = LogFilePath + LogFileName + "_" + num + ".log";
  922. FileToolsCommon.CreateFile(LogName);
  923. }
  924. FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" +
  925. "****************************************************************************************************\r\n");
  926. return LogName;
  927. }
  928. }
  929. catch (Exception)
  930. {
  931. return LogName;
  932. }
  933. }
  934. /// <summary>
  935. /// 输出结果
  936. /// </summary>
  937. /// <param name="sendProcess"></param>
  938. /// <param name="output"></param>
  939. private void Output(object sendProcess, DataReceivedEventArgs output)
  940. {
  941. if (!string.IsNullOrEmpty(output.Data))
  942. {
  943. FileToolsCommon.AppendText(LogPath, "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff") + "】:");
  944. FileToolsCommon.AppendText(LogPath, output.Data + "\r\n");
  945. }
  946. }
  947. #region 麦克风声卡检测
  948. #region 获取麦克风
  949. private bool IsMicrophone = false;
  950. /// <summary>
  951. /// 使用FFmpeg获取的麦克风名
  952. /// </summary>
  953. private string MicrophoneNameInfo = "";
  954. /// <summary>
  955. /// 获取麦克风名称
  956. /// </summary>
  957. /// <returns></returns>
  958. public string GetMicrophoneName()
  959. {
  960. return GetAudioInDevices();
  961. //string FullName = GetInDeviceFull();
  962. //if(string.IsNullOrWhiteSpace(MicrophoneNameInfo))
  963. //{
  964. // return FullName;
  965. //}
  966. ////byte[] bufferASCII = Encoding.Default.GetBytes(MicrophoneNameInfo);
  967. ////string str = Encoding.UTF8.GetString(bufferASCII);
  968. ////str = str.Replace("?", " ");
  969. ////int num = str.Length;
  970. ////int num1 = FullName.Length;
  971. //if (FullName.Contains("(") || FullName.Contains("("))
  972. //{
  973. // string EName = FullName.Substring(FullName.IndexOf("(")+1);
  974. // if(string.IsNullOrEmpty(EName))
  975. // {
  976. // EName = FullName.Substring(FullName.IndexOf("(") + 1);
  977. // EName = EName.Substring(0, EName.LastIndexOf(")"));
  978. // }
  979. // else
  980. // {
  981. // //EName = EName.Substring(0, EName.LastIndexOf(")"));
  982. // }
  983. // if(MicrophoneNameInfo.Contains(EName))
  984. // {
  985. // return FullName;
  986. // }
  987. // else
  988. // {
  989. // return GetInDeviceName();
  990. // }
  991. //}
  992. //else
  993. //{
  994. // return FullName;
  995. //}
  996. }
  997. /// <summary>
  998. /// 获取麦克风列表
  999. /// </summary>
  1000. /// <returns></returns>
  1001. public List<string> GetMicrophoneNameList()
  1002. {
  1003. return GetAudioInDevicesList();
  1004. }
  1005. /// <summary>
  1006. /// 使用FFmpeg获取设备名称--待定
  1007. /// </summary>
  1008. public void GetMToFFmpeg()
  1009. {
  1010. //return;
  1011. new Thread(new ThreadStart(new Action(() =>
  1012. {
  1013. try
  1014. {
  1015. while (myProcess != null)
  1016. {
  1017. Thread.Sleep(100);
  1018. }
  1019. myProcess = new Process();
  1020. LogPath = CreateffmpegLog();
  1021. myProcess.StartInfo.FileName = ffmpegPath; //ffmpeg.exe的绝对路径
  1022. myProcess.StartInfo.Arguments = " -list_devices true -f dshow -i dummy";
  1023. if (OutputVideoLog)
  1024. {
  1025. LogHelper.WriteInfoLog("【获取设备信息】:" + myProcess.StartInfo.Arguments);
  1026. }
  1027. FileToolsCommon.AppendText(LogPath, "【获取设备信息】:" + myProcess.StartInfo.Arguments + "\r\n");
  1028. myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
  1029. myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
  1030. myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
  1031. myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
  1032. //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
  1033. myProcess.ErrorDataReceived += GetDevInfoData;
  1034. myProcess.Start(); //启动线程
  1035. myProcess.BeginErrorReadLine(); //开始异步读取
  1036. myProcess.WaitForExit(); //阻塞等待进程结束
  1037. myProcess.Close(); //关闭进程
  1038. myProcess.Dispose(); //释放资源
  1039. myProcess = null;
  1040. }
  1041. catch (Exception ex)
  1042. {
  1043. LogHelper.WriteErrLog("【获取设备信息】(GetMToFFmpeg)信息获取失败:" + ex.Message, ex);
  1044. }
  1045. }))).Start();
  1046. }
  1047. /// <summary>
  1048. /// 使用FFmpeg获取设备名称--待定
  1049. /// </summary>
  1050. /// <param name="sender"></param>
  1051. /// <param name="e"></param>
  1052. private void GetDevInfoData(object sender, DataReceivedEventArgs e)
  1053. {
  1054. if (!string.IsNullOrEmpty(e.Data))
  1055. {
  1056. if (IsMicrophone)
  1057. {
  1058. try
  1059. {
  1060. MicrophoneNameInfo = e.Data;
  1061. MicrophoneNameInfo = MicrophoneNameInfo.Substring(MicrophoneNameInfo.IndexOf("]") + 1);
  1062. MicrophoneNameInfo = MicrophoneNameInfo.Replace("\"", "");
  1063. MicrophoneNameInfo = MicrophoneNameInfo.Trim();
  1064. #region 测试
  1065. //byte[] bufferASCII = Encoding.Default.GetBytes(MicrophoneNameInfo);
  1066. //MicrophoneNameInfo = Encoding.UTF8.GetString(bufferASCII); // 统一使用UTF-8
  1067. ////System.Text.Encoding GB2312 = System.Text.Encoding.GetEncoding("GB2312");
  1068. ////byte[] gb = GB2312.GetBytes(MicrophoneNameInfo);
  1069. ////MicrophoneNameInfo = Encoding.UTF8.GetString(gb);
  1070. #endregion 测试
  1071. IsMicrophone = false;
  1072. FileToolsCommon.AppendText(LogPath, e.Data);
  1073. //FileToolsCommon.AppendText(LogPath, "\r\n***************************************************\r\n");
  1074. }
  1075. catch (Exception ex)
  1076. {
  1077. LogHelper.WriteErrLog("【获取设备信息】(GetDevInfoData)信息获取失败:" + ex.Message, ex);
  1078. }
  1079. }
  1080. else
  1081. {
  1082. if (e.Data.Contains("DirectShow audio devices"))
  1083. {
  1084. //FileToolsCommon.AppendText(LogPath, "\r\n***************************************************\r\n");
  1085. IsMicrophone = true;
  1086. }
  1087. FileToolsCommon.AppendText(LogPath, e.Data);
  1088. FileToolsCommon.AppendText(LogPath, "\r\n");
  1089. }
  1090. }
  1091. }
  1092. /// <summary>
  1093. /// AForge获取麦克风
  1094. /// </summary>
  1095. /// <returns></returns>
  1096. public static string GetAudioInDevices()
  1097. {
  1098. List<string> devicesList = new List<string>();
  1099. try
  1100. {
  1101. FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.AudioInputDevice);
  1102. foreach (FilterInfo device in videoDevices)
  1103. {
  1104. devicesList.Add(device.Name);
  1105. }
  1106. }
  1107. catch (ApplicationException)
  1108. {
  1109. //Console.WriteLine("No local capture devices");
  1110. }
  1111. if (devicesList.Count > 1)
  1112. {
  1113. return devicesList[1];
  1114. }
  1115. else
  1116. {
  1117. return "";
  1118. }
  1119. }
  1120. /// <summary>
  1121. /// AForge获取麦克风列表
  1122. /// </summary>
  1123. /// <returns></returns>
  1124. public static List<string> GetAudioInDevicesList()
  1125. {
  1126. List<string> devicesList = new List<string>();
  1127. try
  1128. {
  1129. FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.AudioInputDevice);
  1130. foreach (FilterInfo device in videoDevices)
  1131. {
  1132. if (device.Name == "virtual-audio-capturer")
  1133. {
  1134. continue;
  1135. }
  1136. devicesList.Add(device.Name);
  1137. }
  1138. }
  1139. catch (ApplicationException)
  1140. {
  1141. //Console.WriteLine("No local capture devices");
  1142. }
  1143. return devicesList;
  1144. }
  1145. /// <summary>
  1146. /// 获取声音输入设备名称 不完整
  1147. /// </summary>
  1148. /// <returns></returns>
  1149. public static string GetInDeviceName()
  1150. {
  1151. List<WaveInCapabilities> devices = new List<WaveInCapabilities>();
  1152. int waveInDevices = WaveIn.DeviceCount;
  1153. for (int i = 0; i < waveInDevices; i++)
  1154. {
  1155. devices.Add(WaveIn.GetCapabilities(i));
  1156. }
  1157. List<string> devs = new List<string>();
  1158. devs = devices.Select(item => item.ProductName).ToList();
  1159. if (devs.Count > 0)
  1160. {
  1161. byte[] buffer = Encoding.UTF8.GetBytes(devs[0]);
  1162. string MicrophoneName = Encoding.UTF8.GetString(buffer); // 统一使用UTF-8
  1163. return MicrophoneName;
  1164. }
  1165. else
  1166. {
  1167. return "";
  1168. }
  1169. }
  1170. /// <summary>
  1171. /// 获取声音输入设备完整名称
  1172. /// </summary>
  1173. /// <returns></returns>
  1174. public static string GetInDeviceFull()
  1175. {
  1176. List<string> devices = new List<string>();
  1177. MMDeviceEnumerator enumberator = new MMDeviceEnumerator();
  1178. MMDeviceCollection deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
  1179. for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++)
  1180. {
  1181. WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
  1182. foreach (MMDevice device in deviceCollection)
  1183. {
  1184. try
  1185. {
  1186. if (device.FriendlyName.StartsWith(deviceInfo.ProductName))
  1187. {
  1188. devices.Add(device.FriendlyName);
  1189. break;
  1190. }
  1191. }
  1192. catch (Exception)
  1193. {
  1194. continue;
  1195. }
  1196. }
  1197. }
  1198. if (devices.Count > 0)
  1199. {
  1200. byte[] buffer = Encoding.UTF8.GetBytes(devices[0]);
  1201. string MicrophoneName = Encoding.UTF8.GetString(buffer); // 统一使用UTF-8
  1202. return MicrophoneName;
  1203. }
  1204. else
  1205. {
  1206. return "";
  1207. }
  1208. }
  1209. /// <summary>
  1210. /// 获取声音输入设备完整名称列表
  1211. /// </summary>
  1212. /// <returns></returns>
  1213. public List<string> GetInDeviceListFull()
  1214. {
  1215. List<string> devices = new List<string>();
  1216. MMDeviceEnumerator enumberator = new MMDeviceEnumerator();
  1217. MMDeviceCollection deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All);
  1218. for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++)
  1219. {
  1220. WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
  1221. foreach (MMDevice device in deviceCollection)
  1222. {
  1223. try
  1224. {
  1225. if (device.FriendlyName.StartsWith(deviceInfo.ProductName))
  1226. {
  1227. devices.Add(device.FriendlyName);
  1228. break;
  1229. }
  1230. }
  1231. catch (Exception)
  1232. {
  1233. continue;
  1234. }
  1235. }
  1236. }
  1237. return devices;
  1238. }
  1239. #endregion 获取麦克风
  1240. #region 录音
  1241. /// <summary>
  1242. /// 开始录制麦克风
  1243. /// </summary>
  1244. public bool StartAudio(string audioFile, string DeviceName = "")
  1245. {
  1246. try
  1247. {
  1248. int deviceIndex = 0;
  1249. waveIn = new WaveInEvent();
  1250. List<string> InDeviceList = GetInDeviceListFull();
  1251. if (InDeviceList.Exists(x => DeviceName.Contains(x)))
  1252. {
  1253. deviceIndex = InDeviceList.FindIndex(x => DeviceName.Contains(x));
  1254. }
  1255. waveIn.DeviceNumber = deviceIndex;
  1256. //waveIn.DeviceNumber = 1;
  1257. //生成音频文件的对象
  1258. WaveFileWriter writer = new WaveFileWriter(audioFile, waveIn.WaveFormat);
  1259. //开始录音,写数据
  1260. waveIn.DataAvailable += (s, a) =>
  1261. {
  1262. writer.Write(a.Buffer, 0, a.BytesRecorded);
  1263. };
  1264. return true;
  1265. }
  1266. catch (Exception ex)
  1267. {
  1268. LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex);
  1269. return false;
  1270. }
  1271. }
  1272. //public bool StopAudio()
  1273. //{
  1274. // try
  1275. // {
  1276. // //结束录音
  1277. // waveIn.RecordingStopped += (s, a) =>
  1278. // {
  1279. // writer.Dispose();
  1280. // writer = null;
  1281. // waveIn.Dispose();
  1282. // waveIn = null;
  1283. // try
  1284. // {
  1285. // FileToolsCommon.DeleteFile(audioFile);
  1286. // }
  1287. // catch (Exception)
  1288. // {
  1289. // }
  1290. // };
  1291. // waveIn.StartRecording();
  1292. // }
  1293. // catch (Exception ex)
  1294. // {
  1295. // throw;
  1296. // }
  1297. //}
  1298. #endregion 录音
  1299. #region 检测是否可用
  1300. /// <summary>
  1301. /// 录制麦克风的声音
  1302. /// </summary>
  1303. private WaveInEvent waveIn = null; //new WaveInEvent();
  1304. /// <summary>
  1305. /// 开始录制麦克风
  1306. /// </summary>
  1307. public bool StartRecordAudio(string audioFile, string DeviceName = "", bool IsStopDelFile = true)
  1308. {
  1309. try
  1310. {
  1311. int deviceIndex = 0;
  1312. waveIn = new WaveInEvent();
  1313. List<string> InDeviceList = GetInDeviceListFull();
  1314. if (InDeviceList.Exists(x => DeviceName.Contains(x)))
  1315. {
  1316. deviceIndex = InDeviceList.FindIndex(x => DeviceName.Contains(x));
  1317. }
  1318. waveIn.DeviceNumber = deviceIndex;
  1319. //waveIn.DeviceNumber = 1;
  1320. //生成音频文件的对象
  1321. WaveFileWriter writer = new WaveFileWriter(audioFile, waveIn.WaveFormat);
  1322. //开始录音,写数据
  1323. waveIn.DataAvailable += (s, a) =>
  1324. {
  1325. writer.Write(a.Buffer, 0, a.BytesRecorded);
  1326. short s1 = BitConverter.ToInt16(a.Buffer, 0);//这样采样比较少,反正是int16型的
  1327. int s2 = Math.Abs(s1 / 50);
  1328. Console.WriteLine("声音大小:" + s2);
  1329. };
  1330. //结束录音
  1331. waveIn.RecordingStopped += (s, a) =>
  1332. {
  1333. writer.Dispose();
  1334. writer = null;
  1335. waveIn.Dispose();
  1336. waveIn = null;
  1337. try
  1338. {
  1339. if (IsStopDelFile)
  1340. {
  1341. FileToolsCommon.DeleteFile(audioFile);
  1342. }
  1343. }
  1344. catch (Exception)
  1345. {
  1346. }
  1347. };
  1348. waveIn.StartRecording();
  1349. return true;
  1350. }
  1351. catch (Exception ex)
  1352. {
  1353. LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex);
  1354. return false;
  1355. }
  1356. }
  1357. /// <summary>
  1358. /// 结束录制音频
  1359. /// </summary>
  1360. /// <param name="type">1麦克风 2扬声器</param>
  1361. public void StopRecordAudio(int type)
  1362. {
  1363. try
  1364. {
  1365. if (type == 1)
  1366. {
  1367. if (waveIn != null)
  1368. {
  1369. try
  1370. {
  1371. waveIn.StopRecording();
  1372. }
  1373. catch (Exception)
  1374. {
  1375. waveIn = null;
  1376. }
  1377. }
  1378. }
  1379. else if (type == 2)
  1380. {
  1381. if (capture != null)
  1382. {
  1383. try
  1384. {
  1385. capture.StopRecording();
  1386. }
  1387. catch (Exception)
  1388. {
  1389. capture = null;
  1390. }
  1391. }
  1392. }
  1393. }
  1394. catch (Exception ex)
  1395. {
  1396. LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex);
  1397. }
  1398. }
  1399. /// <summary>
  1400. /// 录制扬声器的声音
  1401. /// </summary>
  1402. private WasapiLoopbackCapture capture = null; //new WasapiLoopbackCapture();
  1403. /// <summary>
  1404. /// 开始录制扬声器
  1405. /// </summary>
  1406. public bool StartRecordSpeakerAudio(string audioFile)
  1407. {
  1408. try
  1409. {
  1410. capture = new WasapiLoopbackCapture();
  1411. //生成音频文件的对象
  1412. WaveFileWriter writer = new WaveFileWriter(audioFile, capture.WaveFormat);
  1413. capture.DataAvailable += (s, a) =>
  1414. {
  1415. writer.Write(a.Buffer, 0, a.BytesRecorded);
  1416. };
  1417. //结束录音
  1418. capture.RecordingStopped += (s, a) =>
  1419. {
  1420. writer.Dispose();
  1421. writer = null;
  1422. capture.Dispose();
  1423. capture = null;
  1424. try
  1425. {
  1426. FileToolsCommon.DeleteFile(audioFile);
  1427. }
  1428. catch (Exception)
  1429. {
  1430. }
  1431. };
  1432. capture.StartRecording();
  1433. return true;
  1434. }
  1435. catch (Exception ex)
  1436. {
  1437. LogHelper.WriteErrLog("【扬声器】扬声器不可用:" + ex.Message, ex);
  1438. return false;
  1439. }
  1440. }
  1441. #endregion 检测是否可用
  1442. #endregion 麦克风声卡检测
  1443. }
  1444. }