using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using VisioForge.Shared.NAudio.CoreAudioApi;
using VisioForge.Shared.NAudio.Wave;
namespace Common.system
{
///
/// ffmpeg帮助类
/// 创建人:赵耀
/// 创建时间::2020年8月22日
/// 需要安装\ffmpeg\bin\Setup Screen Capturer Recorder v0.12.10.exe
/// 本地调试需要配置环境变量 将ffmpeg.exe位置配置到环境变量的path中
///
public class FFMpeg
{
public Process myProcess = null;
///
/// ffmpeg输出日志文件地址 每个文件2MB左右
///
string LogPath = "";
///
/// 录制屏幕
///
/// 文件存储路径
public void StartRecordingVideo(string FilePath)
{
while (myProcess != null)
{
Thread.Sleep(100);
}
//路径
string Path = FileToolsCommon.GetDirectoryName(FilePath);
string Extension = FileToolsCommon.GetIOExtension(FilePath);//扩展名
//文件保存路径
string PathName = GetFilePathName(FilePath);
Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
foreach (var KillProcess in KillProcessArray)
{
KillProcess.Kill();
}
myProcess = new Process();
LogPath = CreateffmpegLog();
string MicrophoneName = GetMicrophone();
myProcess.StartInfo.FileName = FileToolsCommon.GetFileAbsolutePath(@"/ffmpeg/bin/ffmpeg.exe"); //ffmpeg.exe的绝对路径
switch (Extension.ToUpper())
{
case "MP4":
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 avi " + 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":
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:
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 avi " + 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;
}
myProcess.StartInfo.UseShellExecute = false; //不使用操作系统外壳程序启动
myProcess.StartInfo.RedirectStandardError = true; //重定向标准错误输出
myProcess.StartInfo.CreateNoWindow = true; //不显示程序窗口
myProcess.StartInfo.RedirectStandardInput = true; //用于模拟该进程控制台的输入
myProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);
myProcess.Start();
myProcess.BeginErrorReadLine();
}
///
/// 录制音频
///
/// MP3音频位置
public void StartRecordingAudio(string Mp3Path)
{
while (myProcess != null)
{
Thread.Sleep(100);
}
//文件保存路径
string PathName = GetFilePathName(Mp3Path);
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的绝对路径
string MicrophoneName = GetMicrophone();
if (string.IsNullOrWhiteSpace(MicrophoneName))
{
myProcess.StartInfo.Arguments = "-f dshow -i audio=\"virtual-audio-capturer\" " + PathName;
}
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 " + PathName;
}
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(); //开始异步读取
}
///
/// 暂停录制
///
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 (var KillProcess in ProcessArray)
{
IsRunning = true;
Thread.Sleep(100);
}
}
#endregion
myProcess = null;
return true;
//myProcess.Kill();
}
///
/// 停止录制并合成文件
///
/// 文件存储路径 必须与开始时的路径完全一致
public bool StopFFmpeg(string FilePath)
{
//文件完整路径
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 (var KillProcess in ProcessArray)
{
IsRunning = true;
Thread.Sleep(100);
}
}
#endregion
myProcess = null;
Thread t1 = new Thread(MergeVideoOrAudio);
t1.Start(FilePath);
return true;
//文件合成
}
///
/// 图片音频合成视频
///
/// 图片列表位置 命名为1.png 2.png
/// MP3音频位置
/// 视频保存位置
/// 每秒帧率
/// 视频宽度
/// 视频高度
public void SynthesisVideo(string ImageListPath, string Mp3Path, string VideoSavePath, int Frequency, int VideoWidth, int VideoHeight)
{
new Thread(new ThreadStart(new Action(() =>
{
Thread.Sleep(500);
while (myProcess != null)
{
Thread.Sleep(100);
}
string Extension = FileToolsCommon.GetIOExtension(VideoSavePath);//扩展名
Process[] KillProcessArray = Process.GetProcessesByName("ffmpeg");
//Debug.WriteLine(KillProcessArray.Length.ToString());
foreach (var KillProcess in KillProcessArray)
{
KillProcess.Kill();
}
myProcess = new Process();
LogPath = CreateffmpegLog();
this.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 -i " +
Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
VideoSavePath;
break;
case "AVI":
myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
ImageListPath + @"%d.png -i " +
Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -f AVI " +
VideoSavePath;
break;
case "FLV":
myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
ImageListPath + @"%d.png -i " +
Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -f FLV " +
VideoSavePath;
break;
default:
myProcess.StartInfo.Arguments = @"-y -r " + Frequency + " -i " +
ImageListPath + @"%d.png -i " +
Mp3Path + @" -s " + VideoWidth + "x" + VideoHeight + " -vcodec mpeg4 " +
VideoSavePath;
break;
}
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;
//生成视频后删除图片和音频保存列表
//FileToolsCommon.DeleteDirectory(ImageListPath);
//FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(Mp3Path));
FileToolsCommon.DeleteDirectory(FileToolsCommon.GetDirectoryName(VideoSavePath) + "temp/");
//Dispatcher.Invoke(() =>
//{
//});
//// 允许不同线程间的调用
//Control.CheckForIllegalCrossThreadCalls = false;
}))).Start();
}
///
/// 视频音频合成
///
/// 存储路径
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);
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的绝对路径
myProcess.StartInfo.Arguments = "-f concat -safe 0 -i " + Path + "temp/filelist.d -c copy " + FilePath;
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;
FileToolsCommon.DeleteDirectory(Path + "temp/");
}
///
/// 获取文件完整路径
///
/// 文件路径
///
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;
}
///
/// 生成缩略图
///
/// 视频地址
/// 图片地址
public void GenerateThumbnails(string VideoPath, string ImagePath)
{
while (myProcess != null)
{
Thread.Sleep(100);
}
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的绝对路径
//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 -f image2 \"" + ImagePath + "\"";
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;
}
///
/// 创建日志文件
///
///
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, output.Data);
}
}
//private void P_ErrorDataReceived(object sender, DataReceivedEventArgs e)
//{
// //+= new DataReceivedEventHandler((s, message) => { Console.WriteLine(message.Data); })
// using (StreamWriter fs = new StreamWriter("E:\\项目\\测试\\Wpf测试\\bin\\Debug\\ffmpeg\\log.txt", true))
// {
// fs.WriteLine(e.Data);
// Debug.WriteLine(output.Data.ToString());
// }
//}
///
/// 获取麦克风
///
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 "";
}
}
///
/// 获取麦克风列表
///
public List GetMicrophoneList()
{
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)
{
}
}
}
return devs;
}
}
}