From 6fa231b010e3207dea6363d5dede84b0343ec6e9 Mon Sep 17 00:00:00 2001 From: xiangbing Date: Sat, 18 Jan 2025 14:02:49 +0800 Subject: [PATCH] update --- Common/Tools/VideoCombine.cs | 44 +++++++++++++++-- ViewModels/VideoViewModel.cs | 93 ++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 20 deletions(-) diff --git a/Common/Tools/VideoCombine.cs b/Common/Tools/VideoCombine.cs index cf80b6e..d9ea783 100644 --- a/Common/Tools/VideoCombine.cs +++ b/Common/Tools/VideoCombine.cs @@ -1,13 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using FFMpegCore.Enums; +using FFMpegCore.Helpers; +using FFMpegCore; +using System.IO; namespace VideoConcat.Common.Tools { internal class VideoCombine { + /// + /// 生成所有组合 + /// public static void GenerateCombinations(List> videoLists, int index, List currentCombination, List> result) { if (index == videoLists.Count) @@ -24,5 +26,37 @@ namespace VideoConcat.Common.Tools currentCombination.RemoveAt(currentCombination.Count - 1); } } + + /// + /// 清理临时文件 + /// + public static void Cleanup(List pathList) + { + foreach (var path in pathList) + { + if (File.Exists(path)) + { + File.Delete(path); + } + } + } + + /// + /// 将视频文件转换为ts格式 + /// + public static string ConvertVideos(string videoPath) + { + + var video = FFProbe.Analyse(videoPath); + FFMpegHelper.ConversionSizeExceptionCheck(video); + + string _tempPath = Path.GetDirectoryName(videoPath) ?? ""; + + //GlobalFFOptions.Current.TemporaryFilesFolder + var destinationPath = Path.Combine(_tempPath, $"{Path.GetFileNameWithoutExtension(videoPath)}{FileExtension.Ts}"); + //Directory.CreateDirectory(GlobalFFOptions.Current.TemporaryFilesFolder); + FFMpeg.Convert(videoPath, destinationPath, VideoType.Ts); + return destinationPath; + } } } diff --git a/ViewModels/VideoViewModel.cs b/ViewModels/VideoViewModel.cs index 50bfde8..14016dc 100644 --- a/ViewModels/VideoViewModel.cs +++ b/ViewModels/VideoViewModel.cs @@ -5,6 +5,7 @@ using VideoConcat.Common.Tools; using System.IO; using Microsoft.Expression.Drawing.Core; using FFMpegCore; +using FFMpegCore.Enums; namespace VideoConcat.ViewModels { @@ -75,21 +76,24 @@ namespace VideoConcat.ViewModels { DoExcue = obj => { - Task.Run(action: () => + Task.Run(action: async () => { MessageBox.Show("开始合并视频"); + if (Directory.Exists($"{VideoModel.FolderPath}\\output") == false) + { + Directory.CreateDirectory($"{VideoModel.FolderPath}\\output"); + } VideoModel.IsStart = true; //开始时间 DateTime startTime = DateTime.Now; - LogUtils.Info("开始合并视频"); + LogUtils.Info("开始合并视频,进行视频拼接组合"); List> combinations = []; List currentCombination = []; List> videoLists = []; - List taskList = []; - + VideoModel.FolderInfos.ForEach(folderInfo => { videoLists.Add(folderInfo.VideoPaths); @@ -105,30 +109,83 @@ namespace VideoConcat.ViewModels // 复制原列表,避免修改原列表 List> tempList = new(combinations); + string[] _converVideoPath = []; + List _clearPath = []; for (int i = 0; i < VideoModel.Num && tempList.Count > 0; i++) { int index = random.Next(tempList.Count); result.Add(tempList[index]); + + _converVideoPath = [.. _converVideoPath, .. tempList[index]]; + tempList.RemoveAt(index); } + SemaphoreSlim semaphore = new(10); // Limit to 3 threads + + List _tasks = []; + + foreach (var _path in _converVideoPath) + { + await semaphore.WaitAsync(); // Wait when more than 3 threads are running + var _task = Task.Run(() => + { + try + { + _clearPath.Add(VideoCombine.ConvertVideos(_path)); + } + finally + { + semaphore.Release(); // Work is done, signal to semaphore that more work is possible + } + }); + _tasks.Add(_task); + } + + await Task.WhenAll(_tasks).ContinueWith((task) => + { + LogUtils.Info($"转换完成,用时{(DateTime.Now - startTime).TotalSeconds}秒"); + }); + + LogUtils.Info("开始拼接视频"); + + + List taskList = []; + semaphore = new(10); foreach (List combination in result) { - taskList.Add(Task.Run(() => + await semaphore.WaitAsync(); + var _task = Task.Run(() => { - try { - if (Directory.Exists($"{VideoModel.FolderPath}\\output") == false) - { - Directory.CreateDirectory($"{VideoModel.FolderPath}\\output"); - } string _outPutName = $"{VideoModel.FolderPath}\\output\\{DateTime.Now:yyyyMMddHHmmss}{random.Next(100000, 999999)}.mp4"; - string _outPutNameImg = $"{VideoModel.FolderPath}\\output\\{DateTime.Now:yyyyMMddHHmmss}{random.Next(100000, 999999)}.mp4"; - bool _isSuccess = FFMpeg.Join(_outPutName, [.. combination]); + var temporaryVideoParts = combination.Select(_ => { + string _tempPath = Path.GetDirectoryName(_) ?? ""; + + //GlobalFFOptions.Current.TemporaryFilesFolder + return Path.Combine(_tempPath, $"{Path.GetFileNameWithoutExtension(_)}{FileExtension.Ts}"); + }).ToArray(); + bool _isSuccess = false; + try + { + _isSuccess= FFMpegArguments + .FromConcatInput(temporaryVideoParts) + .OutputToFile(_outPutName, true, options => options + .CopyChannel() + .WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc)) + .ProcessSynchronously(); + } + catch (Exception ex) + { + _isSuccess = false; + LogUtils.Error("拼接视频失败", ex); + } + + //bool _isSuccess = FFMpeg.Join(_outPutName, [.. combination]); @@ -140,6 +197,7 @@ namespace VideoConcat.ViewModels // 配置 FFmpeg 二进制文件位置(如果 FFmpeg 不在系统路径中) // GlobalFFOptions.Configure(new FFOptions { BinaryFolder = "path/to/ffmpeg/bin" }); + string _outPutNameImg = $"{VideoModel.FolderPath}\\output\\{DateTime.Now:yyyyMMddHHmmss}{random.Next(100000, 999999)}.mp4"; string _customArg = "-filter_complex \"[0:v][1:v] overlay=0:H-h\" "; // 使用 FFMpegArguments 构建命令 @@ -169,19 +227,24 @@ namespace VideoConcat.ViewModels { LogUtils.Error($"视频{string.Join(",", combination)}合并失败", ex); } + finally + { + semaphore.Release(); + } - })); - + }); + taskList.Add(_task); } - Task.WhenAll(taskList).ContinueWith((s) => + await Task.WhenAll(taskList).ContinueWith((s) => { //结束时间 DateTime endTime = DateTime.Now; LogUtils.Info($"所有视频拼接完成,用时{(endTime - startTime).TotalSeconds}秒"); VideoModel.IsStart = false; MessageBox.Show("所有视频拼接完成"); + VideoCombine.Cleanup(_clearPath); }); }); }