using System.Diagnostics; using System.IO; using System.Windows; namespace VideoConcat { /// /// Video.xaml 的交互逻辑 /// public partial class Video : Window { public string text = ""; public FileSystemInfo[]? Floders { get; set; } public string[] Heads = []; public string[] Tails = []; public string[][] Middles = []; public DirectoryInfo? Path { get; set; } public Video() { InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { int count = 0; try { count = int.Parse(videoCount.Text); } catch (Exception) { MessageBox.Show("请输入正确的数字"); return; } WriteTxt($"生成视频的个数:{videoCount.Text} 个"); Check_Folder(); MakeOutPutDir(); startButton.IsEnabled = false; Array.ForEach(array: Middles, s => { WriteTxt(s); }); var dateStart = DateTime.Now; //记录用时的起始时间 List> fpList = new List>(); foreach (var item in await MockIOPerformanceAsync) { var dateEnd = DateTime.Now; var timeSpan = dateEnd - dateStart;//记录开票用时 DebugText += item + " " + timeSpan.TotalSeconds + "\r\n"; } await Task.Run(() => { for (int i = 0; i <= count; i++) { System.Windows.Application.Current.Dispatcher.Invoke(() => { processVideoBar.Dispatcher.Invoke(() => { processVideoBar.Value = i; }); outputTxt.Dispatcher.Invoke(new Action(() => { WriteTxt("正在生成第" + i + "个视频"); })); }); System.Threading.Thread.Sleep(50); } startButton.Dispatcher.Invoke(() => { startButton.IsEnabled = true; }); });//ProcessVideo.RunTask(processVideoBar, startButton) } private void Check_Folder() { string currentPath = Directory.GetCurrentDirectory(); Path = Directory.GetParent(currentPath); if (Path == null) { WriteTxt("获取相关视频文件目录失败!"); return; } WriteTxt("当前目录为:" + Path.FullName); try { Boolean hasHead = false; Boolean hasTail = false; DirectoryInfo dirD = Path as DirectoryInfo; //获取文件夹下所有文件夹 Floders = dirD.GetDirectories(); //对单个FileSystemInfo进行判断,如果是文件夹则进行递归操作 foreach (FileSystemInfo folder in Floders) { WriteTxt($"{folder.Name}"); if (folder.Name == "head") { hasHead = true; Heads = GetAllVideos($"{Path}\\{folder.Name}"); continue; } if (folder.Name == "tail") { hasTail = true; Tails = GetAllVideos($"{Path}\\{folder.Name}"); continue; } Middles = Middles.Concat(GetAllVideos($"{Path}\\{folder.Name}")).ToArray(); } if (!hasHead) { WriteTxt("存在名称为head的文件夹,将作为视频开头!"); } if (!hasTail) { WriteTxt("存在名称为tail的文件夹,将作为视频结尾!"); } } catch (Exception ex) { MessageBox.Show(ex.Message); return; } } private void WriteTxt(string str) { text += str + "\r\n"; outputTxt.Text = text; scrowText.ScrollToEnd(); } public static string[] GetAllVideos(string path) { return Directory.GetFiles(path, "*.mp4", SearchOption.AllDirectories); } /// /// 执行 /// C# Process进程调用 https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.process?view=net-5.0 /// /// 执行命令 public static void CommandManager(string commandStr) { try { using (Process process = new Process()) { process.StartInfo.FileName = @"D:\FFmpeg\ffmpeg.exe";//要执行的程序名称(属性,获取或设置要启动的应用程序或文档。FileName 属性不需要表示可执行文件。 它可以是其扩展名已经与系统上安装的应用程序关联的任何文件类型。) process.StartInfo.Arguments = " " + commandStr;//启动该进程时传递的命令行参数 process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = false;//可能接受来自调用程序的输入信息 process.StartInfo.RedirectStandardOutput = false;//由调用程序获取输出信息 process.StartInfo.RedirectStandardError = false;//重定向标准错误输出 process.StartInfo.CreateNoWindow = false;//不显示程序窗口 process.Start();//启动程序 process.WaitForExit();//等待程序执行完退出进程(避免进程占用文件或者是合成文件还未生成)* } } catch (Exception e) { Console.WriteLine(e.Message); } } public void CombineMp4WithoutTxt(string[] files, string StrOutMp4Path) { Process p = new();//建立外部调用线程 p.StartInfo.FileName = Directory.GetCurrentDirectory() + "\\ffmpeg.exe";//要调用外部程序的绝对路径 string combineFile = ""; foreach (string file in files) { combineFile += $"| {file}"; } string StrArg = $"-i concat:\"{combineFile}\" -vcodec copy -acodec copy " + StrOutMp4Path + " -y"; p.StartInfo.Arguments = StrArg; p.StartInfo.UseShellExecute = false;//不使用操作系统外壳程序启动线程(一定为FALSE,详细的请看MSDN) p.StartInfo.RedirectStandardError = true;//把外部程序错误输出写到StandardError流中(这个一定要注意,FFMPEG的所有输出信息,都为错误输出流,用StandardOutput是捕获不到任何消息的...这是我耗费了2个多月得出来的经验...mencoder就是用standardOutput来捕获的) p.StartInfo.CreateNoWindow = true;//不创建进程窗口 p.ErrorDataReceived += new DataReceivedEventHandler(Output);//外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN p.Start();//启动线程 p.BeginErrorReadLine();//开始异步读取 p.WaitForExit();//阻塞等待进程结束 p.Close();//关闭进程 p.Dispose();//释放资源 } private void Output(object sendProcess, DataReceivedEventArgs output) { if (!String.IsNullOrEmpty(output.Data)) { WriteTxt(output.Data); } } public void MakeOutPutDir() { string outPut = $"{Path}\\output"; if (!Directory.Exists(outPut)) { Directory.CreateDirectory(outPut); } } public static async Task> MockIOPerformanceAsync(List ls) { List lss = new List(); List tasks = new List(); foreach (var item in ls) { Task task = new Task(() => { Debug.WriteLine(Thread.GetCurrentProcessorId()); lss.Add(item); }); tasks.Add(task); task.Start(); } foreach (var item in tasks) { await item; } return lss; } } }