v1.*
This commit is contained in:
parent
ce4b4b218a
commit
dcfb1397bf
@ -28,19 +28,6 @@ namespace VideoConcat
|
||||
|
||||
private void BtnLogin_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
if (txtUserName.Text == "admin" && txtPassword.Password == "&o4Fwag3xYUf")
|
||||
{
|
||||
Video video = new();
|
||||
video.Show();
|
||||
|
||||
this.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show("用户名或者密码错误!");
|
||||
txtUserName.Clear();
|
||||
txtPassword.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
Task.cs
35
Task.cs
@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace VideoConcat
|
||||
{
|
||||
|
||||
public static class ProcessVideo
|
||||
{
|
||||
public static void RunTask(System.Windows.Controls.ProgressBar progressBar, System.Windows.Controls.Button btn)
|
||||
{
|
||||
for (int i = 0; i <= 100; i++)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
progressBar.Dispatcher.Invoke(() =>
|
||||
{
|
||||
progressBar.Value = i;
|
||||
|
||||
});
|
||||
});
|
||||
System.Threading.Thread.Sleep(50);
|
||||
}
|
||||
btn.Dispatcher.Invoke(() =>
|
||||
{
|
||||
btn.IsEnabled = true;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
Video.xaml
37
Video.xaml
@ -1,37 +0,0 @@
|
||||
<Window x:Class="VideoConcat.Video"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:local="clr-namespace:VideoConcat"
|
||||
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
|
||||
mc:Ignorable="d"
|
||||
Title="视频" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Width="710" Height="400">
|
||||
<Grid>
|
||||
<Viewbox Margin="5">
|
||||
<Grid>
|
||||
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="120"></ColumnDefinition>
|
||||
<ColumnDefinition Width="500"></ColumnDefinition>
|
||||
<ColumnDefinition Width="80"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="30"></RowDefinition>
|
||||
<RowDefinition Height="300"></RowDefinition>
|
||||
<RowDefinition Height="10"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Content="生成视频的个数:" VerticalContentAlignment="Center" BorderThickness="1" Padding="5,0,5,0"></Label>
|
||||
<TextBox Grid.Column="1" wd:TextBoxHelper.MinValue="1" x:Name="videoCount" ></TextBox>
|
||||
<Button Grid.Column="2" Content="开始拼接" Click="Button_Click" x:Name="startButton" />
|
||||
<ScrollViewer Grid.Row="1" Grid.ColumnSpan="3" VerticalScrollBarVisibility="Auto" x:Name="scrowText" Margin="0,10,0,0">
|
||||
<TextBlock Text="" x:Name="outputTxt" OverridesDefaultStyle="True" TextWrapping="Wrap"/>
|
||||
</ScrollViewer>
|
||||
<ProgressBar Grid.Row="2" Grid.ColumnSpan="3" x:Name="processVideoBar" Minimum="0" Maximum="100" Value="0" Margin="0,0,0,0" />
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</Window>
|
||||
390
Video.xaml.cs
390
Video.xaml.cs
@ -1,390 +0,0 @@
|
||||
using Standard;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Shapes;
|
||||
using WPFDevelopers.Helpers;
|
||||
using File = System.IO.File;
|
||||
|
||||
|
||||
namespace VideoConcat
|
||||
{
|
||||
/// <summary>
|
||||
/// Video.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class Video : Window
|
||||
{
|
||||
public string text = "", outPut = "";
|
||||
static int totalVideoCount = 0;
|
||||
|
||||
public Dictionary<int, string[]> videoFolders = [];
|
||||
|
||||
private static string Path = "", ParentPath = "";
|
||||
|
||||
public Dictionary<int, A> AllPublicCombinVideos = [];
|
||||
|
||||
int GenerateVideoCount = 0;
|
||||
|
||||
public static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public Video()
|
||||
{
|
||||
InitializeComponent();
|
||||
Init();
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
ParentPath = Directory.GetCurrentDirectory();
|
||||
log4net.Config.XmlConfigurator.Configure();
|
||||
}
|
||||
|
||||
private async void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
GenerateVideoCount = int.Parse(videoCount.Text);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
MessageBox.Show("请输入正确的数字");
|
||||
|
||||
return;
|
||||
}
|
||||
totalVideoCount = 0;
|
||||
|
||||
Check_Folder();
|
||||
|
||||
WriteTxt($"最多可生成视频个数为{totalVideoCount}个");
|
||||
|
||||
if (GenerateVideoCount > totalVideoCount)
|
||||
{
|
||||
WriteTxt($"生成视频个数不能超过{totalVideoCount}个");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
MakeOutPutDir();
|
||||
|
||||
startButton.IsEnabled = false;
|
||||
//GenerateAllvideos(videoFolders);
|
||||
var dateStart = DateTime.Now; //记录用时的起始时间
|
||||
List<List<string>> lists = GenerateAllvideos(videoFolders);
|
||||
|
||||
|
||||
await MockIOPerformanceAsync(lists);
|
||||
|
||||
|
||||
startButton.Dispatcher.Invoke(() =>
|
||||
{
|
||||
startButton.IsEnabled = true;
|
||||
});
|
||||
|
||||
var dateEnd = DateTime.Now;
|
||||
var timeSpan = dateEnd - dateStart;//记录用时
|
||||
|
||||
try
|
||||
{
|
||||
string mp4Path = $"{Directory.GetCurrentDirectory()}\\temp\\";
|
||||
|
||||
DirectoryInfo directoryInfo = new(mp4Path);
|
||||
|
||||
FileInfo[] files = directoryInfo.GetFiles("*.mp4"); // 获取指定目录下的所有 txt 文件
|
||||
|
||||
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
file.Delete(); // 删除文件
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WriteTxt($"用时:{timeSpan.TotalSeconds}秒");
|
||||
}
|
||||
|
||||
private void Check_Folder()
|
||||
{
|
||||
WriteTxt("开始检测视频文件路径");
|
||||
DirectoryInfo? directory = Directory.GetParent(ParentPath);
|
||||
if (directory == null)
|
||||
{
|
||||
WriteTxt("获取视频路径失败!");
|
||||
return;
|
||||
}
|
||||
|
||||
Path = directory.FullName;
|
||||
|
||||
WriteTxt("当前目录为:" + Path);
|
||||
videoFolders.Clear();
|
||||
AllPublicCombinVideos.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
int k = 1;
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
|
||||
var folder = string.Format("{0:D2}", i);
|
||||
string currentFolderPath = $"{Path}\\{folder}";
|
||||
if (Directory.Exists(currentFolderPath))
|
||||
{
|
||||
|
||||
|
||||
string[] videos = GetAllVideos(currentFolderPath);
|
||||
if (videos.Length > 0)
|
||||
{
|
||||
if (totalVideoCount == 0)
|
||||
{
|
||||
totalVideoCount = 1;
|
||||
}
|
||||
WriteTxt($"检测到视频文件夹:{folder},视频个数:{videos.Length}");
|
||||
totalVideoCount *= videos.Length;
|
||||
videoFolders.Add(k, videos);
|
||||
AllPublicCombinVideos.Add(k, new A());
|
||||
k++;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteTxt($"视频文件夹【{folder}】未发现MP4格式视频文件");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteTxt(string str)
|
||||
{
|
||||
|
||||
outputTxt.Dispatcher.Invoke(() =>
|
||||
{
|
||||
log.Info(str);
|
||||
text += str + "\r\n";
|
||||
outputTxt.Text = text;
|
||||
scrowText.ScrollToEnd();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static string[] GetAllVideos(string path)
|
||||
{
|
||||
return Directory.GetFiles(path, "*.mp4", SearchOption.AllDirectories);
|
||||
}
|
||||
|
||||
|
||||
public async Task CombineMp4WithoutTxt(List<string> files, string StrOutMp4Path)
|
||||
{
|
||||
var random = new Random();
|
||||
//int i = 0;
|
||||
//string vao = "";
|
||||
List<string> ooootext = [];
|
||||
List<Task> ts = [];
|
||||
files.ForEach(file =>
|
||||
{
|
||||
ts.Add(Task.Run(() =>
|
||||
{
|
||||
WriteTxt(file + "转换开始");
|
||||
var dateStart = DateTime.Now;
|
||||
string tmpOut = $"{Directory.GetCurrentDirectory()}\\temp\\{Guid.NewGuid()}{random.Next(100000, 999999)}.mp4";
|
||||
// ffmpeg.exe - i a.mp4 - ac 1 - ar 48000 - vcodec copy a_re.mp4
|
||||
|
||||
Run_Command($"-i \"{file}\" -c:v libx264 -c:a aac {tmpOut}");
|
||||
ooootext.Add($"file '{tmpOut}'");
|
||||
|
||||
var dateEnd = DateTime.Now;
|
||||
WriteTxt($"{file}转换已完成,用时{(dateEnd - dateStart).TotalSeconds}秒");
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
log.Info(ooootext);
|
||||
await Task.WhenAll(ts).ContinueWith(t =>
|
||||
{
|
||||
string tmpConcatFile = $"{Directory.GetCurrentDirectory()}\\temp\\{Guid.NewGuid()}{random.Next(100000, 999999)}";
|
||||
// 也可以指定编码方式
|
||||
//File.WriteAllLines(tmpConcatFile, ooootext, Encoding.UTF8);
|
||||
|
||||
File.WriteAllText(tmpConcatFile, string.Join("\n", [.. ooootext]), new System.Text.UTF8Encoding(false));
|
||||
|
||||
|
||||
|
||||
|
||||
string StrArg = $"-f concat -safe 0 -i {tmpConcatFile} -c copy {StrOutMp4Path}";
|
||||
//string StrArg = $"-i {combineFile} -filter_complex \"{vao} concat=n={i}:v=1:a=1 [v][a]\" -map \"[v]\" -map \"[a]\" {StrOutMp4Path}";
|
||||
Run_Command(StrArg);
|
||||
|
||||
if (File.Exists(tmpConcatFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(tmpConcatFile);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void Run_Command(string command)
|
||||
{
|
||||
|
||||
Process p = new();//建立外部调用线程
|
||||
p.StartInfo.FileName = Directory.GetCurrentDirectory() + "\\ffmpeg.exe";//要调用外部程序的绝对路径
|
||||
|
||||
|
||||
|
||||
p.StartInfo.Arguments = command;
|
||||
|
||||
p.StartInfo.UseShellExecute = false;//不使用操作系统外壳程序启动线程(一定为FALSE,详细的请看MSDN)
|
||||
p.StartInfo.RedirectStandardError = true;//把外部程序错误输出写到StandardError流中(这个一定要注意,FFMPEG的所有输出信息,都为错误输出流,用StandardOutput是捕获不到任何消息的...这是我耗费了2个多月得出来的经验...mencoder就是用standardOutput来捕获的)
|
||||
p.StartInfo.CreateNoWindow = true;//不创建进程窗口
|
||||
p.ErrorDataReceived += new DataReceivedEventHandler(Output);//外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
|
||||
p.Start();//启动线程
|
||||
p.BeginErrorReadLine();//开始异步读取
|
||||
p.WaitForExit();//阻塞等待进程结束
|
||||
p.Close();//关闭进程
|
||||
p.Dispose();//释放资源
|
||||
|
||||
}
|
||||
|
||||
private void Output(object sendProcess, DataReceivedEventArgs output)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(output.Data))
|
||||
{
|
||||
log.Info(output.Data, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void MakeOutPutDir()
|
||||
{
|
||||
outPut = $"{Path}\\output";
|
||||
|
||||
if (!Directory.Exists(outPut))
|
||||
{
|
||||
Directory.CreateDirectory(outPut);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MockIOPerformanceAsync(List<List<string>> ls)
|
||||
{
|
||||
List<string> lss = [];
|
||||
List<Task> tasks = [];
|
||||
Random rd = new();
|
||||
|
||||
//ls = [.. ls.OrderBy(x => rd.Next())];
|
||||
|
||||
|
||||
for (int i = 0; i < GenerateVideoCount; i++)
|
||||
{
|
||||
int index = rd.Next(ls.Count);
|
||||
List<string> lsVideos = ls[index];
|
||||
var iIndex = i;
|
||||
tasks.Add(Task.Run(() =>
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
Random random = new();
|
||||
string randomString = random.Next(100000, 999999).ToString();
|
||||
string result = now.ToString("yyyyMMddHHmmss") + randomString;
|
||||
Task combineTask = CombineMp4WithoutTxt(lsVideos, $"{outPut}\\{result}.mp4");
|
||||
combineTask.Wait();
|
||||
var dateEnd = DateTime.Now;
|
||||
WriteTxt($"视频{iIndex}拼接完成,用时{(dateEnd - now).TotalSeconds}秒");
|
||||
}));
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private static List<List<string>> GenerateAllvideos(Dictionary<int, string[]> dict)
|
||||
{
|
||||
|
||||
List<List<string>> ls = [];
|
||||
foreach (var item in dict)
|
||||
{
|
||||
ls.Add(new List<string>(item.Value));
|
||||
}
|
||||
List<List<string>> permutations = GenerateAllPermutations(ls);
|
||||
|
||||
|
||||
return permutations;
|
||||
}
|
||||
|
||||
static List<List<string>> CalculatePermutations(List<List<string>> inputList)
|
||||
{
|
||||
List<List<string>> result = [];
|
||||
|
||||
// 递归计算全排列的辅助函数
|
||||
void Permute(List<string> currentPermutation, int index)
|
||||
{
|
||||
if (index == inputList.Count)
|
||||
{
|
||||
result.Add(new List<string>(currentPermutation));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string item in inputList[index])
|
||||
{
|
||||
currentPermutation.Add(item);
|
||||
Permute(currentPermutation, index + 1);
|
||||
currentPermutation.RemoveAt(currentPermutation.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Permute([], 0);
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static List<List<string>> GenerateAllPermutations(List<List<string>> ballInBoxes)
|
||||
{
|
||||
if (ballInBoxes.Count == 0)
|
||||
{
|
||||
return [[]];
|
||||
}
|
||||
|
||||
List<string> firstBoxBall = ballInBoxes[0];
|
||||
List<List<string>> remainingBoxes = ballInBoxes.GetRange(1, ballInBoxes.Count-1);
|
||||
|
||||
List<List<string>> subPermutations = GenerateAllPermutations(remainingBoxes);
|
||||
|
||||
List<List<string>> result = [];
|
||||
foreach (var firstItem in firstBoxBall)
|
||||
{
|
||||
foreach (var subPermutation in subPermutations)
|
||||
{
|
||||
List<string> newPermutation = [firstItem, .. subPermutation];
|
||||
result.Add(newPermutation);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class A
|
||||
{
|
||||
public int Index { get; set; }
|
||||
};
|
||||
|
||||
}
|
||||
@ -5,6 +5,7 @@
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user