More reliable GetDurationMs, backported code from NmkdUtils

This commit is contained in:
N00MKRAD
2024-09-26 19:32:19 +02:00
parent dcbb70b603
commit 596a954d63
4 changed files with 239 additions and 7 deletions

View File

@@ -481,6 +481,7 @@
<Compile Include="MiscUtils\NmkdStopwatch.cs" />
<Compile Include="MiscUtils\OutputUtils.cs" />
<Compile Include="MiscUtils\ParseUtils.cs" />
<Compile Include="NUtilsTemp.cs" />
<Compile Include="Os\AiProcess.cs" />
<Compile Include="Extensions\ExtensionMethods.cs" />
<Compile Include="Forms\Main\Form1.cs">

View File

@@ -140,6 +140,11 @@ namespace Flowframes
public LogMode LoggingMode { get; set; } = LogMode.Hidden;
public string LogLevel { get; set; } = "panic";
public bool SetBusy { get; set; } = false;
public FfprobeSettings(string args = "")
{
Args = args;
}
}
public static async Task<string> RunFfprobe(FfprobeSettings settings, bool asyncOutput = false)
@@ -195,12 +200,12 @@ namespace Flowframes
return output;
}
static string GetAvDir()
public static string GetAvDir()
{
return Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
}
static string GetCmdArg()
public static string GetCmdArg()
{
return "/C";
}

View File

@@ -93,12 +93,15 @@ namespace Flowframes
public static async Task<long> GetDurationMs(string inputFile)
{
Logger.Log($"GetDuration({inputFile}) - Reading Duration using ffprobe.", true, false, "ffmpeg");
string args = $"-select_streams v:0 -show_entries format=duration -of csv=s=x:p=0 -sexagesimal {inputFile.Wrap()}";
FfprobeSettings settings = new FfprobeSettings() { Args = args };
string output = await RunFfprobe(settings);
Logger.Log($"GetDuration({inputFile}) - Reading duration by demuxing.", true, false, "ffmpeg");
string args = $"ffmpeg -loglevel panic -stats -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null NUL";
string output = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {args}");
return FormatUtils.TimestampToMs(output);
if (!output.MatchesWildcard("*time=* *"))
return 0;
output = output.Split("time=")[1].Split(" ")[0];
return (long)TimeSpan.ParseExact(output, @"hh\:mm\:ss\.ff", null).TotalMilliseconds;
}
public static async Task<Fraction> GetFramerate(string inputFile, bool preferFfmpeg = false)

223
CodeLegacy/NUtilsTemp.cs Normal file
View File

@@ -0,0 +1,223 @@
using Flowframes.IO;
using Flowframes.MiscUtils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Flowframes
{
public class NUtilsTemp
{
public class OsUtils
{
public static List<Process> SessionProcesses = new List<Process>();
public delegate void OutputDelegate(string s);
public class RunConfig
{
public string Command { get; set; } = "";
public int PrintOutputLines { get; set; } = 0;
public ProcessPriorityClass? Priority { get; set; } = null;
public Func<bool> Killswitch { get; set; } = null;
public int KillswitchCheckIntervalMs = 1000;
public OutputDelegate OnStdout;
public OutputDelegate OnStderr;
public OutputDelegate OnOutput;
public RunConfig() { }
public RunConfig(string cmd, int printOutputLines = 0)
{
Command = cmd;
PrintOutputLines = printOutputLines;
}
}
public class CommandResult
{
public string Output { get; set; } = "";
public string StdOut { get; set; } = "";
public string StdErr { get; set; } = "";
public int ExitCode { get; set; } = 0;
public TimeSpan RunTime { get; set; }
}
public static string RunCommand(string command, int printOutputLines = 0)
{
return Run(new RunConfig(command, printOutputLines)).Output;
}
public static CommandResult RunCommandShell(string cmd, int printOutputLines = 0)
{
var cfg = new RunConfig(cmd, printOutputLines);
return Run(cfg);
}
public static CommandResult Run(RunConfig cfg)
{
var sw = new NmkdStopwatch();
CommandResult result = null;
try
{
string tempScript = "";
var startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c {cfg.Command}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
Logger.Log($"{startInfo.FileName} {startInfo.Arguments}");
using (Process process = new Process { StartInfo = startInfo })
{
var output = new StringBuilder();
var stdout = new StringBuilder();
var stderr = new StringBuilder();
var outputClosed = new TaskCompletionSource<bool>();
var errorClosed = new TaskCompletionSource<bool>();
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
{
outputClosed.SetResult(true);
}
else
{
output.AppendLine(e.Data);
stdout.AppendLine(e.Data);
// Log($"[STDOUT] {e.Data}", Level.Debug);
cfg.OnStdout?.Invoke($"{e.Data}");
cfg.OnOutput?.Invoke($"{e.Data}");
}
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
{
errorClosed.SetResult(true);
}
else
{
output.AppendLine(e.Data);
stderr.AppendLine(e.Data);
// Log($"[STDERR] {e.Data}", Level.Debug);
cfg.OnStderr?.Invoke($"{e.Data}");
cfg.OnOutput?.Invoke($"{e.Data}");
}
};
ProcessPriorityClass? previousParentPrio = GetOwnProcessPriority();
SetOwnProcessPriority(cfg.Priority); // The only reliable way of setting the new child proc's priority is by changing the parent's priority...
process.Start();
SessionProcesses.Add(process);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
SetOwnProcessPriority(previousParentPrio); // ...and afterwards changing the parent's priority back to what it was
if (cfg.Killswitch == null)
{
process.WaitForExit();
}
else
{
while (cfg.Killswitch() == false)
{
Thread.Sleep(cfg.KillswitchCheckIntervalMs);
if (process.HasExited)
break;
}
// Killswitch true
if (!process.HasExited)
{
Logger.Log("Killswitch true, killing process.", hidden: true);
process.Kill();
}
}
// Ensure output and error streams have finished processing
Task.WhenAll(outputClosed.Task, errorClosed.Task).Wait();
result = new CommandResult { Output = output.ToString(), StdOut = stdout.ToString(), StdErr = stderr.ToString(), ExitCode = process.ExitCode, RunTime = sw.Elapsed };
if (tempScript.IsNotEmpty())
{
IoUtils.TryDeleteIfExists(tempScript);
}
// if (cfg.PrintOutputLines > 0)
// {
// Logger.Log($"Finished (Code {result.ExitCode}). Output:{Environment.NewLine}...{Environment.NewLine}{string.Join(Environment.NewLine, result.Output.SplitIntoLines().TakeLast(cfg.PrintOutputLines))}", hidden: true);
// }
}
}
catch (Exception ex)
{
Logger.Log($"Error running command: {ex.Message}", hidden: true);
result = new CommandResult { ExitCode = 1, RunTime = sw.Elapsed };
}
return result;
}
public static Process NewProcess(bool hidden, string filename = "cmd.exe", Action<string> logAction = null, bool redirectStdin = false, Encoding outputEnc = null)
{
var p = new Process();
p.StartInfo.UseShellExecute = !hidden;
p.StartInfo.RedirectStandardOutput = hidden;
p.StartInfo.RedirectStandardError = hidden;
p.StartInfo.CreateNoWindow = hidden;
p.StartInfo.FileName = filename;
p.StartInfo.RedirectStandardInput = redirectStdin;
if (outputEnc != null)
{
p.StartInfo.StandardOutputEncoding = outputEnc;
p.StartInfo.StandardErrorEncoding = outputEnc;
}
if (hidden && logAction != null)
{
p.OutputDataReceived += (sender, line) => { logAction(line.Data); };
p.ErrorDataReceived += (sender, line) => { logAction(line.Data); };
}
return p;
}
public static ProcessPriorityClass GetOwnProcessPriority()
{
using (Process self = Process.GetCurrentProcess())
{
return self.PriorityClass;
}
}
public static void SetOwnProcessPriority(ProcessPriorityClass? priority = ProcessPriorityClass.BelowNormal)
{
if (priority == null)
return;
using (Process self = Process.GetCurrentProcess())
{
self.PriorityClass = priority == null ? ProcessPriorityClass.BelowNormal : (ProcessPriorityClass)priority;
// Logger.Log($"Process priority changed to {self.PriorityClass}", hidden: true);
}
}
}
}
}