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
{
///
/// Video.xaml 的交互逻辑
///
public partial class Video : Window
{
public string text = "", outPut = "";
static int totalVideoCount = 0;
public Dictionary videoFolders = [];
private static string Path = "", ParentPath = "";
public Dictionary 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> 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 DirectoryInfo(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 files, string StrOutMp4Path)
{
var random = new Random();
//int i = 0;
//string vao = "";
List ooootext = [];
List 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> ls)
{
List lss = [];
List tasks = [];
Random rd = new();
//ls = [.. ls.OrderBy(x => rd.Next())];
for (int i = 0; i < GenerateVideoCount; i++)
{
int index = rd.Next(ls.Count);
List 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> GenerateAllvideos(Dictionary dict)
{
List> ls = [];
foreach (var item in dict)
{
ls.Add(new List(item.Value));
}
List> permutations = CalculatePermutations(ls);
return permutations;
}
static List> CalculatePermutations(List> inputList)
{
List> result = [];
// 递归计算全排列的辅助函数
void Permute(List currentPermutation, int index)
{
if (index == inputList.Count)
{
result.Add(new List(currentPermutation));
return;
}
foreach (string item in inputList[index])
{
currentPermutation.Add(item);
Permute(currentPermutation, index + 1);
currentPermutation.RemoveAt(currentPermutation.Count - 1);
}
}
Permute([], 0);
return result;
}
}
public class A
{
public int Index { get; set; }
};
}