using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Windows; using System.Windows.Shapes; using WPFDevelopers.Helpers; using File = System.IO.File; namespace VideoConcat { /// /// Video.xaml 的交互逻辑 /// public partial class Video : Window { public string text = "", outPut = ""; public Dictionary videoFolders = []; private static string Path = "", ParentPath = ""; public Dictionary AllPublicCombinVideos = []; int GenerateVideoCount = 0; public static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public Video() { InitializeComponent(); Init(); } public static void Init() { ParentPath = Directory.GetCurrentDirectory(); log4net.Config.XmlConfigurator.Configure(); } private async void Button_Click(object sender, RoutedEventArgs e) { try { GenerateVideoCount = int.Parse(videoCount.Text); } catch (Exception) { MessageBox.Show("请输入正确的数字"); return; } WriteTxt($"生成视频的个数:{GenerateVideoCount} 个"); Check_Folder(); MakeOutPutDir(); startButton.IsEnabled = false; GenerateAllvideos(videoFolders); var dateStart = DateTime.Now; //记录用时的起始时间 await Task.Run(async () => { List> lists = GenerateAllvideos(videoFolders); await MockIOPerformanceAsync(lists); startButton.Dispatcher.Invoke(() => { startButton.IsEnabled = true; }); }); var dateEnd = DateTime.Now; var timeSpan = dateEnd - dateStart;//记录开票用时 WriteTxt($"用时:{timeSpan.TotalSeconds}秒"); } private void Check_Folder() { DirectoryInfo? directory = Directory.GetParent(ParentPath); if (directory == null) { WriteTxt("获取视频路径失败!"); return; } Path = directory.FullName; WriteTxt("当前目录为:" + Path); videoFolders.Clear(); AllPublicCombinVideos.Clear(); try { int k = 1; for (int i = 100; i >0; i--) { var folder = string.Format("{0:D2}", i); string currentFolderPath = $"{Path}\\{folder}"; if (Directory.Exists(currentFolderPath)) { string[] videos = GetAllVideos(currentFolderPath); if (videos.Length > 0) { videoFolders.Add(k, videos); AllPublicCombinVideos.Add(k, new A()); k++; } } } return; } catch (Exception ex) { MessageBox.Show(ex.Message); return; } } private void WriteTxt(string str) { outputTxt.Dispatcher.Invoke(() => { log.Info(str); text += str + "\r\n"; outputTxt.Text = text; scrowText.ScrollToEnd(); }); } public static string[] GetAllVideos(string path) { return Directory.GetFiles(path, "*.mp4", SearchOption.AllDirectories); } public void CombineMp4WithoutTxt(List files, string StrOutMp4Path) { Process p = new();//建立外部调用线程 p.StartInfo.FileName = Directory.GetCurrentDirectory() + "\\ffmpeg.exe";//要调用外部程序的绝对路径 //int i = 0; //string vao = ""; List ooootext = []; files.Reverse(); files.ForEach(file => { //#file = System.Web.HttpUtility.UrlEncode(file); ooootext.Add($"file '{file}'"); }); var random = new Random(); string tmpConcatFile = $"{outPut}\\{Guid.NewGuid()}{random.Next(100000, 999999)}.aaa"; // 也可以指定编码方式 //File.WriteAllLines(tmpConcatFile, ooootext, Encoding.UTF8); File.WriteAllText(tmpConcatFile, string.Join("\n", [.. ooootext]), new System.Text.UTF8Encoding(false)); string StrArg = $"-f concat -safe 0 -i {tmpConcatFile} -bsf:a aac_adtstoasc -movflags +faststart -c copy {StrOutMp4Path}"; //string StrArg = $"-i {combineFile} -filter_complex \"{vao} concat=n={i}:v=1:a=1 [v][a]\" -map \"[v]\" -map \"[a]\" {StrOutMp4Path}"; 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();//释放资源 if (File.Exists(tmpConcatFile)) { try { File.Delete(tmpConcatFile); } catch { } } } private void Output(object sendProcess, DataReceivedEventArgs output) { if (!String.IsNullOrEmpty(output.Data)) { log.Info(output.Data, null); } } public void MakeOutPutDir() { outPut = $"{Path}\\output"; if (!Directory.Exists(outPut)) { Directory.CreateDirectory(outPut); } } public async Task> MockIOPerformanceAsync(List> ls) { List lss = []; List tasks = []; foreach (var item in ls) { Task task = new(() => { DateTime now = DateTime.Now; Random random = new(); string randomString = random.Next(100000, 999999).ToString(); string result = now.ToString("yyyyMMddHHmmss") + randomString; CombineMp4WithoutTxt(item, $"{outPut}\\{result}.mp4"); }); tasks.Add(task); task.Start(); if (tasks.Count >= GenerateVideoCount) { break; } } foreach (var item in tasks) { await item; } return lss; } private static List> GenerateAllvideos(Dictionary dict) { List> ls = []; foreach (var item in dict) { ls.Add(new List(item.Value)); } List> permutations = CalculatePermutations(ls); return permutations; } static List> CalculatePermutations(List> inputList) { List> result = []; // 递归计算全排列的辅助函数 void Permute(List currentPermutation, int index) { if (index == inputList.Count) { result.Add(new List(currentPermutation)); return; } foreach (string item in inputList[index]) { currentPermutation.Add(item); Permute(currentPermutation, index + 1); currentPermutation.RemoveAt(currentPermutation.Count - 1); } } Permute([], 0); return result; } } public class A { public int Index { get; set; } }; }