mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-24 20:29:26 +01:00
Backport fast frame counting and other stuff from Nmkoder
This commit is contained in:
@@ -69,8 +69,8 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
@@ -403,6 +403,7 @@
|
||||
<Compile Include="Main\InterpolateUtils.cs" />
|
||||
<Compile Include="Main\FrameOrder.cs" />
|
||||
<Compile Include="Main\ResumeUtils.cs" />
|
||||
<Compile Include="Media\AvOutputHandler.cs" />
|
||||
<Compile Include="Media\AvProcess.cs" />
|
||||
<Compile Include="Media\FfmpegAlpha.cs" />
|
||||
<Compile Include="Media\FfmpegAudioAndMetadata.cs" />
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace Flowframes
|
||||
public void LoadBatchEntry(InterpSettings entry)
|
||||
{
|
||||
inputTbox.Text = entry.inPath;
|
||||
MainUiFunctions.SetOutPath(inputTbox, entry.outPath);
|
||||
MainUiFunctions.SetOutPath(outputTbox, entry.outPath);
|
||||
interpFactorCombox.Text = entry.interpFactor.ToString();
|
||||
aiCombox.SelectedIndex = Implementations.networks.IndexOf(Implementations.networks.Where(x => x.aiName == entry.ai.aiName).FirstOrDefault());
|
||||
SetOutMode(entry.outMode);
|
||||
|
||||
@@ -743,7 +743,7 @@ namespace Flowframes.IO
|
||||
}
|
||||
|
||||
if (log)
|
||||
Logger.Log($"Computed {hashType} for '{Path.GetFileNameWithoutExtension(path).Trunc(40) + Path.GetExtension(path)}' ({GetFilesizeStr(path)}): {hashStr} ({sw.GetElapsedStr()})", true);
|
||||
Logger.Log($"Computed {hashType} for '{Path.GetFileNameWithoutExtension(path).Trunc(40) + Path.GetExtension(path)}' ({GetFilesizeStr(path)}): {hashStr} ({sw})", true);
|
||||
|
||||
return hashStr;
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace Flowframes.Main
|
||||
MessageBox.Show("An error occured while trying to merge the video chunks.\nCheck the log for details.");
|
||||
}
|
||||
|
||||
Logger.Log($"Merged video chunks in {sw.GetElapsedStr()}", true);
|
||||
Logger.Log($"Merged video chunks in {sw}", true);
|
||||
}
|
||||
|
||||
static async Task MergeChunks(string framesFile, string outPath, bool isBackup = false)
|
||||
|
||||
132
Code/Media/AvOutputHandler.cs
Normal file
132
Code/Media/AvOutputHandler.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Flowframes.MiscUtils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using static Flowframes.AvProcess;
|
||||
|
||||
namespace Flowframes.Media
|
||||
{
|
||||
class AvOutputHandler
|
||||
{
|
||||
public static readonly string prefix = "[ffmpeg]";
|
||||
|
||||
public static void LogOutput(string line, ref string appendStr, string logFilename, LogMode logMode, bool showProgressBar)
|
||||
{
|
||||
if (Interpolate.canceled || string.IsNullOrWhiteSpace(line) || line.Trim().Length < 1)
|
||||
return;
|
||||
|
||||
bool hidden = logMode == LogMode.Hidden;
|
||||
|
||||
if (HideMessage(line)) // Don't print certain warnings
|
||||
hidden = true;
|
||||
|
||||
bool replaceLastLine = logMode == LogMode.OnlyLastLine;
|
||||
|
||||
if (line.Contains("time=") && (line.StartsWith("frame=") || line.StartsWith("size=")))
|
||||
line = FormatUtils.BeautifyFfmpegStats(line);
|
||||
|
||||
appendStr += Environment.NewLine + line;
|
||||
Logger.Log($"{prefix} {line}", hidden, replaceLastLine, logFilename);
|
||||
|
||||
if (!hidden && showProgressBar && line.Contains("Time:"))
|
||||
{
|
||||
Regex timeRegex = new Regex("(?<=Time:).*(?= )");
|
||||
UpdateFfmpegProgress(timeRegex.Match(line).Value);
|
||||
}
|
||||
|
||||
|
||||
if (line.Contains("Unable to"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.Contains("Could not open file"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.Contains("No NVENC capable devices found") || line.MatchesWildcard("*nvcuda.dll*"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}\n\nMake sure you have an NVENC-capable Nvidia GPU.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.Contains("not currently supported in container") || line.Contains("Unsupported codec id"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}\n\nIt looks like you are trying to copy a stream into a container that doesn't support this codec.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.Contains("Subtitle encoding currently only possible from text to text or bitmap to bitmap"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}\n\nYou cannot encode image-based subtitles into text-based subtitles. Please use the Copy Subtitles option instead, with a compatible container.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.Contains("Only VP8 or VP9 or AV1 video and Vorbis or Opus audio and WebVTT subtitles are supported for WebM"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}\n\nIt looks like you are trying to copy an unsupported stream into WEBM!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.MatchesWildcard("*codec*not supported*"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}\n\nTry using a different codec.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.Contains("GIF muxer supports only a single video GIF stream"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}\n\nYou tried to mux a non-GIF stream into a GIF file.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.Contains("Width and height of input videos must be same"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateFfmpegProgress(string ffmpegTime)
|
||||
{
|
||||
try
|
||||
{
|
||||
Form1 form = Program.mainForm;
|
||||
long currInDuration = (form.currInDurationCut < form.currInDuration) ? form.currInDurationCut : form.currInDuration;
|
||||
|
||||
if (currInDuration < 1)
|
||||
{
|
||||
Program.mainForm.SetProgress(0);
|
||||
return;
|
||||
}
|
||||
|
||||
long total = currInDuration / 100;
|
||||
long current = FormatUtils.TimestampToMs(ffmpegTime);
|
||||
int progress = Convert.ToInt32(current / total);
|
||||
Program.mainForm.SetProgress(progress);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log($"Failed to get ffmpeg progress: {e.Message}", true);
|
||||
}
|
||||
}
|
||||
|
||||
static bool HideMessage(string msg)
|
||||
{
|
||||
string[] hiddenMsgs = new string[] { "can produce invalid output", "pixel format", "provided invalid" };
|
||||
|
||||
foreach (string str in hiddenMsgs)
|
||||
if (msg.MatchesWildcard($"*{str}*"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Flowframes.MiscUtils;
|
||||
using Microsoft.VisualBasic;
|
||||
using Flowframes.Media;
|
||||
|
||||
namespace Flowframes
|
||||
{
|
||||
@@ -17,8 +18,6 @@ namespace Flowframes
|
||||
{
|
||||
public static Process lastAvProcess;
|
||||
public static Stopwatch timeSinceLastOutput = new Stopwatch();
|
||||
public enum TaskType { ExtractFrames, ExtractOther, Encode, GetInfo, Merge, Other };
|
||||
public static TaskType lastTask = TaskType.Other;
|
||||
|
||||
public static string lastOutputFfmpeg;
|
||||
|
||||
@@ -42,52 +41,64 @@ namespace Flowframes
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task RunFfmpeg(string args, LogMode logMode, TaskType taskType = TaskType.Other, bool progressBar = false)
|
||||
public static async Task<string> RunFfmpeg(string args, LogMode logMode, bool reliableOutput = true, bool progressBar = false)
|
||||
{
|
||||
await RunFfmpeg(args, "", logMode, defLogLevel, taskType, progressBar);
|
||||
return await RunFfmpeg(args, "", logMode, defLogLevel, reliableOutput, progressBar);
|
||||
}
|
||||
|
||||
public static async Task RunFfmpeg(string args, LogMode logMode, string loglevel, TaskType taskType = TaskType.Other, bool progressBar = false)
|
||||
public static async Task<string> RunFfmpeg(string args, LogMode logMode, string loglevel, bool reliableOutput = true, bool progressBar = false)
|
||||
{
|
||||
await RunFfmpeg(args, "", logMode, loglevel, taskType, progressBar);
|
||||
return await RunFfmpeg(args, "", logMode, loglevel, reliableOutput, progressBar);
|
||||
}
|
||||
|
||||
public static async Task RunFfmpeg(string args, string workingDir, LogMode logMode, TaskType taskType = TaskType.Other, bool progressBar = false)
|
||||
public static async Task<string> RunFfmpeg(string args, string workingDir, LogMode logMode, bool reliableOutput = true, bool progressBar = false)
|
||||
{
|
||||
await RunFfmpeg(args, workingDir, logMode, defLogLevel, taskType, progressBar);
|
||||
return await RunFfmpeg(args, workingDir, logMode, defLogLevel, reliableOutput, progressBar);
|
||||
}
|
||||
|
||||
public static async Task RunFfmpeg(string args, string workingDir, LogMode logMode, string loglevel, TaskType taskType = TaskType.Other, bool progressBar = false)
|
||||
public static async Task<string> RunFfmpeg(string args, string workingDir, LogMode logMode, string loglevel, bool reliableOutput = true, bool progressBar = false)
|
||||
{
|
||||
lastOutputFfmpeg = "";
|
||||
currentLogMode = logMode;
|
||||
showProgressBar = progressBar;
|
||||
Process ffmpeg = OsUtils.NewProcess(true);
|
||||
timeSinceLastOutput.Restart();
|
||||
bool show = Config.GetInt(Config.Key.cmdDebugMode) > 0;
|
||||
string processOutput = "";
|
||||
Process ffmpeg = OsUtils.NewProcess(!show);
|
||||
NmkdStopwatch timeSinceLastOutput = new NmkdStopwatch();
|
||||
lastAvProcess = ffmpeg;
|
||||
lastTask = taskType;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(loglevel))
|
||||
loglevel = defLogLevel;
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(workingDir))
|
||||
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {workingDir.Wrap()} & {Path.Combine(GetAvDir(), "ffmpeg.exe").Wrap()} {GetFfmpegDefaultArgs(loglevel)} {args}";
|
||||
string beforeArgs = $"-hide_banner -stats -loglevel {loglevel} -y";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(workingDir))
|
||||
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {workingDir.Wrap()} & {Path.Combine(GetAvDir(), "ffmpeg.exe").Wrap()} {beforeArgs} {args}";
|
||||
else
|
||||
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg.exe {GetFfmpegDefaultArgs(loglevel)} {args}";
|
||||
|
||||
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg {beforeArgs} {args}";
|
||||
|
||||
if (logMode != LogMode.Hidden) Logger.Log("Running FFmpeg...", false);
|
||||
Logger.Log($"ffmpeg {GetFfmpegDefaultArgs(loglevel)} {args}", true, false, "ffmpeg");
|
||||
ffmpeg.OutputDataReceived += FfmpegOutputHandler;
|
||||
ffmpeg.ErrorDataReceived += FfmpegOutputHandler;
|
||||
Logger.Log($"ffmpeg {beforeArgs} {args}", true, false, "ffmpeg");
|
||||
|
||||
if (!show)
|
||||
{
|
||||
ffmpeg.OutputDataReceived += (sender, outLine) => { AvOutputHandler.LogOutput(outLine.Data, ref processOutput, "ffmpeg", logMode, progressBar); timeSinceLastOutput.sw.Restart(); };
|
||||
ffmpeg.ErrorDataReceived += (sender, outLine) => { AvOutputHandler.LogOutput(outLine.Data, ref processOutput, "ffmpeg", logMode, progressBar); timeSinceLastOutput.sw.Restart(); };
|
||||
}
|
||||
|
||||
ffmpeg.Start();
|
||||
ffmpeg.BeginOutputReadLine();
|
||||
ffmpeg.BeginErrorReadLine();
|
||||
ffmpeg.PriorityClass = ProcessPriorityClass.BelowNormal;
|
||||
|
||||
while (!ffmpeg.HasExited)
|
||||
await Task.Delay(1);
|
||||
if (!show)
|
||||
{
|
||||
ffmpeg.BeginOutputReadLine();
|
||||
ffmpeg.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
if(progressBar)
|
||||
while (!ffmpeg.HasExited) await Task.Delay(10);
|
||||
while (reliableOutput && timeSinceLastOutput.ElapsedMs < 200) await Task.Delay(50);
|
||||
|
||||
if (progressBar)
|
||||
Program.mainForm.SetProgress(0);
|
||||
|
||||
return processOutput;
|
||||
}
|
||||
|
||||
public static string GetFfmpegDefaultArgs (string loglevel = "warning")
|
||||
@@ -95,71 +106,41 @@ namespace Flowframes
|
||||
return $"-hide_banner -stats -loglevel {loglevel} -y";
|
||||
}
|
||||
|
||||
static void FfmpegOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
|
||||
public static async Task<string> RunFfprobe(string args, LogMode logMode = LogMode.Hidden, string loglevel = "quiet")
|
||||
{
|
||||
timeSinceLastOutput.Restart();
|
||||
bool show = Config.GetInt(Config.Key.cmdDebugMode) > 0;
|
||||
string processOutput = "";
|
||||
Process ffprobe = OsUtils.NewProcess(!show);
|
||||
NmkdStopwatch timeSinceLastOutput = new NmkdStopwatch();
|
||||
lastAvProcess = ffprobe;
|
||||
|
||||
if (Interpolate.canceled || outLine == null || outLine.Data == null)
|
||||
return;
|
||||
if (string.IsNullOrWhiteSpace(loglevel))
|
||||
loglevel = defLogLevel;
|
||||
|
||||
string line = outLine.Data;
|
||||
lastOutputFfmpeg = lastOutputFfmpeg + "\n" + line;
|
||||
ffprobe.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffprobe -v {loglevel} {args}";
|
||||
|
||||
bool hidden = currentLogMode == LogMode.Hidden;
|
||||
if (logMode != LogMode.Hidden) Logger.Log("Running FFprobe...", false);
|
||||
Logger.Log($"ffprobe -v {loglevel} {args}", true, false, "ffmpeg");
|
||||
|
||||
if (HideMessage(line)) // Don't print certain warnings
|
||||
hidden = true;
|
||||
|
||||
bool replaceLastLine = currentLogMode == LogMode.OnlyLastLine;
|
||||
|
||||
if (line.StartsWith("frame="))
|
||||
line = FormatUtils.BeautifyFfmpegStats(line);
|
||||
|
||||
Logger.Log(line, hidden, replaceLastLine, "ffmpeg");
|
||||
|
||||
if (line.Contains(".srt: Invalid data found"))
|
||||
Logger.Log($"Warning: Failed to encode subtitle track {line.Split(':')[2]}. This track will be missing in the output file.");
|
||||
|
||||
if (line.Contains("Could not open file"))
|
||||
Interpolate.Cancel($"FFmpeg Error: {line}");
|
||||
|
||||
if (line.Contains("No NVENC capable devices found") || line.MatchesWildcard("*nvcuda.dll*"))
|
||||
Interpolate.Cancel($"FFmpeg Error: {line}\nMake sure you have an NVENC-capable Nvidia GPU.");
|
||||
|
||||
if (!hidden && showProgressBar && line.Contains("Time:"))
|
||||
if (!show)
|
||||
{
|
||||
Regex timeRegex = new Regex("(?<=Time:).*(?= )");
|
||||
UpdateFfmpegProgress(timeRegex.Match(line).Value);
|
||||
ffprobe.OutputDataReceived += (sender, outLine) => { AvOutputHandler.LogOutput(outLine.Data, ref processOutput, "ffmpeg", logMode, false); timeSinceLastOutput.sw.Restart(); };
|
||||
ffprobe.ErrorDataReceived += (sender, outLine) => { AvOutputHandler.LogOutput(outLine.Data, ref processOutput, "ffmpeg", logMode, false); timeSinceLastOutput.sw.Restart(); };
|
||||
}
|
||||
}
|
||||
|
||||
static bool HideMessage (string msg)
|
||||
{
|
||||
string[] hiddenMsgs = new string[] { "can produce invalid output", "pixel format", "provided invalid" };
|
||||
ffprobe.Start();
|
||||
ffprobe.PriorityClass = ProcessPriorityClass.BelowNormal;
|
||||
|
||||
foreach (string str in hiddenMsgs)
|
||||
if (msg.MatchesWildcard($"*{str}*"))
|
||||
return true;
|
||||
if (!show)
|
||||
{
|
||||
ffprobe.BeginOutputReadLine();
|
||||
ffprobe.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
while (!ffprobe.HasExited) await Task.Delay(10);
|
||||
while (timeSinceLastOutput.ElapsedMs < 200) await Task.Delay(50);
|
||||
|
||||
public static async Task<string> GetFfmpegOutputAsync(string args, bool setBusy = false, bool progressBar = false)
|
||||
{
|
||||
timeSinceLastOutput.Restart();
|
||||
if (Program.busy) setBusy = false;
|
||||
lastOutputFfmpeg = "";
|
||||
showProgressBar = progressBar;
|
||||
Process ffmpeg = OsUtils.NewProcess(true);
|
||||
lastAvProcess = ffmpeg;
|
||||
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg.exe -hide_banner -y -stats {args}";
|
||||
Logger.Log($"ffmpeg {args}", true, false, "ffmpeg");
|
||||
if (setBusy) Program.mainForm.SetWorking(true);
|
||||
lastOutputFfmpeg = await OsUtils.GetOutputAsync(ffmpeg);
|
||||
while (!ffmpeg.HasExited) await Task.Delay(50);
|
||||
while(timeSinceLastOutput.ElapsedMilliseconds < 200) await Task.Delay(50);
|
||||
if (setBusy) Program.mainForm.SetWorking(false);
|
||||
return lastOutputFfmpeg;
|
||||
return processOutput;
|
||||
}
|
||||
|
||||
public static string GetFfprobeOutput (string args)
|
||||
@@ -174,30 +155,6 @@ namespace Flowframes
|
||||
if (!string.IsNullOrWhiteSpace(err)) output += "\n" + err;
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void UpdateFfmpegProgress(string ffmpegTime)
|
||||
{
|
||||
try
|
||||
{
|
||||
Form1 form = Program.mainForm;
|
||||
long currInDuration = (form.currInDurationCut < form.currInDuration) ? form.currInDurationCut : form.currInDuration;
|
||||
|
||||
if (currInDuration < 1)
|
||||
{
|
||||
Program.mainForm.SetProgress(0);
|
||||
return;
|
||||
}
|
||||
|
||||
long total = currInDuration / 100;
|
||||
long current = FormatUtils.TimestampToMs(ffmpegTime);
|
||||
int progress = Convert.ToInt32(current / total);
|
||||
Program.mainForm.SetProgress(progress);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log($"Failed to get ffmpeg progress: {e.Message}", true);
|
||||
}
|
||||
}
|
||||
|
||||
static string GetAvDir ()
|
||||
{
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Flowframes
|
||||
string loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : "";
|
||||
string vfrFilename = Path.GetFileName(concatFile);
|
||||
string args = $" {loopStr} -vsync 1 -f concat -i {vfrFilename} -c copy -movflags +faststart -fflags +genpts {outPath.Wrap()}";
|
||||
await RunFfmpeg(args, concatFile.GetParentDir(), LogMode.Hidden, TaskType.Merge);
|
||||
await RunFfmpeg(args, concatFile.GetParentDir(), LogMode.Hidden);
|
||||
}
|
||||
|
||||
public static async Task LoopVideo(string inputFile, int times, bool delSrc = false)
|
||||
@@ -157,9 +157,13 @@ namespace Flowframes
|
||||
|
||||
public static async Task<int> GetFrameCountAsync(string inputFile)
|
||||
{
|
||||
Logger.Log($"GetFrameCountAsync('{inputFile}') - Trying ffprobe first.", true, false, "ffmpeg");
|
||||
Logger.Log($"GetFrameCountAsync('{inputFile}') - Trying ffprobe packet counting first (fastest).", true, false, "ffmpeg");
|
||||
int frames = await ReadFrameCountFfprobePacketCount(inputFile); // Try reading frame count with ffprobe packet counting
|
||||
if (frames > 0) return frames;
|
||||
|
||||
int frames = await ReadFrameCountFfprobeAsync(inputFile, Config.GetBool(Config.Key.ffprobeFrameCount)); // Try reading frame count with ffprobe
|
||||
Logger.Log($"GetFrameCountAsync('{inputFile}') - Trying ffprobe decoding now.", true, false, "ffmpeg");
|
||||
|
||||
frames = await ReadFrameCountFfprobe(inputFile); // Try reading frame count with ffprobe decoding
|
||||
if (frames > 0) return frames;
|
||||
|
||||
Logger.Log($"Failed to get frame count using ffprobe (frames = {frames}). Trying to read with ffmpeg.", true, false, "ffmpeg");
|
||||
@@ -170,7 +174,7 @@ namespace Flowframes
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ReadFrameCountFromDuration (string inputFile, long durationMs, float fps)
|
||||
static int ReadFrameCountFromDuration(string inputFile, long durationMs, float fps)
|
||||
{
|
||||
float durationSeconds = durationMs / 1000f;
|
||||
float frameCount = durationSeconds * fps;
|
||||
@@ -179,21 +183,25 @@ namespace Flowframes
|
||||
return frameCountRounded;
|
||||
}
|
||||
|
||||
static async Task<int> ReadFrameCountFfprobeAsync(string inputFile, bool readFramesSlow)
|
||||
public static async Task<int> ReadFrameCountFfprobePacketCount(string filePath)
|
||||
{
|
||||
string args = $" -v panic -threads 0 -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1 {inputFile.Wrap()}";
|
||||
if (readFramesSlow)
|
||||
{
|
||||
Logger.Log("Counting total frames using FFprobe. This can take a moment...");
|
||||
await Task.Delay(10);
|
||||
args = $" -v panic -threads 0 -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 {inputFile.Wrap()}";
|
||||
}
|
||||
string info = GetFfprobeOutput(args);
|
||||
string output = await RunFfprobe($"-select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 {filePath.Wrap()}", LogMode.Hidden, "error");
|
||||
string[] lines = output.SplitIntoLines().Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
|
||||
|
||||
if (lines == null || lines.Length < 1)
|
||||
return 0;
|
||||
|
||||
return lines.Last().GetInt();
|
||||
}
|
||||
|
||||
public static async Task<int> ReadFrameCountFfprobe(string filePath)
|
||||
{
|
||||
string args = $" -v panic {filePath.Wrap()} -threads 0 -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1 {filePath.Wrap()}";
|
||||
string info = await RunFfprobe(args);
|
||||
string[] entries = info.SplitIntoLines();
|
||||
|
||||
try
|
||||
{
|
||||
if (readFramesSlow)
|
||||
return info.GetInt();
|
||||
foreach (string entry in entries)
|
||||
{
|
||||
if (entry.Contains("nb_frames="))
|
||||
@@ -201,17 +209,18 @@ namespace Flowframes
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static async Task<int> ReadFrameCountFfmpegAsync (string inputFile)
|
||||
public static async Task<int> ReadFrameCountFfmpegAsync(string filePath)
|
||||
{
|
||||
string args = $" -loglevel panic -stats -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null - ";
|
||||
string info = await GetFfmpegOutputAsync(args, true, true);
|
||||
string args = $" -loglevel panic -stats {filePath.Wrap()} -i {filePath.Wrap()} -map 0:v:0 -c copy -f null - ";
|
||||
string info = await RunFfmpeg(args, LogMode.Hidden);
|
||||
try
|
||||
{
|
||||
string[] lines = info.SplitIntoLines();
|
||||
string lastLine = lines.Last();
|
||||
string lastLine = lines.Last().ToLower();
|
||||
return lastLine.Substring(0, lastLine.IndexOf("fps")).GetInt();
|
||||
}
|
||||
catch
|
||||
@@ -231,7 +240,7 @@ namespace Flowframes
|
||||
{
|
||||
Logger.Log($"IsEncoderCompatible('{enc}')", true, false, "ffmpeg");
|
||||
string args = $"-loglevel error -f lavfi -i color=black:s=540x540 -vframes 1 -an -c:v {enc} -f null -";
|
||||
string output = await GetFfmpegOutputAsync(args);
|
||||
string output = await RunFfmpeg(args, LogMode.Hidden);
|
||||
return !output.ToLower().Contains("error");
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Flowframes.Media
|
||||
}
|
||||
|
||||
//string argsOld = $"-vsync 0 -r {fps} {inArg} {encArgs} {vf} {GetAspectArg(extraData)} {extraArgs} -threads {Config.GetInt(Config.Key.ffEncThreads)} {outPath.Wrap()}";
|
||||
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, "error", TaskType.Encode, !isChunk);
|
||||
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, !isChunk);
|
||||
IoUtils.TryDeleteIfExists(linksDir);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Flowframes.Media
|
||||
string compression = format == "png" ? pngCompr : $"-q:v {lossyQ}";
|
||||
string codec = format == "webp" ? "-c:v libwebp" : ""; // Specify libwebp to avoid putting all frames into single animated WEBP
|
||||
string args = $"-vsync 0 -r {rate} {inArg} {codec} {compression} {sn} {vf} \"{outDir}/%{Padding.interpFrames}d.{format}\"";
|
||||
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, "error", TaskType.Encode, true);
|
||||
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, "error", true);
|
||||
IoUtils.TryDeleteIfExists(linksDir);
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Flowframes.Media
|
||||
string extraArgs = Config.Get(Config.Key.ffEncArgs);
|
||||
rate = rate / new Fraction(itsScale);
|
||||
string args = $"-f concat -r {rate} -i {framesFilename.Wrap()} -gifflags -offsetting {vf} {extraArgs} {outPath.Wrap()}";
|
||||
await RunFfmpeg(args, framesFile.GetParentDir(), LogMode.OnlyLastLine, "error", TaskType.Encode);
|
||||
await RunFfmpeg(args, framesFile.GetParentDir(), LogMode.OnlyLastLine, "error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Flowframes.Media
|
||||
string args = $"-vsync 0 {GetTrimArg(true)} {inArg} {GetImgArgs(format)} {rateArg} {scnDetect} -frame_pts 1 -s 256x144 {GetTrimArg(false)} \"{outDir}/%{Padding.inputFrames}d{format}\"";
|
||||
|
||||
LogMode logMode = await Interpolate.GetCurrentInputFrameCount() > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
|
||||
await RunFfmpeg(args, logMode, inputIsFrames ? "panic" : "warning", TaskType.ExtractFrames, true);
|
||||
await RunFfmpeg(args, logMode, inputIsFrames ? "panic" : "warning", true);
|
||||
|
||||
bool hiddenLog = await Interpolate.GetCurrentInputFrameCount() <= 50;
|
||||
int amount = IoUtils.GetAmountOfFiles(outDir, false);
|
||||
@@ -84,7 +84,7 @@ namespace Flowframes.Media
|
||||
string rateArg = (rate.GetFloat() > 0) ? $" -r {rate}" : "";
|
||||
string args = $"{GetTrimArg(true)} -i {inputFile.Wrap()} {GetImgArgs(format, true, alpha)} -vsync 0 {rateArg} -frame_pts 1 {vf} {sizeStr} {GetTrimArg(false)} \"{framesDir}/%{Padding.inputFrames}d{format}\"";
|
||||
LogMode logMode = await Interpolate.GetCurrentInputFrameCount() > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
|
||||
await RunFfmpeg(args, logMode, TaskType.ExtractFrames, true);
|
||||
await RunFfmpeg(args, logMode, true);
|
||||
int amount = IoUtils.GetAmountOfFiles(framesDir, false, "*" + format);
|
||||
Logger.Log($"Extracted {amount} {(amount == 1 ? "frame" : "frames")} from input.", false, true);
|
||||
await Task.Delay(1);
|
||||
@@ -173,7 +173,7 @@ namespace Flowframes.Media
|
||||
|
||||
if (!allSameSize)
|
||||
{
|
||||
Logger.Log($"Sequence not compatible: Not all images have the same dimensions [{sw.GetElapsedStr()}].", true);
|
||||
Logger.Log($"Sequence not compatible: Not all images have the same dimensions.", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ namespace Flowframes.Media
|
||||
|
||||
if (!allDivBy2)
|
||||
{
|
||||
Logger.Log($"Sequence not compatible: Not all image dimensions are divisible by {div} [{sw.GetElapsedStr()}].", true);
|
||||
Logger.Log($"Sequence not compatible: Not all image dimensions are divisible by {div}.", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace Flowframes.Media
|
||||
|
||||
if (!allSmallEnough)
|
||||
{
|
||||
Logger.Log($"Sequence not compatible: Image dimensions above max size [{sw.GetElapsedStr()}].", true);
|
||||
Logger.Log($"Sequence not compatible: Image dimensions above max size.", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -198,12 +198,12 @@ namespace Flowframes.Media
|
||||
|
||||
if (!all24Bit)
|
||||
{
|
||||
Logger.Log($"Sequence not compatible: Some images are not 24-bit (8bpp) [{sw.GetElapsedStr()}].", true);
|
||||
Logger.Log($"Sequence not compatible: Some images are not 24-bit (8bpp).", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
Interpolate.current.framesExt = files.First().Extension;
|
||||
Logger.Log($"Sequence compatible! [{sw.GetElapsedStr()}]", true);
|
||||
Logger.Log($"Sequence compatible!", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace Flowframes.Media
|
||||
string vf = $"-vf {GetPadFilter()}";
|
||||
string args = $"-r 25 {inArg} {GetImgArgs(format, true, alpha)} {sizeStr} -vsync 0 -start_number 0 {vf} \"{outPath}/%{Padding.inputFrames}d{format}\"";
|
||||
LogMode logMode = IoUtils.GetAmountOfFiles(inPath, false) > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
|
||||
await RunFfmpeg(args, logMode, "panic", TaskType.ExtractFrames);
|
||||
await RunFfmpeg(args, logMode, "panic");
|
||||
}
|
||||
|
||||
public static string[] GetTrimArgs()
|
||||
@@ -282,7 +282,7 @@ namespace Flowframes.Media
|
||||
string comprArg = isPng ? pngCompr : "";
|
||||
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
|
||||
string args = $"-i {inputFile.Wrap()} {comprArg} {sizeStr} {pixFmt} -vf {GetPadFilter()} {outPath.Wrap()}";
|
||||
await RunFfmpeg(args, LogMode.Hidden, TaskType.ExtractFrames);
|
||||
await RunFfmpeg(args, LogMode.Hidden);
|
||||
}
|
||||
|
||||
public static async Task ExtractSingleFrame(string inputFile, string outputPath, int frameNum)
|
||||
@@ -291,7 +291,7 @@ namespace Flowframes.Media
|
||||
string comprArg = isPng ? pngCompr : "";
|
||||
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
|
||||
string args = $"-i {inputFile.Wrap()} -vf \"select=eq(n\\,{frameNum})\" -vframes 1 {pixFmt} {outputPath.Wrap()}";
|
||||
await RunFfmpeg(args, LogMode.Hidden, TaskType.ExtractFrames);
|
||||
await RunFfmpeg(args, LogMode.Hidden);
|
||||
}
|
||||
|
||||
public static async Task ExtractLastFrame(string inputFile, string outputPath, Size size)
|
||||
@@ -309,7 +309,7 @@ namespace Flowframes.Media
|
||||
string trim = QuickSettingsTab.trimEnabled ? $"-ss {QuickSettingsTab.GetTrimEndMinusOne()} -to {QuickSettingsTab.trimEnd}" : "";
|
||||
string sseof = string.IsNullOrWhiteSpace(trim) ? "-sseof -1" : "";
|
||||
string args = $"{sseof} -i {inputFile.Wrap()} -update 1 {pixFmt} {sizeStr} {trim} {outputPath.Wrap()}";
|
||||
await RunFfmpeg(args, LogMode.Hidden, TaskType.ExtractFrames);
|
||||
await RunFfmpeg(args, LogMode.Hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Flowframes.MiscUtils
|
||||
{
|
||||
if(task.timer.sw.ElapsedMilliseconds > task.timeoutSeconds * 1000)
|
||||
{
|
||||
Logger.Log($"[BgTaskMgr] Task with ID {task.id} timed out, has been running for {task.timer.GetElapsedStr()}!", true);
|
||||
Logger.Log($"[BgTaskMgr] Task with ID {task.id} timed out, has been running for {task.timer}!", true);
|
||||
runningTasks.Remove(task);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace Flowframes.MiscUtils
|
||||
{
|
||||
if(task.id == id)
|
||||
{
|
||||
Logger.Log($"[BgTaskMgr] Task '{task.name}' has finished after {task.timer.GetElapsedStr()} (Timeout {task.timeoutSeconds}s)", true);
|
||||
Logger.Log($"[BgTaskMgr] Task '{task.name}' has finished after {task.timer} (Timeout {task.timeoutSeconds}s)", true);
|
||||
runningTasks.Remove(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Flowframes.MiscUtils
|
||||
{
|
||||
NmkdStopwatch sw = new NmkdStopwatch();
|
||||
var returnVal = method.DynamicInvoke(args);
|
||||
Logger.Log($"Ran {methodName} in {sw.GetElapsedStr()}", true);
|
||||
Logger.Log($"Ran {methodName} in {sw}", true);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Flowframes.MiscUtils
|
||||
{
|
||||
NmkdStopwatch sw = new NmkdStopwatch();
|
||||
method.DynamicInvoke(args);
|
||||
Logger.Log($"Ran {methodName} in {sw.GetElapsedStr()}", true);
|
||||
Logger.Log($"Ran {methodName} in {sw}", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ namespace Flowframes.MiscUtils
|
||||
{
|
||||
class NmkdStopwatch
|
||||
{
|
||||
public Stopwatch sw = new Stopwatch();
|
||||
public Stopwatch sw = new Stopwatch();
|
||||
public long ElapsedMs { get { return sw.ElapsedMilliseconds; } }
|
||||
|
||||
public NmkdStopwatch (bool startOnCreation = true)
|
||||
public NmkdStopwatch(bool startOnCreation = true)
|
||||
{
|
||||
if (startOnCreation)
|
||||
sw.Restart();
|
||||
}
|
||||
|
||||
public string GetElapsedStr ()
|
||||
public override string ToString()
|
||||
{
|
||||
return FormatUtils.TimeSw(sw);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Flowframes.Os
|
||||
|
||||
while (Interpolate.currentlyUsingAutoEnc && Program.busy)
|
||||
{
|
||||
if (AvProcess.lastAvProcess != null && !AvProcess.lastAvProcess.HasExited && AvProcess.lastTask == AvProcess.TaskType.Encode)
|
||||
if (AvProcess.lastAvProcess != null && !AvProcess.lastAvProcess.HasExited)
|
||||
{
|
||||
string lastLine = AvProcess.lastOutputFfmpeg.SplitIntoLines().Last();
|
||||
Logger.Log(FormatUtils.BeautifyFfmpegStats(lastLine), false, Logger.GetLastLine().ToLower().Contains("frame"));
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace Flowframes.Os
|
||||
while (timeSinceLastOutput.ElapsedMilliseconds < 100) await Task.Delay(50);
|
||||
output = output.Trim('\r', '\n');
|
||||
|
||||
Logger.Log($"Output (after {sw.GetElapsedStr()}): {output.Replace("\r", " / ").Replace("\n", " / ").Trunc(250)}", true);
|
||||
Logger.Log($"Output (after {sw}): {output.Replace("\r", " / ").Replace("\n", " / ").Trunc(250)}", true);
|
||||
|
||||
if (onlyLastLine)
|
||||
output = output.SplitIntoLines().LastOrDefault();
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Flowframes.Ui
|
||||
}
|
||||
|
||||
Config.Set(dict);
|
||||
Logger.Log($"Config Editor: Saved {grid.Rows.Count} config keys in {sw.GetElapsedStr()}", true);
|
||||
Logger.Log($"Config Editor: Saved {grid.Rows.Count} config keys in {sw}", true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Flowframes.Ui
|
||||
if (Config.GetBool(Config.Key.clearLogOnInput))
|
||||
Logger.ClearLogBox();
|
||||
|
||||
SetOutPath(inputTbox, inputTbox.Text.Trim().GetParentDir());
|
||||
SetOutPath(outputTbox, inputTbox.Text.Trim().GetParentDir());
|
||||
|
||||
Program.lastInputPath = path;
|
||||
Program.lastInputPathIsSsd = OsUtils.DriveIsSSD(path);
|
||||
|
||||
Reference in New Issue
Block a user