using Standard; 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 = ""; static int totalVideoCount = 0; 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; } totalVideoCount = 0; Check_Folder(); WriteTxt($"最多可生成视频个数为{totalVideoCount}个"); if (GenerateVideoCount > totalVideoCount) { WriteTxt($"生成视频个数不能超过{totalVideoCount}个"); return; } MakeOutPutDir(); startButton.IsEnabled = false; //GenerateAllvideos(videoFolders); var dateStart = DateTime.Now; //记录用时的起始时间 List> lists = GenerateAllvideos(videoFolders); await MockIOPerformanceAsync(lists); startButton.Dispatcher.Invoke(() => { startButton.IsEnabled = true; }); var dateEnd = DateTime.Now; var timeSpan = dateEnd - dateStart;//记录用时 try { string mp4Path = $"{Directory.GetCurrentDirectory()}\\temp\\"; DirectoryInfo directoryInfo = new DirectoryInfo(mp4Path); FileInfo[] files = directoryInfo.GetFiles("*.mp4"); // 获取指定目录下的所有 txt 文件 foreach (FileInfo file in files) { file.Delete(); // 删除文件 } } catch (Exception) { } WriteTxt($"用时:{timeSpan.TotalSeconds}秒"); } private void Check_Folder() { WriteTxt("开始检测视频文件路径"); 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 = 1; i < 100; i++) { var folder = string.Format("{0:D2}", i); string currentFolderPath = $"{Path}\\{folder}"; if (Directory.Exists(currentFolderPath)) { string[] videos = GetAllVideos(currentFolderPath); if (videos.Length > 0) { if (totalVideoCount == 0) { totalVideoCount = 1; } WriteTxt($"检测到视频文件夹:{folder},视频个数:{videos.Length}"); totalVideoCount *= videos.Length; videoFolders.Add(k, videos); AllPublicCombinVideos.Add(k, new A()); k++; } else { WriteTxt($"视频文件夹【{folder}】未发现MP4格式视频文件"); } } } 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 async Task CombineMp4WithoutTxt(List files, string StrOutMp4Path) { var random = new Random(); //int i = 0; //string vao = ""; List ooootext = []; List ts = []; files.ForEach(file => { ts.Add(Task.Run(() => { WriteTxt(file + "转换开始"); var dateStart = DateTime.Now; string tmpOut = $"{Directory.GetCurrentDirectory()}\\temp\\{Guid.NewGuid()}{random.Next(100000, 999999)}.mp4"; // ffmpeg.exe - i a.mp4 - ac 1 - ar 48000 - vcodec copy a_re.mp4 Run_Command($"-i \"{file}\" -c:v libx264 -c:a aac {tmpOut}"); ooootext.Add($"file '{tmpOut}'"); var dateEnd = DateTime.Now; WriteTxt($"{file}转换已完成,用时{(dateEnd - dateStart).TotalSeconds}秒"); })); }); log.Info(ooootext); await Task.WhenAll(ts).ContinueWith(t => { string tmpConcatFile = $"{Directory.GetCurrentDirectory()}\\temp\\{Guid.NewGuid()}{random.Next(100000, 999999)}"; // 也可以指定编码方式 //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} -c copy {StrOutMp4Path}"; //string StrArg = $"-i {combineFile} -filter_complex \"{vao} concat=n={i}:v=1:a=1 [v][a]\" -map \"[v]\" -map \"[a]\" {StrOutMp4Path}"; Run_Command(StrArg); if (File.Exists(tmpConcatFile)) { try { File.Delete(tmpConcatFile); } catch { } } }); } public void Run_Command(string command) { Process p = new();//建立外部调用线程 p.StartInfo.FileName = Directory.GetCurrentDirectory() + "\\ffmpeg.exe";//要调用外部程序的绝对路径 p.StartInfo.Arguments = command; 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)) { 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 = []; Random rd = new(); //ls = [.. ls.OrderBy(x => rd.Next())]; for (int i = 0; i < GenerateVideoCount; i++) { int index = rd.Next(ls.Count); List lsVideos = ls[index]; var iIndex = i; tasks.Add(Task.Run(() => { DateTime now = DateTime.Now; Random random = new(); string randomString = random.Next(100000, 999999).ToString(); string result = now.ToString("yyyyMMddHHmmss") + randomString; Task combineTask = CombineMp4WithoutTxt(lsVideos, $"{outPut}\\{result}.mp4"); combineTask.Wait(); var dateEnd = DateTime.Now; WriteTxt($"视频{iIndex}拼接完成,用时{(dateEnd - now).TotalSeconds}秒"); })); } await Task.WhenAll(tasks); } 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; } }; }