VideoConcat/ViewModels/VideoViewModel.cs
2025-01-19 20:31:43 +08:00

332 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.Enums;
using static VideoConcat.Models.VideoModel;
using System.Windows.Threading;
using System.Windows;
namespace VideoConcat.ViewModels
{
public class VideoViewModel
{
private VideoModel _videoModel;
public List<ConcatVideo> ConcatVideos { get; set; } = [];
public VideoModel VideoModel
{
get { return _videoModel; }
set
{
_videoModel = value;
}
}
public VideoViewModel()
{
VideoModel = new VideoModel
{
FolderInfos = [],
ConcatVideos = [],
MaxNum = 0,
CanStart = false,
IsStart = false,
BtnOpenFolderCommand = new Command()
{
DoExcue = obj =>
{
FolderBrowserDialog folderBrowserDialog = new();
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
VideoModel.FolderPath = folderBrowserDialog.SelectedPath;
LogUtils.Info($"获取视频文件夹,视频路径:{VideoModel.FolderPath}");
ListFolder(VideoModel.FolderPath);
}
}
},
BtnChooseAuditImageCommand = new Command()
{
DoExcue = obj =>
{
// 创建一个 OpenFileDialog 实例
OpenFileDialog openFileDialog = new()
{
// 设置文件对话框的标题
Title = "选择广审图片",
// 设置文件筛选器,只允许选择文本文件和图像文件
Filter = "图片文件[*.png;*.jpg;*.jpeg;*.bmp]|*.png;*.jpg;*.jpeg;*.bmp",
};
// 显示文件对话框并获取结果
DialogResult result = openFileDialog.ShowDialog();
// 检查用户是否点击了打开按钮
if (result == DialogResult.OK)
{
// 获取用户选择的文件路径列表
VideoModel.AuditImagePath = openFileDialog.FileName;
}
}
},
BtnStartVideoConcatCommand = new Command()
{
DoExcue = obj =>
{
Task.Run(action: async () =>
{
VideoModel.Dispatcher.Invoke(() =>
{
VideoModel.ConcatVideos.Clear();
VideoModel.IsStart = true;
});
if (Directory.Exists($"{VideoModel.FolderPath}\\output") == false)
{
Directory.CreateDirectory($"{VideoModel.FolderPath}\\output");
}
//开始时间
DateTime startTime = DateTime.Now;
LogUtils.Info("开始合并视频,进行视频拼接组合");
List<List<string>> combinations = [];
List<string> currentCombination = [];
List<List<string>> videoLists = [];
VideoModel.FolderInfos.ForEach(folderInfo =>
{
videoLists.Add(folderInfo.VideoPaths);
});
VideoCombine.GenerateCombinations(videoLists, 0, currentCombination, combinations);
List<List<string>> result = [];
Random random = new();
// 复制原列表,避免修改原列表
List<List<string>> tempList = new(combinations);
string[] _converVideoPath = [];
List<string> _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<Task> _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<Task> taskList = [];
semaphore = new(10);
foreach (List<string> combination in result)
{
await semaphore.WaitAsync();
var _task = Task.Run(() =>
{
try
{
string _tempFileName = $"{DateTime.Now:yyyyMMddHHmmss}{random.Next(100000, 999999)}.mp4";
string _outPutName = Path.Combine($"{VideoModel.FolderPath}", "output", _tempFileName); ;
var temporaryVideoParts = combination.Select((_videoPath) =>
{
string _tempPath = Path.GetDirectoryName(_videoPath) ?? "";
//GlobalFFOptions.Current.TemporaryFilesFolder
return Path.Combine(_tempPath, $"{Path.GetFileNameWithoutExtension(_videoPath)}{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]);
VideoModel.Dispatcher.Invoke(() =>
{
IMediaAnalysis _mediaInfo = FFProbe.Analyse(_outPutName);
FileInfo fileInfo = new(_outPutName);
VideoModel.ConcatVideos.Add(new ConcatVideo()
{
Index = VideoModel.ConcatVideos.Count + 1,
FileName = _tempFileName,
Size = $"{fileInfo.Length / 1024 / 1024}MB",
Seconds = ((int)_mediaInfo.Duration.TotalSeconds),
Status = _isSuccess ? "拼接成功" : "拼接失败",
Progress = "100%",
});
});
if (_isSuccess && VideoModel.AuditImagePath != "")
{
// 使用 FFMpegCore 执行添加图片到视频的操作
try
{
// 配置 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 构建命令
bool _isCoverSuccess = FFMpegArguments
.FromFileInput(_outPutName)
.AddFileInput(VideoModel.AuditImagePath)
.OutputToFile(
_outPutNameImg,
true,
options => options.WithCustomArgument(_customArg)
)
.ProcessSynchronously();
LogUtils.Info($"图片已成功添加到视频中,输出文件:{_outPutName}");
}
catch (Exception ex)
{
LogUtils.Error($"图片添加到视频中失败", ex);
}
}
LogUtils.Info($"当前视频: {string.Join("", combination)} 合并成功");
}
catch (Exception ex)
{
LogUtils.Error($"视频:{string.Join("", combination)} 合并失败", ex);
}
finally
{
semaphore.Release();
}
});
taskList.Add(_task);
}
await Task.WhenAll(taskList).ContinueWith((s) =>
{
//结束时间
DateTime endTime = DateTime.Now;
LogUtils.Info($"所有视频拼接完成,用时{(endTime - startTime).TotalSeconds}秒");
VideoModel.IsStart = false;
VideoCombine.Cleanup(_clearPath);
MessageBox.Show("所有视频拼接完成");
});
});
}
}
};
}
private void ListFolder(string path)
{
DirectoryInfo dir = new(path);
try
{
DirectoryInfo dirD = dir as DirectoryInfo;
DirectoryInfo[] folders = dirD.GetDirectories();
VideoModel.FolderInfos.Clear();
//获取文件夹下所有视频文件
foreach (DirectoryInfo Folder in folders)
{
if (Folder.Name != "output")
{
string[] files = Directory.GetFiles(Folder.FullName, "*.mp4");
LogUtils.Info($"{Folder.Name}下有{files.Length}个视频文件");
VideoModel.FolderInfos.Add(new VideoModel.FolderInfo { DirectoryInfo = Folder, Num = files.Length, VideoPaths = new List<string>(files) });
}
}
VideoModel.UpdateSum();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
}
}
class Command : ICommand
{
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter) => true;
public void Execute(object? parameter)
{
DoExcue?.Invoke(parameter);
}
public Action<Object?>? DoExcue { get; set; }
}
}