From a28d3cac8eb87835a4f49e526e9676613beb08c9 Mon Sep 17 00:00:00 2001 From: xiangbing Date: Wed, 13 Aug 2025 22:43:32 +0800 Subject: [PATCH] update --- Models/ExtractWindowModel.cs | 26 ++++++++++---- Services/Video/VideoProcess.cs | 47 ++++++++++++++++++++++--- ViewModels/ExtractWindowViewModel.cs | 52 ++++++++++++++++++++++++++-- Views/ExtractWindow.xaml | 7 ++-- Views/MainWindow.xaml | 2 +- 5 files changed, 117 insertions(+), 17 deletions(-) diff --git a/Models/ExtractWindowModel.cs b/Models/ExtractWindowModel.cs index 756114f..9d42ec3 100644 --- a/Models/ExtractWindowModel.cs +++ b/Models/ExtractWindowModel.cs @@ -26,7 +26,8 @@ namespace VideoConcat.Models private string _folderPath = ""; private string _helpInfo = ""; - private bool _canStart = false; + private bool _canExtractFrame = false; + private bool _canModify = false; private bool _isCanOperate = false; private bool _isStart = false; private string[] _videos = []; @@ -102,18 +103,28 @@ namespace VideoConcat.Models } } - public bool CanStart + public bool CanExtractFrame { - get { return _canStart; } + get { return _canExtractFrame; } set { - _canStart = value; - OnPropertyChanged(nameof(CanStart)); + _canExtractFrame = value; + OnPropertyChanged(nameof(CanExtractFrame)); + } + } + public bool CanModify + { + get { return _canModify; } + set + { + _canModify = value; + OnPropertyChanged(nameof(CanModify)); } } public ICommand? BtnOpenFolderCommand { get; set; } public ICommand? BtnStartVideoConcatCommand { get; set; } + public ICommand? BtnStartVideoModifyCommand { get; set; } public ICommand? BtnChooseAuditImageCommand { get; set; } public event PropertyChangedEventHandler? PropertyChanged; @@ -132,13 +143,14 @@ namespace VideoConcat.Models public void SetCanStart() { + CanExtractFrame = false; if (videos.Length > 0) { - CanStart = true; + CanModify = true; } else { - CanStart = false; + CanModify = false; } } } diff --git a/Services/Video/VideoProcess.cs b/Services/Video/VideoProcess.cs index 29a1c50..5cf42d8 100644 --- a/Services/Video/VideoProcess.cs +++ b/Services/Video/VideoProcess.cs @@ -75,14 +75,14 @@ namespace VideoConcat.Services.Video int totalFram = (int)(totalDuration * frameRate); var random = new Random(); - var randomFrame = random.Next(1, (int)totalDuration); - + var randomFrame = random.Next(20, (int)totalDuration); + //return RemoveVideoFrame(inputPath, outputPath, randomFrame); string videoPart1 = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4"); string videoPart2 = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4"); - bool hasSubVideo1 = SubVideo(inputPath, videoPart1, 0, randomFrame-0.016); + bool hasSubVideo1 = SubVideo(inputPath, videoPart1, 0, randomFrame - 0.016); bool hasSubVideo2 = SubVideo(inputPath, videoPart2, randomFrame, totalDuration); if (!hasSubVideo1 || !hasSubVideo2) { @@ -116,6 +116,27 @@ namespace VideoConcat.Services.Video } } + + + + /// + /// 通过修改视频元数据(添加注释)改变MD5 + /// + public static bool ModifyByMetadata(string inputPath, string outputPath, string comment = "JSY") + { + // 添加或修改视频元数据中的注释信息 + return FFMpegArguments + .FromFileInput(inputPath) + .OutputToFile(outputPath, true, options => options + //.WithVideoCodec("copy") // 直接复制视频流,不重新编码 + //.WithAudioCodec("copy") // 直接复制音频流 + .CopyChannel() + .WithCustomArgument($"-metadata comment=\"{comment}_{Guid.NewGuid()}\"") // 添加唯一注释 + ) + .ProcessSynchronously(); + } + + public static bool SubVideo(string inputPath, string outputPath, Double startSec, Double endSec) { return FFMpegArguments @@ -138,7 +159,7 @@ namespace VideoConcat.Services.Video return FFMpeg.Join(outPutPath, videoParts); } - public async Task ProcessVideo(string inputPath,string outputPath, string tempDir) + public async Task ProcessVideo(string inputPath, string outputPath, string tempDir) { // 1. 获取视频信息 IMediaAnalysis mediaInfo = await FFProbe.AnalyseAsync(inputPath); @@ -220,5 +241,23 @@ namespace VideoConcat.Services.Video opt.WithCustomArgument("-c copy -shortest -y")) .ProcessAsynchronously(); } + + + public static bool RemoveVideoFrame(string inputPath, string outputPath, int frameToRemove) + { + // 使用FFMpegArguments构建转换流程 + return FFMpegArguments + .FromFileInput(inputPath) + .OutputToFile(outputPath, true, options => options + // 使用select过滤器排除指定帧(帧序号从0开始) + .WithCustomArgument($"select=not(eq(n\\,{frameToRemove}))") + + // $"not(eq(n\\,{frameToRemove}))"); // 注意:在C#中需要转义反斜杠 + //// 保持其他编码参数 + .WithVideoCodec("libx264") + .WithAudioCodec("copy") // 如果不需要处理音频,直接复制 + ) + .ProcessSynchronously(); + } } } diff --git a/ViewModels/ExtractWindowViewModel.cs b/ViewModels/ExtractWindowViewModel.cs index e44fac8..3563e88 100644 --- a/ViewModels/ExtractWindowViewModel.cs +++ b/ViewModels/ExtractWindowViewModel.cs @@ -35,7 +35,8 @@ namespace VideoConcat.ViewModels { ExtractWindowModel = new ExtractWindowModel { - CanStart = false, + CanExtractFrame = false, + CanModify = false, IsStart = false, IsCanOperate = true, @@ -76,7 +77,7 @@ namespace VideoConcat.ViewModels var remover = new VideoProcess(); // 删除4秒处的帧(需根据实际帧位置调整) string _tmpPath = Path.GetDirectoryName(video) ?? ""; - string _tmpFileName = $"{(new Random()).Next(10000,99999)}{Path.GetFileName(video)}"; + string _tmpFileName = $"{(new Random()).Next(10000, 99999)}{Path.GetFileName(video)}"; string outPath = Path.Combine(_tmpPath, "out"); if (!Path.Exists(outPath)) @@ -94,6 +95,53 @@ namespace VideoConcat.ViewModels _tasks.Add(_task); }); + Task.WhenAll(_tasks).ContinueWith((task) => + { + ExtractWindowModel.HelpInfo = "全部完成!"; + }); + } + }, + + BtnStartVideoModifyCommand = new Command() + { + DoExcue = obj => + { + ExtractWindowModel.HelpInfo = ""; + + + SemaphoreSlim semaphore = new(10); // Limit to 3 threads + + List _tasks = []; + + ExtractWindowModel.videos.ForEach(async (video) => + { + await semaphore.WaitAsync(); // Wait when more than 3 threads are running + var _task = Task.Run(async () => + { + try + { + // 实例化并调用 + var remover = new VideoProcess(); + // 删除4秒处的帧(需根据实际帧位置调整) + string _tmpPath = Path.GetDirectoryName(video) ?? ""; + string _tmpFileName = $"{(new Random()).Next(10000, 99999)}{Path.GetFileName(video)}"; + + string outPath = Path.Combine(_tmpPath, "out"); + if (!Path.Exists(outPath)) + { + Directory.CreateDirectory(outPath); + } + + VideoProcess.ModifyByMetadata(video, $"{_tmpPath}\\out\\modify{_tmpFileName}"); + } + finally + { + semaphore.Release(); // Work is done, signal to semaphore that more work is possible + } + }); + _tasks.Add(_task); + }); + Task.WhenAll(_tasks).ContinueWith((task) => { ExtractWindowModel.HelpInfo = "全部完成!"; diff --git a/Views/ExtractWindow.xaml b/Views/ExtractWindow.xaml index 484b64d..0c48535 100644 --- a/Views/ExtractWindow.xaml +++ b/Views/ExtractWindow.xaml @@ -29,9 +29,10 @@ - -