update
This commit is contained in:
parent
dd2827dc71
commit
e2a75a64bd
@ -1,5 +1,6 @@
|
||||
using FFMpegCore;
|
||||
using FFMpegCore.Enums;
|
||||
using Standard;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
@ -21,7 +22,8 @@ namespace VideoConcat.Services.Video
|
||||
{
|
||||
if (!File.Exists(inputPath))
|
||||
return false;
|
||||
|
||||
// 创建临时目录
|
||||
string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
try
|
||||
{
|
||||
// 1. 获取视频信息
|
||||
@ -35,8 +37,6 @@ namespace VideoConcat.Services.Video
|
||||
|
||||
bool isHevc = videoStream.CodecName == "hevc";
|
||||
|
||||
// 2. 创建临时目录
|
||||
string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempDir);
|
||||
|
||||
if (isHevc)
|
||||
@ -45,19 +45,19 @@ namespace VideoConcat.Services.Video
|
||||
{
|
||||
|
||||
// 临时文件路径
|
||||
string videoConvert = Path.Combine(tempDir, "convert.mp4");
|
||||
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);
|
||||
mediaInfo = await FFProbe.AnalyseAsync(videoConvert);
|
||||
|
||||
inputPath = videoConvert;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
File.Delete(outputPath);
|
||||
throw new Exception("转换失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,27 +65,72 @@ namespace VideoConcat.Services.Video
|
||||
var totalDuration = mediaInfo.Duration.TotalSeconds;
|
||||
// 计算帧时间参数
|
||||
double frameRate = videoStream.FrameRate;
|
||||
double frameDuration = 1.0 / 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 = (randomFrame - 1) * frameDuration; // 目标帧开始时间
|
||||
double nextFrameTime = frameTime + frameDuration; // 下一帧开始时间
|
||||
double frameTime = Math.Round((randomFrame - 1) * frameDuration,6); // 目标帧开始时间
|
||||
double nextFrameTime = Math.Round(randomFrame * 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;
|
||||
|
||||
|
||||
|
||||
|
||||
// 临时文件路径
|
||||
string videoPart1 = Path.Combine(tempDir, "video_part1.mp4");
|
||||
string videoPart2 = Path.Combine(tempDir, "video_part2.mp4");
|
||||
bool hasSubVideo1 = SubVideo(inputPath, videoPart1, 0, frameTime);
|
||||
bool hasSubVideo2 = SubVideo(inputPath, videoPart2, nextFrameTime, totalDuration);
|
||||
if (hasSubVideo1 && hasSubVideo2)
|
||||
if (!hasSubVideo1 || !hasSubVideo2)
|
||||
{
|
||||
return JoinVideo(outputPath, [videoPart1, videoPart2]);
|
||||
return false;
|
||||
}
|
||||
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();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -93,17 +138,37 @@ namespace VideoConcat.Services.Video
|
||||
Console.WriteLine($"操作失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
string[] files = Directory.GetFiles(tempDir);
|
||||
foreach (string file in files)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
File.Delete(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SubVideo(string inputPath, string outputPath, Double startSec, Double endSec)
|
||||
{
|
||||
return FFMpeg.SubVideo(inputPath, outputPath, TimeSpan.FromSeconds(startSec), TimeSpan.FromSeconds(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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
using System.Windows.Input;
|
||||
using VideoConcat.Models;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using VideoConcat.Common.Tools;
|
||||
using System.IO;
|
||||
using Microsoft.Expression.Drawing.Core;
|
||||
using FFMpegCore;
|
||||
using FFMpegCore;
|
||||
using FFMpegCore.Enums;
|
||||
using static VideoConcat.Models.VideoModel;
|
||||
using System.Windows.Threading;
|
||||
using Microsoft.Expression.Drawing.Core;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using VideoConcat.Common.Tools;
|
||||
using VideoConcat.Models;
|
||||
using VideoConcat.Services.Video;
|
||||
using static VideoConcat.Models.VideoModel;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
|
||||
namespace VideoConcat.ViewModels
|
||||
{
|
||||
@ -58,20 +59,38 @@ namespace VideoConcat.ViewModels
|
||||
DoExcue = obj =>
|
||||
{
|
||||
ExtractWindowModel.HelpInfo = "";
|
||||
Task.Run(action: () =>
|
||||
|
||||
|
||||
SemaphoreSlim semaphore = new(10); // Limit to 3 threads
|
||||
|
||||
List<Task> _tasks = [];
|
||||
|
||||
ExtractWindowModel.videos.ForEach(async (video) =>
|
||||
{
|
||||
ExtractWindowModel.videos.ForEach(async (video) =>
|
||||
await semaphore.WaitAsync(); // Wait when more than 3 threads are running
|
||||
var _task = Task.Run(async () =>
|
||||
{
|
||||
// 实例化并调用
|
||||
var remover = new VideoProcess();
|
||||
// 删除4秒处的帧(需根据实际帧位置调整)
|
||||
string _tmpPath = Path.GetDirectoryName(video) ?? "";
|
||||
string _tmpFileName = $"抽帧视频-{Path.GetFileName(video)}";
|
||||
await VideoProcess.RemoveFrameRandomeAsync(video, $"{_tmpPath}\\{_tmpFileName}");
|
||||
try
|
||||
{
|
||||
// 实例化并调用
|
||||
var remover = new VideoProcess();
|
||||
// 删除4秒处的帧(需根据实际帧位置调整)
|
||||
string _tmpPath = Path.GetDirectoryName(video) ?? "";
|
||||
string _tmpFileName = $"抽帧视频-{Path.GetFileName(video)}";
|
||||
await VideoProcess.RemoveFrameRandomeAsync(video, $"{_tmpPath}\\{_tmpFileName}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release(); // Work is done, signal to semaphore that more work is possible
|
||||
}
|
||||
});
|
||||
_tasks.Add(_task);
|
||||
});
|
||||
|
||||
ExtractWindowModel.HelpInfo = "全部完成!";
|
||||
Task.WhenAll(_tasks).ContinueWith((task) =>
|
||||
{
|
||||
ExtractWindowModel.HelpInfo = "全部完成!";
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user