feat: 增加拼接方式
This commit is contained in:
parent
bda7e13042
commit
c6a1692f90
@ -3,6 +3,6 @@
|
|||||||
<appSettings>
|
<appSettings>
|
||||||
<add key="userName" value=""/>
|
<add key="userName" value=""/>
|
||||||
<add key="password" value=""/>
|
<add key="password" value=""/>
|
||||||
<add key="isRemind" value=""/>
|
<add key="isRemember" value=""/>
|
||||||
</appSettings>
|
</appSettings>
|
||||||
</configuration>
|
</configuration>
|
||||||
2
App.xaml
2
App.xaml
@ -2,7 +2,7 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:VideoConcat"
|
xmlns:local="clr-namespace:VideoConcat"
|
||||||
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
|
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers" xmlns:local1="clr-namespace:VideoConcat.Views"
|
||||||
StartupUri="Views/MainWindow.xaml">
|
StartupUri="Views/MainWindow.xaml">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace VideoConcat.Common.Tools
|
|||||||
{
|
{
|
||||||
if (index == videoLists.Count)
|
if (index == videoLists.Count)
|
||||||
{
|
{
|
||||||
result.Add(new List<string>(currentCombination));
|
result.Add([.. currentCombination]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
EnumToBooleanConverter.cs
Normal file
23
EnumToBooleanConverter.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace VideoConcat
|
||||||
|
{
|
||||||
|
public class EnumToBooleanConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return value.Equals(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return value?.Equals(true) == true ? parameter : System.Windows.Data.Binding.DoNothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,18 +4,27 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Data;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Header;
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Header;
|
||||||
|
using static VideoConcat.Models.VideoModel;
|
||||||
|
|
||||||
namespace VideoConcat.Models
|
namespace VideoConcat.Models
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public enum Gender
|
||||||
|
{
|
||||||
|
Male,
|
||||||
|
Female
|
||||||
|
}
|
||||||
public class VideoModel : INotifyPropertyChanged
|
public class VideoModel : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private int _num;
|
private int _num;
|
||||||
@ -23,7 +32,7 @@ namespace VideoConcat.Models
|
|||||||
private string _folderPath = "";
|
private string _folderPath = "";
|
||||||
private string _auditImagePath = "";
|
private string _auditImagePath = "";
|
||||||
private bool _canStart = false;
|
private bool _canStart = false;
|
||||||
private bool _isCanOperate=false;
|
private bool _isCanOperate = false;
|
||||||
private bool _isStart = false;
|
private bool _isStart = false;
|
||||||
private ObservableCollection<FolderInfo> _FolderInfos = [];
|
private ObservableCollection<FolderInfo> _FolderInfos = [];
|
||||||
private ObservableCollection<ConcatVideo> _concatVideos = [];
|
private ObservableCollection<ConcatVideo> _concatVideos = [];
|
||||||
@ -192,14 +201,21 @@ namespace VideoConcat.Models
|
|||||||
int _temp = 1;
|
int _temp = 1;
|
||||||
if (FolderInfos.Count > 0)
|
if (FolderInfos.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (FolderInfo item in FolderInfos)
|
if (IsJoinType1Selected)
|
||||||
{
|
{
|
||||||
if (item.Num > 0)
|
foreach (FolderInfo item in FolderInfos)
|
||||||
{
|
{
|
||||||
_temp *= item.Num;
|
if (item.Num > 0)
|
||||||
|
{
|
||||||
|
_temp *= item.Num;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
MaxNum = _temp;
|
||||||
|
}
|
||||||
|
if (IsJoinType2Selected)
|
||||||
|
{
|
||||||
|
MaxNum = FolderInfos[0].Num;
|
||||||
}
|
}
|
||||||
MaxNum = _temp;
|
|
||||||
SetCanStart();
|
SetCanStart();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -227,5 +243,29 @@ namespace VideoConcat.Models
|
|||||||
ConcatVideos = [];
|
ConcatVideos = [];
|
||||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _isJoinType1Selected;
|
||||||
|
private bool _isJoinType2Selected;
|
||||||
|
|
||||||
|
public bool IsJoinType1Selected
|
||||||
|
{
|
||||||
|
get { return _isJoinType1Selected; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isJoinType1Selected = value;
|
||||||
|
OnPropertyChanged(nameof(IsJoinType1Selected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsJoinType2Selected
|
||||||
|
{
|
||||||
|
get { return _isJoinType2Selected; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isJoinType2Selected = value;
|
||||||
|
OnPropertyChanged(nameof(IsJoinType2Selected));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
12
Services/BaseService.cs
Normal file
12
Services/BaseService.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VideoConcat.Services
|
||||||
|
{
|
||||||
|
abstract class BaseService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
244
Services/Video/VideoService.cs
Normal file
244
Services/Video/VideoService.cs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
using FFMpegCore.Enums;
|
||||||
|
using FFMpegCore.Helpers;
|
||||||
|
using FFMpegCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VideoConcat.Common.Tools;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.IO;
|
||||||
|
using VideoConcat.Models;
|
||||||
|
using static VideoConcat.Models.VideoModel;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace VideoConcat.Services.Video
|
||||||
|
{
|
||||||
|
internal class VideoService : BaseService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将视频文件转换为ts格式
|
||||||
|
/// </summary>
|
||||||
|
public static string ConvertVideos(string videoPath)
|
||||||
|
{
|
||||||
|
|
||||||
|
var video = FFProbe.Analyse(videoPath);
|
||||||
|
|
||||||
|
string mediaInfo = "";
|
||||||
|
|
||||||
|
mediaInfo += $"视频: {videoPath}";
|
||||||
|
mediaInfo += $"时长: {video.Duration}";
|
||||||
|
mediaInfo += $"格式: {video.Format}";
|
||||||
|
// 视频流信息
|
||||||
|
foreach (var videoStream in video.VideoStreams)
|
||||||
|
{
|
||||||
|
mediaInfo += $"视频编码: {videoStream.CodecName}, 分辨率: {videoStream.Width}x{videoStream.Height}";
|
||||||
|
}
|
||||||
|
// 音频流信息
|
||||||
|
foreach (var audioStream in video.AudioStreams)
|
||||||
|
{
|
||||||
|
mediaInfo += $"音频编码: {audioStream.CodecName}, 采样率: {audioStream.SampleRateHz} Hz";
|
||||||
|
}
|
||||||
|
|
||||||
|
LogUtils.Info(mediaInfo);
|
||||||
|
|
||||||
|
|
||||||
|
FFMpegHelper.ConversionSizeExceptionCheck(video);
|
||||||
|
|
||||||
|
string _tempMd5Name = GetLargeFileMD5(videoPath);
|
||||||
|
|
||||||
|
//GlobalFFOptions.Current.TemporaryFilesFolder
|
||||||
|
var destinationPath = Path.Combine(Path.GetTempPath(), $"{_tempMd5Name}{FileExtension.Ts}");
|
||||||
|
|
||||||
|
if (File.Exists(destinationPath))
|
||||||
|
{
|
||||||
|
return destinationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Directory.CreateDirectory(GlobalFFOptions.Current.TemporaryFilesFolder);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FFMpeg.Convert(videoPath, destinationPath, VideoType.Ts);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
LogUtils.Info("视频转换失败!尝试另外一种转换");
|
||||||
|
// 创建FFmpeg参数
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FFMpegArguments
|
||||||
|
.FromFileInput(videoPath)
|
||||||
|
.OutputToFile(destinationPath, true, o => o
|
||||||
|
.WithVideoCodec("libx264") // 设置视频编码器
|
||||||
|
.WithAudioCodec("aac") // 设置音频编码器
|
||||||
|
.WithAudioSamplingRate(44100)
|
||||||
|
.WithAudioBitrate(128000)
|
||||||
|
.WithConstantRateFactor(23) // 设置质量调整参数
|
||||||
|
.WithCustomArgument("-vf fps=30") // 强制指定帧率(例如 30fps)
|
||||||
|
.WithFastStart()
|
||||||
|
//.WithCustomArgument("-movflags +faststart") // 确保 moov atom 正确写入
|
||||||
|
//.CopyChannel()
|
||||||
|
//.WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB)
|
||||||
|
.ForceFormat(VideoType.Ts)
|
||||||
|
)
|
||||||
|
//.NotifyOnOutput(Console.WriteLine) // 打印 FFmpeg 详细日志
|
||||||
|
.ProcessSynchronously(true);
|
||||||
|
//FFMpeg.Convert(destinationPathExecp, destinationPath, VideoType.Ts);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogUtils.Error($"{videoPath} 转换失败", ex);
|
||||||
|
LogUtils.Error($"{videoPath} 转换再次失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return destinationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetLargeFileMD5(string filePath)
|
||||||
|
{
|
||||||
|
using var md5 = MD5.Create();
|
||||||
|
using var stream = File.OpenRead(filePath);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192]; // 8KB 缓冲区
|
||||||
|
int bytesRead;
|
||||||
|
long totalBytesRead = 0;
|
||||||
|
|
||||||
|
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
|
||||||
|
{
|
||||||
|
md5.TransformBlock(buffer, 0, bytesRead, null, 0);
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
// 可在此添加进度显示:Console.WriteLine($"已读取 {totalBytesRead / 1024 / 1024}MB");
|
||||||
|
}
|
||||||
|
|
||||||
|
md5.TransformFinalBlock(buffer, 0, 0);
|
||||||
|
|
||||||
|
return BitConverter.ToString(value: md5.Hash ?? []).Replace("-", "").ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理临时文件
|
||||||
|
/// </summary>
|
||||||
|
public static void Cleanup(List<string> pathList)
|
||||||
|
{
|
||||||
|
foreach (var path in pathList)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void JoinVideos(List<string> combination)
|
||||||
|
{
|
||||||
|
VideoModel videoModel = new();
|
||||||
|
|
||||||
|
if (Directory.Exists($"{videoModel.FolderPath}\\output") == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory($"{videoModel.FolderPath}\\output");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Random random = new();
|
||||||
|
string _tempFileName = $"{DateTime.Now:yyyyMMddHHmmss}{random.Next(100000, 999999)}.mp4";
|
||||||
|
|
||||||
|
string _outPutName = Path.Combine($"{videoModel.FolderPath}", "output", _tempFileName); ;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var temporaryVideoParts = combination.Select((_videoPath) =>
|
||||||
|
{
|
||||||
|
string _tempMd5Name = VideoService.GetLargeFileMD5(_videoPath);
|
||||||
|
//GlobalFFOptions.Current.TemporaryFilesFolder
|
||||||
|
return Path.Combine(Path.GetTempPath(), $"{_tempMd5Name}{FileExtension.Ts}");
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
bool _isSuccess = false;
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isSuccess = FFMpegArguments
|
||||||
|
.FromConcatInput(temporaryVideoParts)
|
||||||
|
.OutputToFile(_outPutName, true, options => options
|
||||||
|
.CopyChannel()
|
||||||
|
.WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc)
|
||||||
|
.WithFastStart()
|
||||||
|
.WithVideoCodec("copy") // 复制视频流
|
||||||
|
.WithAudioCodec("aac") // 重新编码音频
|
||||||
|
.WithAudioSamplingRate(44100) // 强制采样率
|
||||||
|
.WithCustomArgument("-movflags +faststart -analyzeduration 100M -probesize 100M")
|
||||||
|
)
|
||||||
|
.ProcessSynchronously();
|
||||||
|
}
|
||||||
|
catch (Exception ex1)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
LogUtils.Error("拼接视频失败", ex1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//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)// 或显式指定编码器参数
|
||||||
|
.WithCustomArgument("-movflags +faststart") // 确保 moov atom 正确写入
|
||||||
|
)
|
||||||
|
.ProcessSynchronously();
|
||||||
|
|
||||||
|
|
||||||
|
LogUtils.Info($"图片已成功添加到视频中,输出文件:{_outPutName}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogUtils.Error($"图片添加到视频中失败", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LogUtils.Info($"当前视频-[${_outPutName}]: {string.Join(";", combination)} 合并成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,8 +5,6 @@ VisualStudioVersion = 17.11.35327.3
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VideoConcat", "VideoConcat.csproj", "{2FF5691C-3184-4B68-944B-C704E64C4E4E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VideoConcat", "VideoConcat.csproj", "{2FF5691C-3184-4B68-944B-C704E64C4E4E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "视频", "..\视频\视频.vdproj", "{F784558F-CA6F-E806-1DC3-7B0C364779F3}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -17,8 +15,6 @@ Global
|
|||||||
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.Build.0 = Release|Any CPU
|
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{F784558F-CA6F-E806-1DC3-7B0C364779F3}.Debug|Any CPU.ActiveCfg = Debug
|
|
||||||
{F784558F-CA6F-E806-1DC3-7B0C364779F3}.Release|Any CPU.ActiveCfg = Release
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@ -35,6 +35,9 @@ namespace VideoConcat.ViewModels
|
|||||||
{
|
{
|
||||||
FolderInfos = [],
|
FolderInfos = [],
|
||||||
ConcatVideos = [],
|
ConcatVideos = [],
|
||||||
|
IsJoinType1Selected = true,
|
||||||
|
IsJoinType2Selected = false,
|
||||||
|
|
||||||
MaxNum = 0,
|
MaxNum = 0,
|
||||||
CanStart = false,
|
CanStart = false,
|
||||||
IsStart = false,
|
IsStart = false,
|
||||||
@ -106,33 +109,52 @@ namespace VideoConcat.ViewModels
|
|||||||
List<List<string>> videoLists = [];
|
List<List<string>> videoLists = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
VideoModel.FolderInfos.ForEach(folderInfo =>
|
VideoModel.FolderInfos.ForEach(folderInfo =>
|
||||||
{
|
{
|
||||||
videoLists.Add(folderInfo.VideoPaths);
|
videoLists.Add(folderInfo.VideoPaths);
|
||||||
});
|
});
|
||||||
|
string[] _converVideoPath = [];
|
||||||
VideoCombine.GenerateCombinations(videoLists, 0, currentCombination, combinations);
|
|
||||||
|
|
||||||
|
|
||||||
List<List<string>> result = [];
|
List<List<string>> result = [];
|
||||||
Random random = new();
|
Random random = new();
|
||||||
|
if (VideoModel.IsJoinType1Selected)
|
||||||
|
|
||||||
// 复制原列表,避免修改原列表
|
|
||||||
List<List<string>> tempList = [.. combinations];
|
|
||||||
|
|
||||||
string[] _converVideoPath = [];
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < VideoModel.Num && tempList.Count > 0; i++)
|
|
||||||
{
|
{
|
||||||
int index = random.Next(tempList.Count);
|
VideoCombine.GenerateCombinations(videoLists, 0, currentCombination, combinations);
|
||||||
result.Add(tempList[index]);
|
|
||||||
|
|
||||||
_converVideoPath = [.. _converVideoPath, .. tempList[index]];
|
// 复制原列表,避免修改原列表
|
||||||
|
List<List<string>> tempList = [.. combinations];
|
||||||
|
for (int i = 0; i < VideoModel.Num && tempList.Count > 0; i++)
|
||||||
|
{
|
||||||
|
int index = random.Next(tempList.Count);
|
||||||
|
result.Add(tempList[index]);
|
||||||
|
|
||||||
tempList.RemoveAt(index);
|
_converVideoPath = [.. _converVideoPath, .. tempList[index]];
|
||||||
|
|
||||||
|
tempList.RemoveAt(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VideoModel.IsJoinType2Selected)
|
||||||
|
{
|
||||||
|
int count = videoLists[0].Count;
|
||||||
|
for (int index = 1; index < count; index++)
|
||||||
|
{
|
||||||
|
if (videoLists[0].Count != count)
|
||||||
|
{
|
||||||
|
WPFDevelopers.Controls.MessageBox.Show("请输入用户名或者密码!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int index = 0; index < count; index++)
|
||||||
|
{
|
||||||
|
List<string> list2 = [];
|
||||||
|
foreach (List<string> list in videoLists)
|
||||||
|
{
|
||||||
|
_converVideoPath=[.. _converVideoPath, list[index]];
|
||||||
|
list2.Add(list[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(list2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SemaphoreSlim semaphore = new(10); // Limit to 3 threads
|
SemaphoreSlim semaphore = new(10); // Limit to 3 threads
|
||||||
@ -301,8 +323,8 @@ namespace VideoConcat.ViewModels
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
DirectoryInfo dirD = dir as DirectoryInfo;
|
DirectoryInfo dirD = dir as DirectoryInfo;
|
||||||
DirectoryInfo[] folders = [.. dirD.GetDirectories().OrderBy(d=>d.Name)];
|
DirectoryInfo[] folders = [.. dirD.GetDirectories().OrderBy(d => d.Name)];
|
||||||
|
|
||||||
VideoModel.FolderInfos.Clear();
|
VideoModel.FolderInfos.Clear();
|
||||||
//获取文件夹下所有视频文件
|
//获取文件夹下所有视频文件
|
||||||
foreach (DirectoryInfo Folder in folders)
|
foreach (DirectoryInfo Folder in folders)
|
||||||
|
|||||||
@ -32,6 +32,11 @@
|
|||||||
<TextBox Grid.Row="1" Width="500" Grid.Column="1" Text="{Binding VideoModel.Num,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" ToolTip="请输入合成视频个数" materialDesign:HintAssist.Hint="请输入拼接视频数目" IsEnabled="{Binding VideoModel.IsCanOperate}" />
|
<TextBox Grid.Row="1" Width="500" Grid.Column="1" Text="{Binding VideoModel.Num,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" ToolTip="请输入合成视频个数" materialDesign:HintAssist.Hint="请输入拼接视频数目" IsEnabled="{Binding VideoModel.IsCanOperate}" />
|
||||||
<Button Grid.Row="1" Width="100" Grid.Column="2" Content="开始拼接" FontSize="16" Command="{Binding VideoModel.BtnStartVideoConcatCommand}" Background="#E54858" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="开始拼接视频" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2" IsEnabled="{Binding VideoModel.CanStart}" Cursor="Hand"/>
|
<Button Grid.Row="1" Width="100" Grid.Column="2" Content="开始拼接" FontSize="16" Command="{Binding VideoModel.BtnStartVideoConcatCommand}" Background="#E54858" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="开始拼接视频" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2" IsEnabled="{Binding VideoModel.CanStart}" Cursor="Hand"/>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Height="40">
|
||||||
|
<Label FontWeight="ExtraBold" FontSize="16" Grid.Row="1">拼接方式:</Label>
|
||||||
|
<RadioButton Name="JoinType1" Content="随机" GroupName="JoinType" IsChecked="{Binding VideoModel.IsJoinType1Selected, Mode=TwoWay}" Margin="10,2" />
|
||||||
|
<RadioButton Name="JoinType2" Content="按顺序组合" GroupName="JoinType" IsChecked="{Binding VideoModel.IsJoinType2Selected, Mode=TwoWay}" Margin="10,2" />
|
||||||
|
</StackPanel>
|
||||||
<StackPanel Height="30">
|
<StackPanel Height="30">
|
||||||
<Label FontWeight="ExtraBold" FontSize="16" Grid.Row="1">文件信息:</Label>
|
<Label FontWeight="ExtraBold" FontSize="16" Grid.Row="1">文件信息:</Label>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@ -45,7 +50,7 @@
|
|||||||
</DataGrid>
|
</DataGrid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Height="30">
|
<StackPanel Height="30">
|
||||||
<Label FontWeight="ExtraBold" FontSize="16">视频合成进度:</Label>
|
<Label FontWeight="ExtraBold" FontSize="16">视频合成进度:</Label>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user