using AForge.Video.DirectShow; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using VisioForge.Shared.NAudio.CoreAudioApi; using VisioForge.Shared.NAudio.Wave; namespace Common.system { /// /// ffmpeg帮助类 /// 创建人:赵耀 /// 创建时间::2020年8月22日 /// 需要安装\SSCR\Setup Screen Capturer Recorder v0.12.10.exe /// 本地调试需要配置环境变量 将ffmpeg.exe位置配置到环境变量的path中 /// public class FFMpeg { #region 变量 public Process myProcess = null; /// /// 是否输出录课录屏日志 /// public bool OutputVideoLog = FileToolsCommon.GetConfigValue("OutputVideoLog") != "0"; /// /// ffmpeg输出日志文件地址 每个文件2MB左右 /// private string LogPath = ""; /// /// 是否可以录制扬声器 /// private bool IsRecordSpeaker = true; /// /// 是否可以录制麦克风 /// private bool IsRecordMicrophone = true; #endregion /// /// 录制屏幕 /// /// 文件存储路径 /// 大小 /// 录制音频 /// 录制麦克风 /// 错误信息 /// 麦克风名 /// public bool StartRecordingVideo(string FilePath, Size size, bool RecordingSound, bool RecordingMicrophone, out string ErrMessage, string MicrophoneName = null) { while (myProcess != null) { Thread.Sleep(100); } ErrMessage = null; //路径 string Path = FileToolsCommon.GetDirectoryName(FilePath); string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名 //文件保存路径 string PathName = GetRSFilePathName(FilePath); Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in KillProcessArray) { KillProcess.Kill(); } myProcess = new Process(); LogPath = CreateffmpegLog(); if (string.IsNullOrWhiteSpace(MicrophoneName)) { MicrophoneName = GetMicrophoneName(); } #region 检测麦克风扬声器 string AudioPath = FileToolsCommon.GetFileAbsolutePath("/temp/audio/"); FileToolsCommon.CreateDirectory(AudioPath); string audioSpeakerPath = AudioPath + "adoS" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m";//FileToolsCommon.GetFileAbsolutePath("adoS.m"); string audioMicrophonePath = AudioPath + "adoM" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m";//FileToolsCommon.GetFileAbsolutePath("adoM.m"); try { FileToolsCommon.DeleteFile(audioSpeakerPath); FileToolsCommon.DeleteFile(audioMicrophonePath); } catch (Exception) { } //是否录制音频 if (RecordingSound) { if (StartRecordSpeakerAudio(audioSpeakerPath)) { IsRecordSpeaker = true; } else { //无法录制扬声器音频 IsRecordSpeaker = false; } StopRecordAudio(2); Thread.Sleep(100); } //是否录制麦克风 if (RecordingMicrophone) { if (StartRecordAudio(audioMicrophonePath)) { IsRecordMicrophone = true; } else { //无法录制麦克风 IsRecordMicrophone = false; } StopRecordAudio(1); } #endregion myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 string SpeakerStr = ""; string MicrophoneStr = ""; if (IsRecordSpeaker && RecordingSound) { SpeakerStr = "-f dshow -i audio=\"virtual-audio-capturer\" "; } if (IsRecordMicrophone && RecordingMicrophone) { if (!string.IsNullOrWhiteSpace(MicrophoneName)) { MicrophoneStr = "-f dshow -i audio=\"" + MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 "; } } switch (Extension.ToUpper()) { case ".MP4": 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; //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; //if (string.IsNullOrWhiteSpace(MicrophoneName)) //{ // 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的参数 //} //else //{ // myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" + // 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的参数 //} break; case ".AVI": case ".FLV": 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; //if (string.IsNullOrWhiteSpace(MicrophoneName)) //{ // 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的参数 //} //else //{ // myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" + // 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的参数 //} break; default: 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; break; } if (OutputVideoLog) { LogHelper.WriteInfoLog("【录屏】:" + myProcess.StartInfo.Arguments); } FileToolsCommon.AppendText(LogPath, "【录屏】:" + myProcess.StartInfo.Arguments + "\r\n"); myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output); try { myProcess.Start(); myProcess.BeginErrorReadLine(); } catch (Exception ex) { if (ex.Message.Contains("访问")) { ErrMessage = "访问被拒绝,请关闭杀毒软件或新任此软件后重试!"; } else { ErrMessage = ex.Message + " 请关闭杀毒软件或信任此软件后重试!"; } LogHelper.WriteErrLog("【录音】(StartRecordingAudio)" + ErrMessage, ex); myProcess.Dispose(); myProcess = null; return false; } return true; } /// /// 录制音频 /// /// MP3音频位置 /// 错误信息 /// 录制音频 /// 录制麦克风 /// 麦克风名 public bool StartRecordingAudio(string Mp3Path, out string ErrMessage, bool RecordingSound, bool RecordingMicrophone, string MicrophoneName = null) { ErrMessage = null; while (myProcess != null) { Thread.Sleep(100); } //路径 string Path = FileToolsCommon.GetDirectoryName(Mp3Path); //文件保存路径 string PathName = GetFilePathName(Mp3Path); Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in KillProcessArray) { KillProcess.Kill(); } if (string.IsNullOrWhiteSpace(MicrophoneName)) { MicrophoneName = GetMicrophoneName(); } #region 检测麦克风扬声器 string AudioPath = FileToolsCommon.GetFileAbsolutePath("/temp/audio/"); FileToolsCommon.CreateDirectory(AudioPath); string audioSpeakerPath = AudioPath + "adoS" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m"; string audioMicrophonePath = AudioPath + "adoM" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".m"; try { FileToolsCommon.DeleteFile(audioSpeakerPath); FileToolsCommon.DeleteFile(audioMicrophonePath); } catch (Exception) { } if (RecordingSound) { if (StartRecordSpeakerAudio(audioSpeakerPath)) { IsRecordSpeaker = true; } else { //无法录制扬声器音频 IsRecordSpeaker = false; } StopRecordAudio(2); Thread.Sleep(100); if (!IsRecordSpeaker) { ErrMessage = "无法录制声音,请关闭杀毒软件,确保扬声器处于可用状态!"; //return false; } } if (RecordingMicrophone) { if (StartRecordAudio(audioMicrophonePath)) { IsRecordMicrophone = true; } else { //无法录制麦克风 IsRecordMicrophone = false; } StopRecordAudio(1); if (!IsRecordMicrophone) { ErrMessage = "无法录制麦克风,请关闭杀毒软件,确保麦克风处于可用状态!"; //return false; } } #endregion myProcess = new Process(); LogPath = CreateffmpegLog(); myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 if (IsRecordSpeaker && IsRecordMicrophone && RecordingSound && RecordingMicrophone) { myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" -f dshow -i audio=\"" + MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 " + PathName; } else if (IsRecordSpeaker && RecordingSound) { myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" " + PathName; } else if (IsRecordMicrophone && RecordingMicrophone) { //录制麦克风 if (!string.IsNullOrWhiteSpace(MicrophoneName)) { myProcess.StartInfo.Arguments = "-f dshow -i audio=\"" + MicrophoneName + "\" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 " + PathName; } } if (OutputVideoLog) { LogHelper.WriteInfoLog("【录音】:" + myProcess.StartInfo.Arguments); } FileToolsCommon.AppendText(LogPath, "【录音】:" + myProcess.StartInfo.Arguments + "\r\n"); myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output); try { myProcess.Start(); //启动线程 myProcess.BeginErrorReadLine(); //开始异步读取 } catch (Exception ex) { if (ex.Message.Contains("访问")) { ErrMessage = "无法录制声音,访问被拒绝,请关闭杀毒软件或信任此软件后重试!"; } else { ErrMessage = ex.Message + " 请关闭杀毒软件或信任此软件后重试!"; } LogHelper.WriteErrLog("【录音】(StartRecordingAudio)" + ErrMessage, ex); myProcess.Dispose(); myProcess = null; return false; } return true; } /// /// 暂停录制 /// public bool SuspendFFmpeg() { if (myProcess != null) { myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制 myProcess.Close();//关闭进程 myProcess.Dispose();//释放资源 } #region 进程是否已经释放 bool IsRunning = true; while (IsRunning) { IsRunning = false; Process[] ProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in ProcessArray) { IsRunning = true; Thread.Sleep(100); } } #endregion myProcess = null; return true; //myProcess.Kill(); } /// /// 停止录制并合成文件 /// /// 文件存储路径 必须与开始时的路径完全一致 public bool StopFFmpeg(string FilePath) { try { //文件完整路径 if (myProcess != null) { myProcess.StandardInput.WriteLine("q");//在这个进程的控制台中模拟输入q,用于暂停录制 myProcess.Close();//关闭进程 myProcess.Dispose();//释放资源 } } catch (Exception) { myProcess = null; } #region 进程是否已经释放 bool IsRunning = true; while (IsRunning) { IsRunning = false; Process[] ProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in ProcessArray) { IsRunning = true; Thread.Sleep(100); } } #endregion try { myProcess = null; Thread t1 = new Thread(MergeVideoOrAudio); t1.Start(FilePath); return true; } catch (Exception ex) { LogHelper.WriteErrLog("【合成文件】:" + FilePath + "合成失败," + ex.Message, ex); return false; } //文件合成 } /// /// 图片音频合成视频 /// /// 图片列表位置 命名为1.png 2.png /// MP3音频位置 /// 视频保存位置 /// 每秒帧率 /// 视频宽度 /// 视频高度 public bool SynthesisVideo(string ImageListPath, string Mp3Path, string VideoSavePath, int Frequency, int VideoWidth, int VideoHeight, out string ErrMessage) { //new Thread(new ThreadStart(new Action(() => //{ //}))).Start(); while (myProcess != null) { Thread.Sleep(100); } ErrMessage = null; string Extension = FileToolsCommon.GetIOExtension(VideoSavePath);//扩展名 Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg"); //Debug.WriteLine(KillProcessArray.Length.ToString()); foreach (Process KillProcess in KillProcessArray) { KillProcess.Kill(); } myProcess = new Process(); try { LogPath = CreateffmpegLog(); string mp3Str = ""; #region 判断音频是否存在 if (!string.IsNullOrWhiteSpace(Mp3Path)) { if (FileToolsCommon.IsExistFile(Mp3Path)) { mp3Str = "-i " + Mp3Path; } } #endregion myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 switch (Extension.ToUpper()) { case ".MP4": myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " + ImageListPath + @"%d.png " + mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " + VideoSavePath; break; case ".AVI": myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " + ImageListPath + @"%d.png " + mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -f AVI " + VideoSavePath; break; case ".FLV": myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " + ImageListPath + @"%d.png " + mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -f FLV " + VideoSavePath; break; default: myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " + ImageListPath + @"%d.png " + mp3Str + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " + VideoSavePath; break; } if (OutputVideoLog) { LogHelper.WriteInfoLog("【图片音频合成视频】:" + myProcess.StartInfo.Arguments); } FileToolsCommon.AppendText(LogPath, "【图片音频合成视频】:" + myProcess.StartInfo.Arguments + "\r\n"); myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output); try { myProcess.Start(); //启动线程 myProcess.BeginErrorReadLine(); //开始异步读取 myProcess.WaitForExit(); //阻塞等待进程结束 myProcess.Close(); //关闭进程 } catch (Exception ex) { if (ex.Message.Contains("访问")) { ErrMessage = "访问被拒绝,请关闭杀毒软件或新任此软件后重试!"; } else { ErrMessage = ex.Message + " 请关闭杀毒软件或信任此软件后重试!"; } LogHelper.WriteErrLog("【录音】(StartRecordingAudio)" + ErrMessage, ex); myProcess.Dispose(); myProcess = null; return false; } myProcess.Dispose(); //释放资源 #region 进程是否已经释放 bool IsRunning = true; while (IsRunning) { IsRunning = false; Process[] ProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in ProcessArray) { IsRunning = true; Thread.Sleep(100); } } #endregion myProcess = null; //生成视频后删除图片和音频保存列表 //FileToolsCommon.DeleteDirectory(ImageListPath); //FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(Mp3Path)); Thread.Sleep(100); FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(VideoSavePath) + "temp"); return true; } catch (Exception ex) { LogHelper.WriteErrLog("【视频合成】(SynthesisVideo)视频生成失败," + ex.Message, ex); ErrMessage = ex.Message; return false; } //Dispatcher.Invoke(() => //{ //}); //// 允许不同线程间的调用 //Control.CheckForIllegalCrossThreadCalls = false; } /// /// 视频音频合成 /// /// 存储路径 public void MergeVideoOrAudio(object FilePathobj) { //new Thread(new ThreadStart(new Action(() => //{ //Dispatcher.Invoke(() => //{ //}); //}))).Start(); while (myProcess != null) { Thread.Sleep(100); } //路径 string FilePath = (string)FilePathobj; string Path = FileToolsCommon.GetDirectoryName(FilePath); //扩展名 string Extension = FileToolsCommon.GetIOExtension(FilePath); //Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg"); //foreach (var KillProcess in KillProcessArray) //{ // KillProcess.Kill(); //} #region 判断文件是否存在 if (Extension.ToUpper() == ".MP3") { if (!FileToolsCommon.IsExistFile(Path + "temp/filelist.d")) { return; } } else { if (!FileToolsCommon.IsExistFile(Path + "temprs/filelist.d")) { return; } } #endregion myProcess = new Process(); LogPath = CreateffmpegLog(); myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 if (Extension.ToUpper() == ".MP3") { myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "temp/filelist.d -c copy " + FilePath; } else { myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "temprs/filelist.d -c copy " + FilePath; } if (OutputVideoLog) { LogHelper.WriteInfoLog("【音视频合成】:" + myProcess.StartInfo.Arguments); } FileToolsCommon.AppendText(LogPath, "【音视频合成】:" + myProcess.StartInfo.Arguments + "\r\n"); myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output); myProcess.Start(); //启动线程 myProcess.BeginErrorReadLine(); //开始异步读取 myProcess.WaitForExit(); //阻塞等待进程结束 myProcess.Close(); //关闭进程 myProcess.Dispose(); //释放资源 #region 进程是否已经释放 bool IsRunning = true; while (IsRunning) { IsRunning = false; Process[] ProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in ProcessArray) { IsRunning = true; Thread.Sleep(100); } } #endregion myProcess = null; if (Extension.ToUpper() == ".MP3") { FileToolsCommon.DeleteDirectory(Path + "temp/"); } else { FileToolsCommon.DeleteDirectory(Path + "temprs/"); } } /// /// 视频剪辑 /// /// 视频路径 /// 开始时间 格式 HH:mm:ss /// 结束时间 格式 HH:mm:ss /// 保存文件路径 public void ClipVideo(string VideoFilePath, string StartTime, string EndTime, string SaveFilePath) { //new Thread(new ThreadStart(new Action(() => //{ //Dispatcher.Invoke(() => //{ //}); //}))).Start(); while (myProcess != null) { Thread.Sleep(100); } //string Path = FileToolsCommon.GetDirectoryName(VideoFilePath); Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (var KillProcess in KillProcessArray) { KillProcess.Kill(); } myProcess = new Process(); LogPath = CreateffmpegLog(); this.myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 //ffmpeg -ss 开始时间 -to 结束时间 -accurate_seek -i mp4输入路径 -c:v libx264 -c:a aac -strict experimental -vf scale = 宽:高 -b 500k 输出路径.mp4 myProcess.StartInfo.Arguments = "-i " + VideoFilePath + " -vcodec copy -acodec copy -ss " + StartTime + " -to " + EndTime + " " + SaveFilePath; //-filter:a "volume=0.5" myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output); myProcess.Start(); //启动线程 myProcess.BeginErrorReadLine(); //开始异步读取 myProcess.WaitForExit(); //阻塞等待进程结束 myProcess.Close(); //关闭进程 myProcess.Dispose(); //释放资源 #region 进程是否已经释放 bool IsRunning = true; while (IsRunning) { IsRunning = false; Process[] ProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (var KillProcess in ProcessArray) { IsRunning = true; Thread.Sleep(100); } } #endregion myProcess = null; } /// /// 获取文件完整路径 音频 /// /// 文件路径 /// private string GetFilePathName(string FilePath) { //路径 string Path = FileToolsCommon.GetDirectoryName(FilePath); //Path.GetFileName(FilePath);//获取文件名 string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名 string tempFilePath = Path + "temp/"; //创建临时目录 FileToolsCommon.CreateDirectory(tempFilePath); //创建记录文件 if (!FileToolsCommon.IsExistFile(tempFilePath + "filelist.d")) { FileToolsCommon.CreateFile(tempFilePath + "filelist.d"); } int num = 1; string CompleteFilePath = tempFilePath + num + Extension; while (FileToolsCommon.IsExistFile(CompleteFilePath)) { num++; CompleteFilePath = tempFilePath + num + Extension; } FileToolsCommon.AppendText(tempFilePath + "filelist.d", "file ./" + num + Extension + "\r\n"); return CompleteFilePath; } /// /// 获取文件完整路径 录屏 /// /// 文件路径 /// private string GetRSFilePathName(string FilePath) { //路径 string Path = FileToolsCommon.GetDirectoryName(FilePath); //Path.GetFileName(FilePath);//获取文件名 string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名 string tempFilePath = Path + "temprs/"; //创建临时目录 FileToolsCommon.CreateDirectory(tempFilePath); //创建记录文件 if (!FileToolsCommon.IsExistFile(tempFilePath + "filelist.d")) { FileToolsCommon.CreateFile(tempFilePath + "filelist.d"); } int num = 1; string CompleteFilePath = tempFilePath + num + Extension; while (FileToolsCommon.IsExistFile(CompleteFilePath)) { num++; CompleteFilePath = tempFilePath + num + Extension; } FileToolsCommon.AppendText(tempFilePath + "filelist.d", "file ./" + num + Extension + "\r\n"); return CompleteFilePath; } /// /// 生成缩略图 /// /// 视频地址 /// 图片地址 /// 图片大小 /// 图片大小 public bool GenerateThumbnails(string VideoPath, string ImagePath, int Width = 0, int Height = 0) { while (myProcess != null) { Thread.Sleep(100); } Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in KillProcessArray) { KillProcess.Kill(); } string WHStr = ""; if (Width > 0) { WHStr = " -s " + Width + "x" + Height; } try { myProcess = new Process(); LogPath = CreateffmpegLog(); myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 //myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2 -s " + thubWidth + "*" + thubHeight + " -f image2 \"" + ImagePath + "\""; myProcess.StartInfo.Arguments = "-i \"" + VideoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2" + WHStr + " -f image2 \"" + ImagePath + "\""; if (OutputVideoLog) { LogHelper.WriteInfoLog("【生成缩略图】:" + myProcess.StartInfo.Arguments); } FileToolsCommon.AppendText(LogPath, "【生成缩略图】:" + myProcess.StartInfo.Arguments + "\r\n"); myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output); myProcess.Start(); //启动线程 myProcess.BeginErrorReadLine(); //开始异步读取 myProcess.WaitForExit(); //阻塞等待进程结束 myProcess.Close(); //关闭进程 myProcess.Dispose(); //释放资源 #region 进程是否已经释放 bool IsRunning = true; while (IsRunning) { IsRunning = false; Process[] ProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in ProcessArray) { IsRunning = true; Thread.Sleep(100); } } #endregion myProcess = null; return true; } catch (Exception ex) { LogHelper.WriteErrLog("【生成缩略图】(GenerateThumbnails)缩略图生成失败," + ex.Message, ex); return false; } } /// /// 视频转码 /// /// 源视频 /// 目标视频存储路径 /// 宽 /// 高 /// public bool VideoTranscode(string VideoPathName, string VideoSavePathName, int Width, int Height) { while (myProcess != null) { Thread.Sleep(100); } Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in KillProcessArray) { KillProcess.Kill(); } try { myProcess = new Process(); LogPath = CreateffmpegLog(); myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 myProcess.StartInfo.Arguments = "-i " + VideoPathName + " -acodec copy -vcodec libx264 -s " + Width + "*" + Height + " " + VideoSavePathName; if (OutputVideoLog) { LogHelper.WriteInfoLog("【视频转码】:" + myProcess.StartInfo.Arguments); } FileToolsCommon.AppendText(LogPath, "【视频转码】:" + myProcess.StartInfo.Arguments + "\r\n"); myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output); myProcess.Start(); //启动线程 myProcess.BeginErrorReadLine(); //开始异步读取 myProcess.WaitForExit(); //阻塞等待进程结束 myProcess.Close(); //关闭进程 myProcess.Dispose(); //释放资源 #region 进程是否已经释放 bool IsRunning = true; while (IsRunning) { IsRunning = false; Process[] ProcessArray = Process.GetProcessesByName("ffmpeg"); foreach (Process KillProcess in ProcessArray) { IsRunning = true; Thread.Sleep(100); } } #endregion myProcess = null; Thread.Sleep(200); FileToolsCommon.DeleteFile(VideoPathName); return true; } catch (Exception ex) { LogHelper.WriteErrLog("【视频转码】(VideoTranscode)视频转码失败," + ex.Message, ex); return false; } } /// /// 创建日志文件 /// /// private string CreateffmpegLog() { string LogFileName = DateTime.Now.ToString("yyyyMMdd"); string LogFilePath = FileToolsCommon.GetFileAbsolutePath("/Log/FFMpegLog/"); FileToolsCommon.CreateDirectory(LogFilePath); string LogName = LogFilePath + LogFileName + ".log"; try { if (!FileToolsCommon.IsExistFile(LogName)) { FileToolsCommon.CreateFile(LogName); FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" + "****************************************************************************************************\r\n"); return LogName; } else { int num = 0; while (FileToolsCommon.GetFileSizeByMB(LogName) > 2) { num++; LogName = LogFilePath + LogFileName + "_" + num + ".log"; FileToolsCommon.CreateFile(LogName); } FileToolsCommon.AppendText(LogName, "\r\n****************************************************************************************************" + "****************************************************************************************************\r\n"); return LogName; } } catch (Exception) { return LogName; } } /// /// 输出结果 /// /// /// private void Output(object sendProcess, DataReceivedEventArgs output) { if (!string.IsNullOrEmpty(output.Data)) { FileToolsCommon.AppendText(LogPath, "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff") + "】:"); FileToolsCommon.AppendText(LogPath, output.Data + "\r\n"); } } #region 麦克风声卡检测 #region 获取麦克风 private bool IsMicrophone = false; /// /// 使用FFmpeg获取的麦克风名 /// private string MicrophoneNameInfo = ""; /// /// 获取麦克风名称 /// /// public string GetMicrophoneName() { return GetAudioInDevices(); //string FullName = GetInDeviceFull(); //if(string.IsNullOrWhiteSpace(MicrophoneNameInfo)) //{ // return FullName; //} ////byte[] bufferASCII = Encoding.Default.GetBytes(MicrophoneNameInfo); ////string str = Encoding.UTF8.GetString(bufferASCII); ////str = str.Replace("?", " "); ////int num = str.Length; ////int num1 = FullName.Length; //if (FullName.Contains("(") || FullName.Contains("(")) //{ // string EName = FullName.Substring(FullName.IndexOf("(")+1); // if(string.IsNullOrEmpty(EName)) // { // EName = FullName.Substring(FullName.IndexOf("(") + 1); // EName = EName.Substring(0, EName.LastIndexOf(")")); // } // else // { // //EName = EName.Substring(0, EName.LastIndexOf(")")); // } // if(MicrophoneNameInfo.Contains(EName)) // { // return FullName; // } // else // { // return GetInDeviceName(); // } //} //else //{ // return FullName; //} } /// /// 获取麦克风列表 /// /// public List GetMicrophoneNameList() { return GetAudioInDevicesList(); } /// /// 使用FFmpeg获取设备名称--待定 /// public void GetMToFFmpeg() { //return; new Thread(new ThreadStart(new Action(() => { try { while (myProcess != null) { Thread.Sleep(100); } myProcess = new Process(); LogPath = CreateffmpegLog(); myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径 myProcess.StartInfo.Arguments = " -list_devices true -f dshow -i dummy"; if (OutputVideoLog) { LogHelper.WriteInfoLog("【获取设备信息】:" + myProcess.StartInfo.Arguments); } FileToolsCommon.AppendText(LogPath, "【获取设备信息】:" + myProcess.StartInfo.Arguments + "\r\n"); myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动 myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出 myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口 myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入 //外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN myProcess.ErrorDataReceived += GetDevInfoData; myProcess.Start(); //启动线程 myProcess.BeginErrorReadLine(); //开始异步读取 myProcess.WaitForExit(); //阻塞等待进程结束 myProcess.Close(); //关闭进程 myProcess.Dispose(); //释放资源 myProcess = null; } catch (Exception ex) { LogHelper.WriteErrLog("【获取设备信息】(GetMToFFmpeg)信息获取失败:" + ex.Message, ex); } }))).Start(); } /// /// 使用FFmpeg获取设备名称--待定 /// /// /// private void GetDevInfoData(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) { if (IsMicrophone) { try { MicrophoneNameInfo = e.Data; MicrophoneNameInfo = MicrophoneNameInfo.Substring(MicrophoneNameInfo.IndexOf("]") + 1); MicrophoneNameInfo = MicrophoneNameInfo.Replace("\"", ""); MicrophoneNameInfo = MicrophoneNameInfo.Trim(); #region 测试 //byte[] bufferASCII = Encoding.Default.GetBytes(MicrophoneNameInfo); //MicrophoneNameInfo = Encoding.UTF8.GetString(bufferASCII); // 统一使用UTF-8 ////System.Text.Encoding GB2312 = System.Text.Encoding.GetEncoding("GB2312"); ////byte[] gb = GB2312.GetBytes(MicrophoneNameInfo); ////MicrophoneNameInfo = Encoding.UTF8.GetString(gb); #endregion IsMicrophone = false; FileToolsCommon.AppendText(LogPath, e.Data); //FileToolsCommon.AppendText(LogPath, "\r\n***************************************************\r\n"); } catch (Exception ex) { LogHelper.WriteErrLog("【获取设备信息】(GetDevInfoData)信息获取失败:" + ex.Message, ex); } } else { if (e.Data.Contains("DirectShow audio devices")) { //FileToolsCommon.AppendText(LogPath, "\r\n***************************************************\r\n"); IsMicrophone = true; } FileToolsCommon.AppendText(LogPath, e.Data); FileToolsCommon.AppendText(LogPath, "\r\n"); } } } /// /// AForge获取麦克风 /// /// public static string GetAudioInDevices() { List devicesList = new List(); try { FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.AudioInputDevice); foreach (FilterInfo device in videoDevices) { devicesList.Add(device.Name); } } catch (ApplicationException) { //Console.WriteLine("No local capture devices"); } if (devicesList.Count > 1) { return devicesList[1]; } else { return ""; } } /// /// AForge获取麦克风列表 /// /// public static List GetAudioInDevicesList() { List devicesList = new List(); try { FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.AudioInputDevice); foreach (FilterInfo device in videoDevices) { if (device.Name == "virtual-audio-capturer") { continue; } devicesList.Add(device.Name); } } catch (ApplicationException) { //Console.WriteLine("No local capture devices"); } return devicesList; } /// /// 获取声音输入设备名称 不完整 /// /// public static string GetInDeviceName() { List devices = new List(); int waveInDevices = WaveIn.DeviceCount; for (int i = 0; i < waveInDevices; i++) { devices.Add(WaveIn.GetCapabilities(i)); } List devs = new List(); devs = devices.Select(item => item.ProductName).ToList(); if (devs.Count > 0) { byte[] buffer = Encoding.UTF8.GetBytes(devs[0]); string MicrophoneName = Encoding.UTF8.GetString(buffer); // 统一使用UTF-8 return MicrophoneName; } else { return ""; } } /// /// 获取声音输入设备完整名称 /// /// public static string GetInDeviceFull() { List devices = new List(); MMDeviceEnumerator enumberator = new MMDeviceEnumerator(); MMDeviceCollection deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All); for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++) { WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice); foreach (MMDevice device in deviceCollection) { try { if (device.FriendlyName.StartsWith(deviceInfo.ProductName)) { devices.Add(device.FriendlyName); break; } } catch (Exception) { continue; } } } if (devices.Count > 0) { byte[] buffer = Encoding.UTF8.GetBytes(devices[0]); string MicrophoneName = Encoding.UTF8.GetString(buffer); // 统一使用UTF-8 return MicrophoneName; } else { return ""; } } /// /// 获取声音输入设备完整名称列表 /// /// public List GetInDeviceListFull() { List devices = new List(); MMDeviceEnumerator enumberator = new MMDeviceEnumerator(); MMDeviceCollection deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All); for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++) { WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice); foreach (MMDevice device in deviceCollection) { try { if (device.FriendlyName.StartsWith(deviceInfo.ProductName)) { devices.Add(device.FriendlyName); break; } } catch (Exception) { continue; } } } return devices; } /// /// 获取麦克风---废弃 /// private string GetMicrophone() { List devs = new List(); MMDeviceEnumerator enumberator = new MMDeviceEnumerator(); MMDeviceCollection deviceCollection = enumberator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All); for (int waveInDevice = 0; waveInDevice < WaveIn.DeviceCount; waveInDevice++) { WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice); foreach (MMDevice device in deviceCollection) { try { if (device.FriendlyName.StartsWith(deviceInfo.ProductName)) { devs.Add(device.FriendlyName); break; } } catch (Exception) { } } } if (devs.Count > 0) { return devs[0]; } else { return ""; } } #endregion #region 录音 /// /// 开始录制麦克风 /// public bool StartAudio(string audioFile, string DeviceName = "") { try { int deviceIndex = 0; waveIn = new WaveInEvent(); List InDeviceList = GetInDeviceListFull(); if (InDeviceList.Exists(x => DeviceName.Contains(x))) { deviceIndex = InDeviceList.FindIndex(x => DeviceName.Contains(x)); } waveIn.DeviceNumber = deviceIndex; //waveIn.DeviceNumber = 1; //生成音频文件的对象 WaveFileWriter writer = new WaveFileWriter(audioFile, waveIn.WaveFormat); //开始录音,写数据 waveIn.DataAvailable += (s, a) => { writer.Write(a.Buffer, 0, a.BytesRecorded); }; return true; } catch (Exception ex) { LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex); return false; } } //public bool StopAudio() //{ // try // { // //结束录音 // waveIn.RecordingStopped += (s, a) => // { // writer.Dispose(); // writer = null; // waveIn.Dispose(); // waveIn = null; // try // { // FileToolsCommon.DeleteFile(audioFile); // } // catch (Exception) // { // } // }; // waveIn.StartRecording(); // } // catch (Exception ex) // { // throw; // } //} #endregion #region 检测是否可用 /// /// 录制麦克风的声音 /// private WaveInEvent waveIn = null; //new WaveInEvent(); /// /// 开始录制麦克风 /// public bool StartRecordAudio(string audioFile, string DeviceName = "", bool IsStopDelFile=true) { try { int deviceIndex = 0; waveIn = new WaveInEvent(); List InDeviceList = GetInDeviceListFull(); if (InDeviceList.Exists(x => DeviceName.Contains(x))) { deviceIndex = InDeviceList.FindIndex(x => DeviceName.Contains(x)); } waveIn.DeviceNumber = deviceIndex; //waveIn.DeviceNumber = 1; //生成音频文件的对象 WaveFileWriter writer = new WaveFileWriter(audioFile, waveIn.WaveFormat); //开始录音,写数据 waveIn.DataAvailable += (s, a) => { writer.Write(a.Buffer, 0, a.BytesRecorded); }; //结束录音 waveIn.RecordingStopped += (s, a) => { writer.Dispose(); writer = null; waveIn.Dispose(); waveIn = null; try { if (IsStopDelFile) { FileToolsCommon.DeleteFile(audioFile); } } catch (Exception) { } }; waveIn.StartRecording(); return true; } catch (Exception ex) { LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex); return false; } } /// /// 结束录制音频 /// /// 1麦克风 2扬声器 public void StopRecordAudio(int type) { try { if (type == 1) { if (waveIn != null) { try { waveIn.StopRecording(); } catch (Exception) { waveIn = null; } } } else if (type == 2) { if (capture != null) { try { capture.StopRecording(); } catch (Exception) { capture = null; } } } } catch (Exception ex) { LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex); } } /// /// 录制扬声器的声音 /// private WasapiLoopbackCapture capture = null; //new WasapiLoopbackCapture(); /// /// 开始录制扬声器 /// public bool StartRecordSpeakerAudio(string audioFile) { try { capture = new WasapiLoopbackCapture(); //生成音频文件的对象 WaveFileWriter writer = new WaveFileWriter(audioFile, capture.WaveFormat); capture.DataAvailable += (s, a) => { writer.Write(a.Buffer, 0, a.BytesRecorded); }; //结束录音 capture.RecordingStopped += (s, a) => { writer.Dispose(); writer = null; capture.Dispose(); capture = null; try { FileToolsCommon.DeleteFile(audioFile); } catch (Exception) { } }; capture.StartRecording(); return true; } catch (Exception ex) { LogHelper.WriteErrLog("【麦克风】麦克风不可用:" + ex.Message, ex); return false; } } #endregion #endregion } }