Compare commits
No commits in common. "main" and "feat-v1" have entirely different histories.
@ -26,8 +26,7 @@ namespace VideoConcat.Models
|
|||||||
|
|
||||||
private string _folderPath = "";
|
private string _folderPath = "";
|
||||||
private string _helpInfo = "";
|
private string _helpInfo = "";
|
||||||
private bool _canExtractFrame = false;
|
private bool _canStart = false;
|
||||||
private bool _canModify = false;
|
|
||||||
private bool _isCanOperate = false;
|
private bool _isCanOperate = false;
|
||||||
private bool _isStart = false;
|
private bool _isStart = false;
|
||||||
private string[] _videos = [];
|
private string[] _videos = [];
|
||||||
@ -103,28 +102,18 @@ namespace VideoConcat.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanExtractFrame
|
public bool CanStart
|
||||||
{
|
{
|
||||||
get { return _canExtractFrame; }
|
get { return _canStart; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_canExtractFrame = value;
|
_canStart = value;
|
||||||
OnPropertyChanged(nameof(CanExtractFrame));
|
OnPropertyChanged(nameof(CanStart));
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool CanModify
|
|
||||||
{
|
|
||||||
get { return _canModify; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_canModify = value;
|
|
||||||
OnPropertyChanged(nameof(CanModify));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommand? BtnOpenFolderCommand { get; set; }
|
public ICommand? BtnOpenFolderCommand { get; set; }
|
||||||
public ICommand? BtnStartVideoConcatCommand { get; set; }
|
public ICommand? BtnStartVideoConcatCommand { get; set; }
|
||||||
public ICommand? BtnStartVideoModifyCommand { get; set; }
|
|
||||||
public ICommand? BtnChooseAuditImageCommand { get; set; }
|
public ICommand? BtnChooseAuditImageCommand { get; set; }
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
@ -143,14 +132,13 @@ namespace VideoConcat.Models
|
|||||||
|
|
||||||
public void SetCanStart()
|
public void SetCanStart()
|
||||||
{
|
{
|
||||||
CanExtractFrame = false;
|
|
||||||
if (videos.Length > 0)
|
if (videos.Length > 0)
|
||||||
{
|
{
|
||||||
CanModify = true;
|
CanStart = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CanModify = false;
|
CanStart = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using FFMpegCore;
|
using FFMpegCore;
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
using Standard;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -22,12 +21,11 @@ namespace VideoConcat.Services.Video
|
|||||||
{
|
{
|
||||||
if (!File.Exists(inputPath))
|
if (!File.Exists(inputPath))
|
||||||
return false;
|
return false;
|
||||||
// 创建临时目录
|
|
||||||
string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 1. 获取视频信息
|
// 1. 获取视频信息
|
||||||
IMediaAnalysis mediaInfo = await FFProbe.AnalyseAsync(inputPath);
|
var mediaInfo = await FFProbe.AnalyseAsync(inputPath);
|
||||||
var videoStream = mediaInfo.PrimaryVideoStream;
|
var videoStream = mediaInfo.PrimaryVideoStream;
|
||||||
if (videoStream == null)
|
if (videoStream == null)
|
||||||
{
|
{
|
||||||
@ -37,6 +35,8 @@ namespace VideoConcat.Services.Video
|
|||||||
|
|
||||||
bool isHevc = videoStream.CodecName == "hevc";
|
bool isHevc = videoStream.CodecName == "hevc";
|
||||||
|
|
||||||
|
// 2. 创建临时目录
|
||||||
|
string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||||
Directory.CreateDirectory(tempDir);
|
Directory.CreateDirectory(tempDir);
|
||||||
|
|
||||||
if (isHevc)
|
if (isHevc)
|
||||||
@ -45,59 +45,47 @@ namespace VideoConcat.Services.Video
|
|||||||
{
|
{
|
||||||
|
|
||||||
// 临时文件路径
|
// 临时文件路径
|
||||||
string videoConvert = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4");
|
string videoConvert = Path.Combine(tempDir, "convert.mp4");
|
||||||
|
|
||||||
await FFMpegArguments.FromFileInput(inputPath)
|
await FFMpegArguments.FromFileInput(inputPath)
|
||||||
.OutputToFile(videoConvert, true, opt => // 设置输出格式
|
.OutputToFile(videoConvert, true, opt => // 设置输出格式
|
||||||
opt.WithVideoCodec("libx264")
|
opt.WithVideoCodec("libx264")
|
||||||
).ProcessAsynchronously();
|
).ProcessAsynchronously();
|
||||||
mediaInfo = await FFProbe.AnalyseAsync(videoConvert);
|
mediaInfo = await FFProbe.AnalyseAsync(videoConvert);
|
||||||
|
|
||||||
inputPath = videoConvert;
|
inputPath = videoConvert;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
throw new Exception("转换失败!");
|
File.Delete(outputPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 1. 获取视频信息
|
|
||||||
mediaInfo = await FFProbe.AnalyseAsync(inputPath);
|
|
||||||
videoStream = mediaInfo.PrimaryVideoStream;
|
|
||||||
if (videoStream == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("没有找到视频流");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 视频总时长(秒)
|
// 视频总时长(秒)
|
||||||
double totalDuration = mediaInfo.Duration.TotalSeconds;
|
var totalDuration = mediaInfo.Duration.TotalSeconds;
|
||||||
|
// 计算帧时间参数
|
||||||
double frameRate = videoStream.FrameRate;
|
double frameRate = videoStream.FrameRate;
|
||||||
double frameDuration = Math.Round(1.0 / frameRate, 6); // 一帧时长(秒)
|
double frameDuration = 1.0 / frameRate; // 一帧时长(秒)
|
||||||
|
|
||||||
int totalFram = (int)(totalDuration * frameRate);
|
int totalFram = (int)(totalDuration * frameRate);
|
||||||
|
// 2. 随机生成要删除的帧的时间点(避开最后一帧,防止越界)
|
||||||
var random = new Random();
|
var random = new Random();
|
||||||
var randomFrame = random.Next(20, (int)totalDuration);
|
var randomFrame = random.Next(20, totalFram - 10);
|
||||||
|
|
||||||
//return RemoveVideoFrame(inputPath, outputPath, randomFrame);
|
double frameTime = (randomFrame - 1) * frameDuration; // 目标帧开始时间
|
||||||
|
double nextFrameTime = frameTime + frameDuration; // 下一帧开始时间
|
||||||
|
|
||||||
|
|
||||||
string videoPart1 = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4");
|
// 临时文件路径
|
||||||
string videoPart2 = Path.Combine(tempDir, $"{Guid.NewGuid()}.mp4");
|
string videoPart1 = Path.Combine(tempDir, "video_part1.mp4");
|
||||||
bool hasSubVideo1 = SubVideo(inputPath, videoPart1, 0, randomFrame - 0.016);
|
string videoPart2 = Path.Combine(tempDir, "video_part2.mp4");
|
||||||
bool hasSubVideo2 = SubVideo(inputPath, videoPart2, randomFrame, totalDuration);
|
bool hasSubVideo1 = SubVideo(inputPath, videoPart1, 0, frameTime);
|
||||||
if (!hasSubVideo1 || !hasSubVideo2)
|
bool hasSubVideo2 = SubVideo(inputPath, videoPart2, nextFrameTime, totalDuration);
|
||||||
|
if (hasSubVideo1 && hasSubVideo2)
|
||||||
{
|
{
|
||||||
return false;
|
return JoinVideo(outputPath, [videoPart1, videoPart2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool isJoinSuccess = JoinVideo(outputPath, [videoPart1, videoPart2]);
|
|
||||||
if (!isJoinSuccess)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -105,53 +93,11 @@ namespace VideoConcat.Services.Video
|
|||||||
Console.WriteLine($"操作失败: {ex.Message}");
|
Console.WriteLine($"操作失败: {ex.Message}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
string[] files = Directory.GetFiles(tempDir);
|
|
||||||
foreach (string file in files)
|
|
||||||
{
|
|
||||||
File.Delete(file);
|
|
||||||
}
|
|
||||||
File.Delete(tempDir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 通过修改视频元数据(添加注释)改变MD5
|
|
||||||
/// </summary>
|
|
||||||
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)
|
public static bool SubVideo(string inputPath, string outputPath, Double startSec, Double endSec)
|
||||||
{
|
{
|
||||||
return FFMpegArguments
|
return FFMpeg.SubVideo(inputPath, outputPath, TimeSpan.FromSeconds(startSec), TimeSpan.FromSeconds(endSec));
|
||||||
.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)
|
public static bool JoinVideo(string outPutPath, string[] videoParts)
|
||||||
@ -159,105 +105,5 @@ namespace VideoConcat.Services.Video
|
|||||||
return FFMpeg.Join(outPutPath, videoParts);
|
return FFMpeg.Join(outPutPath, videoParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
using FFMpegCore;
|
using System.Windows.Input;
|
||||||
using FFMpegCore.Enums;
|
|
||||||
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.Models;
|
||||||
using VideoConcat.Services.Video;
|
|
||||||
using static VideoConcat.Models.VideoModel;
|
|
||||||
using MessageBox = System.Windows.MessageBox;
|
using MessageBox = System.Windows.MessageBox;
|
||||||
|
using VideoConcat.Common.Tools;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.Expression.Drawing.Core;
|
||||||
|
using FFMpegCore;
|
||||||
|
using FFMpegCore.Enums;
|
||||||
|
using static VideoConcat.Models.VideoModel;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using System.Windows;
|
||||||
|
using VideoConcat.Services.Video;
|
||||||
|
|
||||||
namespace VideoConcat.ViewModels
|
namespace VideoConcat.ViewModels
|
||||||
{
|
{
|
||||||
@ -35,8 +34,7 @@ namespace VideoConcat.ViewModels
|
|||||||
{
|
{
|
||||||
ExtractWindowModel = new ExtractWindowModel
|
ExtractWindowModel = new ExtractWindowModel
|
||||||
{
|
{
|
||||||
CanExtractFrame = false,
|
CanStart = false,
|
||||||
CanModify = false,
|
|
||||||
IsStart = false,
|
IsStart = false,
|
||||||
IsCanOperate = true,
|
IsCanOperate = true,
|
||||||
|
|
||||||
@ -60,92 +58,20 @@ namespace VideoConcat.ViewModels
|
|||||||
DoExcue = obj =>
|
DoExcue = obj =>
|
||||||
{
|
{
|
||||||
ExtractWindowModel.HelpInfo = "";
|
ExtractWindowModel.HelpInfo = "";
|
||||||
|
Task.Run(action: () =>
|
||||||
|
|
||||||
SemaphoreSlim semaphore = new(10); // Limit to 3 threads
|
|
||||||
|
|
||||||
List<Task> _tasks = [];
|
|
||||||
|
|
||||||
ExtractWindowModel.videos.ForEach(async (video) =>
|
|
||||||
{
|
{
|
||||||
await semaphore.WaitAsync(); // Wait when more than 3 threads are running
|
ExtractWindowModel.videos.ForEach(async (video) =>
|
||||||
var _task = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
try
|
// 实例化并调用
|
||||||
{
|
var remover = new VideoProcess();
|
||||||
// 实例化并调用
|
// 删除4秒处的帧(需根据实际帧位置调整)
|
||||||
var remover = new VideoProcess();
|
string _tmpPath = Path.GetDirectoryName(video) ?? "";
|
||||||
// 删除4秒处的帧(需根据实际帧位置调整)
|
string _tmpFileName = $"抽帧视频-{Path.GetFileName(video)}";
|
||||||
string _tmpPath = Path.GetDirectoryName(video) ?? "";
|
await VideoProcess.RemoveFrameRandomeAsync(video, $"{_tmpPath}\\{_tmpFileName}");
|
||||||
string _tmpFileName = $"{(new Random()).Next(10000, 99999)}{Path.GetFileName(video)}";
|
|
||||||
|
|
||||||
string outPath = Path.Combine(_tmpPath, "out");
|
|
||||||
if (!Path.Exists(outPath))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(outPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
await VideoProcess.RemoveFrameRandomeAsync(video, $"{_tmpPath}\\out\\{_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 = "全部完成!";
|
||||||
{
|
|
||||||
ExtractWindowModel.HelpInfo = "全部完成!";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
BtnStartVideoModifyCommand = new Command()
|
|
||||||
{
|
|
||||||
DoExcue = obj =>
|
|
||||||
{
|
|
||||||
ExtractWindowModel.HelpInfo = "";
|
|
||||||
|
|
||||||
|
|
||||||
SemaphoreSlim semaphore = new(10); // Limit to 3 threads
|
|
||||||
|
|
||||||
List<Task> _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 = "全部完成!";
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,10 +29,9 @@
|
|||||||
<Border>
|
<Border>
|
||||||
<WrapPanel>
|
<WrapPanel>
|
||||||
<Label VerticalAlignment="Center" Width="100" FontSize="16" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" Style="{StaticResource MaterialDesignLabel}" VerticalContentAlignment="Stretch">视频文件夹:</Label>
|
<Label VerticalAlignment="Center" Width="100" FontSize="16" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" Style="{StaticResource MaterialDesignLabel}" VerticalContentAlignment="Stretch">视频文件夹:</Label>
|
||||||
<TextBox Grid.Column="1" Width="600" Name="FoldPath" Text="{Binding ExtractWindowModel.FolderPath,Mode=TwoWay}" IsReadOnly="True" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" materialDesign:HintAssist.Hint="选择包含需要处理的视频文件夹"/>
|
<TextBox Grid.Column="1" Width="650" Name="FoldPath" Text="{Binding ExtractWindowModel.FolderPath,Mode=TwoWay}" IsReadOnly="True" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" materialDesign:HintAssist.Hint="选择包含需要处理的视频文件夹"/>
|
||||||
<Button Grid.Column="2" Width="80" Content="选择" FontSize="16" Command="{Binding ExtractWindowModel.BtnOpenFolderCommand}" Background="#40568D" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="选择含有视频的主文件夹" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2" IsEnabled="{Binding ExtractWindowModel.IsCanOperate}"/>
|
<Button Grid.Column="2" Width="100" Content="选择" FontSize="16" Command="{Binding ExtractWindowModel.BtnOpenFolderCommand}" Background="#40568D" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="选择含有视频的主文件夹" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2" IsEnabled="{Binding ExtractWindowModel.IsCanOperate}"/>
|
||||||
<Button Grid.Column="3" Width="80" Content="抽帧" FontSize="16" Command="{Binding ExtractWindowModel.BtnStartVideoConcatCommand}" Background="#3B94FE" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="开始处理视频" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2,0,2" IsEnabled="{Binding ExtractWindowModel.CanExtractFrame}"/>
|
<Button Grid.Column="3" Width="100" Content="处理" FontSize="16" Command="{Binding ExtractWindowModel.BtnStartVideoConcatCommand}" Background="#3B94FE" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="开始处理视频" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2,0,2" IsEnabled="{Binding ExtractWindowModel.CanStart}"/>
|
||||||
<Button Grid.Column="4" Width="80" Content="修改" FontSize="16" Command="{Binding ExtractWindowModel.BtnStartVideoConcatCommand}" Background="#3B94FE" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="开始处理视频" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2,0,2" IsEnabled="{Binding ExtractWindowModel.CanModify}"/>
|
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Width="1100" Height="800" WindowStartupLocation="CenterScreen"
|
Width="1100" Height="800" WindowStartupLocation="CenterScreen"
|
||||||
ResizeMode="CanMinimize"
|
ResizeMode="NoResize"
|
||||||
Title="工具">
|
Title="工具">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<Style x:Key="BottomButton" TargetType="Button">
|
<Style x:Key="BottomButton" TargetType="Button">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user