using FFMpegCore; using FFMpegCore.Enums; using Standard; using System; using System.IO; using System.Threading.Tasks; using VideoConcat.Common.Tools; using static System.Windows.Forms.DataFormats; namespace VideoConcat.Services.Video { public class VideoProcess { /// /// 同步删除视频指定帧和对应音频片段 /// /// 输入文件路径 /// 输出文件路径 /// 要删除的帧编号(从1开始) /// 操作是否成功 public static async Task RemoveFrameRandomeAsync(string inputPath, string outputPath) { if (!File.Exists(inputPath)) return false; // 创建临时目录 string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); try { // 1. 获取视频信息 IMediaAnalysis mediaInfo = await FFProbe.AnalyseAsync(inputPath); var videoStream = mediaInfo.PrimaryVideoStream; if (videoStream == null) { Console.WriteLine("没有找到视频流"); return false; } bool isHevc = videoStream.CodecName == "hevc"; Directory.CreateDirectory(tempDir); if (isHevc) { try { // 临时文件路径 string videoConvert = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4"); await FFMpegArguments.FromFileInput(inputPath) .OutputToFile(videoConvert, true, opt => // 设置输出格式 opt.WithVideoCodec("libx264") ).ProcessAsynchronously(); mediaInfo = await FFProbe.AnalyseAsync(videoConvert); inputPath = videoConvert; } catch (Exception) { throw new Exception("转换失败!"); } } // 1. 获取视频信息 mediaInfo = await FFProbe.AnalyseAsync(inputPath); videoStream = mediaInfo.PrimaryVideoStream; if (videoStream == null) { Console.WriteLine("没有找到视频流"); return false; } // 视频总时长(秒) double totalDuration = mediaInfo.Duration.TotalSeconds; double frameRate = videoStream.FrameRate; double frameDuration = Math.Round(1.0 / frameRate, 6); // 一帧时长(秒) int totalFram = (int)(totalDuration * frameRate); var random = new Random(); 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 hasSubVideo2 = SubVideo(inputPath, videoPart2, randomFrame, totalDuration); if (!hasSubVideo1 || !hasSubVideo2) { return false; } bool isJoinSuccess = JoinVideo(outputPath, [videoPart1, videoPart2]); if (!isJoinSuccess) { return false; } return false; } catch (Exception ex) { LogUtils.Error("抽帧失败", ex); Console.WriteLine($"操作失败: {ex.Message}"); return false; } finally { string[] files = Directory.GetFiles(tempDir); foreach (string file in files) { File.Delete(file); } File.Delete(tempDir); } } /// /// 通过修改视频元数据(添加注释)改变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 .FromFileInput(inputPath, true, options => options.Seek(TimeSpan.FromSeconds(startSec)).EndSeek(TimeSpan.FromSeconds(endSec))) .OutputToFile(outputPath, true, options => options.CopyChannel()) //.WithCustomArgument("-an") 去掉音频 .ProcessSynchronously(); } public static bool SubAudio(string inputPath, string outputPath, Double startSec, Double endSec) { return FFMpegArguments .FromFileInput(inputPath, true, options => options.Seek(TimeSpan.FromSeconds(startSec)).EndSeek(TimeSpan.FromSeconds(endSec))) .OutputToFile(outputPath, true, options => options.CopyChannel()) .ProcessSynchronously(); } public static bool JoinVideo(string outPutPath, string[] videoParts) { return FFMpeg.Join(outPutPath, videoParts); } public async Task ProcessVideo(string inputPath, string outputPath, string tempDir) { // 1. 获取视频信息 IMediaAnalysis mediaInfo = await FFProbe.AnalyseAsync(inputPath); var videoStream = mediaInfo.PrimaryVideoStream; if (videoStream == null) { Console.WriteLine("没有找到视频流"); return false; } // 视频总时长(秒) var totalDuration = mediaInfo.Duration.TotalSeconds; // 计算帧时间参数 double frameRate = videoStream.FrameRate; double frameDuration = Math.Round(1.0 / frameRate, 6); // 一帧时长(秒) int totalFram = (int)(totalDuration * frameRate); // 2. 随机生成要删除的帧的时间点(避开最后一帧,防止越界) var random = new Random(); var randomFrame = random.Next(20, totalFram - 10); double frameTime = Math.Round((randomFrame - 1) * frameDuration, 6); // 目标帧开始时间 double nextFrameTime = Math.Round((randomFrame + 1) * frameDuration, 6); // 下一帧开始时间 // 临时文件路径 string videoPart1 = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4"); string videoPart2 = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4"); string audioPath = Path.Combine(tempDir, $"{Guid.NewGuid()}.aac"); string videoNoaudioPath = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4"); string finalyPath = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4"); string audioPart1 = Path.Combine(tempDir, $"{Guid.NewGuid()}.aac"); string audioPart2 = Path.Combine(tempDir, $"{Guid.NewGuid()}.aac"); // 分离视频流(不含音频) await FFMpegArguments .FromFileInput(inputPath) .OutputToFile(videoNoaudioPath, true, opt => opt.WithCustomArgument("-an -c:v copy")) .ProcessAsynchronously(); if (!File.Exists(videoNoaudioPath)) return false; // 分离音频流(不含视频)(如有音频) if (mediaInfo.PrimaryAudioStream != null) { await FFMpegArguments .FromFileInput(inputPath) .OutputToFile(audioPath, true, opt => opt.WithCustomArgument("-vn -c:a copy")) .ProcessAsynchronously(); if (!File.Exists(audioPath)) return false; } inputPath = videoNoaudioPath; bool hasSubVideo1 = SubVideo(inputPath, videoPart1, 0, frameTime); bool hasSubVideo2 = SubVideo(inputPath, videoPart2, nextFrameTime, totalDuration); if (!hasSubVideo1 || !hasSubVideo2) { return false; } bool isJoinSuccess = JoinVideo(finalyPath, [videoPart1, videoPart2]); if (!isJoinSuccess) { return false; } return await FFMpegArguments.FromFileInput(finalyPath) .AddFileInput(audioPath) .OutputToFile(outputPath, true, opt => 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(); } } }