diff --git a/Code/IO/IOUtils.cs b/Code/IO/IOUtils.cs index 6d14561..4bd3898 100644 --- a/Code/IO/IOUtils.cs +++ b/Code/IO/IOUtils.cs @@ -521,5 +521,37 @@ namespace Flowframes.IO } return true; } + + public static void ZeroPadDir(string path, string ext, int targetLength, bool recursive = false) + { + FileInfo[] files; + if (recursive) + files = new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.AllDirectories); + else + files = new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.TopDirectoryOnly); + + ZeroPadDir(files.Select(x => x.FullName).ToList(), targetLength); + } + + public static void ZeroPadDir(List files, int targetLength, List exclude = null) + { + if(exclude != null) + files = files.Except(exclude).ToList(); + + foreach (string file in files) + { + string fname = Path.GetFileNameWithoutExtension(file); + string targetFilename = Path.Combine(Path.GetDirectoryName(file), fname.PadLeft(targetLength, '0') + Path.GetExtension(file)); + try + { + if (targetFilename != file) + File.Move(file, targetFilename); + } + catch (Exception e) + { + Logger.Log($"Failed to zero-pad {file} => {targetFilename}: {e.Message}", true); + } + } + } } } diff --git a/Code/Magick/MagickDedupe.cs b/Code/Magick/MagickDedupe.cs index dcae5b2..89f5149 100644 --- a/Code/Magick/MagickDedupe.cs +++ b/Code/Magick/MagickDedupe.cs @@ -231,7 +231,7 @@ namespace Flowframes.Magick Logger.Log("Re-Duplicating frames to fix timing..."); RenameCounterDir(path, ext); - ZeroPadDir(path, ext, 8); + IOUtils.ZeroPadDir(path, ext, 8); string[] dupeFrameLines = IOUtils.ReadLines(dupeInfoFile); string tempSubFolder = Path.Combine(path, "temp"); @@ -300,7 +300,7 @@ namespace Flowframes.Magick } } } - ZeroPadDir(tempSubFolder, ext, 8); + IOUtils.ZeroPadDir(tempSubFolder, ext, 8); foreach (FileInfo file in new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.TopDirectoryOnly)) file.Delete(); @@ -325,21 +325,5 @@ namespace Flowframes.Magick counter++; } } - - public static void ZeroPadDir(string path, string ext, int targetLength, bool recursive = false) - { - FileInfo[] files; - if (recursive) - files = new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.AllDirectories); - else - files = new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.TopDirectoryOnly); - - foreach (FileInfo file in files) - { - string fnameNoExt = Path.GetFileNameWithoutExtension(file.Name); - string ext2 = Path.GetExtension(file.Name); ; - File.Move(file.FullName, Path.Combine(Path.GetDirectoryName(file.FullName), fnameNoExt.PadLeft(targetLength, '0') + ext2)); - } - } } } diff --git a/Code/Main/AutoEncode.cs b/Code/Main/AutoEncode.cs index bbb0ead..6ceb520 100644 --- a/Code/Main/AutoEncode.cs +++ b/Code/Main/AutoEncode.cs @@ -1,4 +1,5 @@ -using Flowframes.IO; +using Flowframes.Data; +using Flowframes.IO; using System; using System.Collections.Generic; using System.IO; @@ -19,6 +20,8 @@ namespace Flowframes.Main public static bool busy; + public static bool paused; + public static async Task MainLoop(string interpFramesPath) { @@ -28,20 +31,25 @@ namespace Flowframes.Main encodedFrames.Clear(); unencodedFrames.Clear(); - int interpFramesAmount; chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.currentFramesPath, false, "*.png") * Interpolate.lastInterpFactor); int videoIndex = 1; while (!Interpolate.canceled && GetInterpFramesAmount() < 2) - await Task.Delay(100); + await Task.Delay(1000); while (HasWorkToDo()) // Loop while proc is running and not all frames have been encoded { if (Interpolate.canceled) return; + if (paused) + { + await Task.Delay(100); + continue; + } + + IOUtils.ZeroPadDir(Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.lastExt}").ToList(), Padding.interpFrames, encodedFrames); string[] interpFrames = Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.lastExt}"); - interpFramesAmount = interpFrames.Length; unencodedFrames = interpFrames.ToList().Except(encodedFrames).ToList(); Directory.CreateDirectory(videoChunksFolder); @@ -54,6 +62,7 @@ namespace Flowframes.Main Logger.Log("Encoding Chunk #" + videoIndex, true, false, "ffmpeg.txt"); List framesToEncode = aiRunning ? unencodedFrames.Take(chunkSize).ToList() : unencodedFrames; // Take all remaining frames if process is done + IOUtils.ZeroPadDir(framesToEncode, Padding.interpFrames); // Zero-pad frames before encoding to make sure filenames match with VFR file string outpath = Path.Combine(videoChunksFolder, $"{videoIndex.ToString().PadLeft(4, '0')}{InterpolateUtils.GetExt(Interpolate.currentOutMode)}"); int firstFrameNum = Path.GetFileNameWithoutExtension(framesToEncode[0]).GetInt(); diff --git a/Code/Main/CreateVideo.cs b/Code/Main/CreateVideo.cs index d19ab53..f0fcae0 100644 --- a/Code/Main/CreateVideo.cs +++ b/Code/Main/CreateVideo.cs @@ -9,9 +9,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; +using Padding = Flowframes.Data.Padding; using i = Flowframes.Interpolate; namespace Flowframes.Main @@ -37,6 +37,9 @@ namespace Flowframes.Main return; } + //Logger.Log("zero-padding " + path); + //IOUtils.ZeroPadDir(path, $"*.{InterpolateUtils.lastExt}", Padding.interpFrames); + if (IOUtils.GetAmountOfFiles(path, false, $"*.{InterpolateUtils.lastExt}") <= 1) { i.Cancel("Output folder does not contain frames - An error must have occured during interpolation!", AiProcess.hasShownError); diff --git a/Code/Main/Interpolate.cs b/Code/Main/Interpolate.cs index 1d8676f..d7b4965 100644 --- a/Code/Main/Interpolate.cs +++ b/Code/Main/Interpolate.cs @@ -143,13 +143,11 @@ namespace Flowframes public static async Task PostProcessFrames (bool sbsMode = false) { - bool firstFrameFix = (!sbsMode && lastAi.aiName == Networks.rifeCuda.aiName) || (sbsMode && InterpolateSteps.currentAi.aiName == Networks.rifeCuda.aiName); - firstFrameFix = false; // TODO: Remove firstframefix if new rife code works + //bool firstFrameFix = (!sbsMode && lastAi.aiName == Networks.rifeCuda.aiName) || (sbsMode && InterpolateSteps.currentAi.aiName == Networks.rifeCuda.aiName); + //firstFrameFix = false; // TODO: Remove firstframefix if new rife code works if (!Directory.Exists(currentFramesPath) || IOUtils.GetAmountOfFiles(currentFramesPath, false, "*.png") <= 0) - { Cancel("Input frames folder is empty!"); - } if (Config.GetInt("dedupMode") == 1) await MagickDedupe.Run(currentFramesPath); @@ -161,24 +159,13 @@ namespace Flowframes bool useTimestamps = Config.GetInt("timingMode") == 1; // TODO: Auto-Disable timestamps if input frames are sequential, not timestamped if(sbsMode) - await VfrDedupe.CreateTimecodeFiles(currentFramesPath, Config.GetBool("enableLoop"), firstFrameFix, -1, !useTimestamps); + await VfrDedupe.CreateTimecodeFiles(currentFramesPath, Config.GetBool("enableLoop"), -1, !useTimestamps); else - await VfrDedupe.CreateTimecodeFiles(currentFramesPath, Config.GetBool("enableLoop"), firstFrameFix, lastInterpFactor, !useTimestamps); + await VfrDedupe.CreateTimecodeFiles(currentFramesPath, Config.GetBool("enableLoop"), lastInterpFactor, !useTimestamps); if (canceled) return; AiProcess.filenameMap = IOUtils.RenameCounterDirReversible(currentFramesPath, "png", 1, 8); - - //string hasPreprocessedFile = Path.Combine(currentTempDir, ".preprocessed"); - //if (File.Exists(hasPreprocessedFile)) return; - - if (firstFrameFix) - { - bool s = IOUtils.TryCopy(new DirectoryInfo(currentFramesPath).GetFiles("*.png")[0].FullName, Path.Combine(currentFramesPath, "00000000.png"), true); - Logger.Log("FirstFrameFix TryCopy Success: " + s, true); - } - - //File.Create(hasPreprocessedFile); } public static async Task RunAi(string outpath, int targetFrames, int tilesize, AI ai) diff --git a/Code/Main/InterpolateUtils.cs b/Code/Main/InterpolateUtils.cs index 79d3381..ad1010e 100644 --- a/Code/Main/InterpolateUtils.cs +++ b/Code/Main/InterpolateUtils.cs @@ -103,12 +103,12 @@ namespace Flowframes.Main basePath = custPath; } - return Path.Combine(basePath, Path.GetFileNameWithoutExtension(inPath).StripBadChars().Trunc(30, false) + "-temp"); + return Path.Combine(basePath, Path.GetFileNameWithoutExtension(inPath).StripBadChars().Remove(" ").Trunc(30, false) + "-temp"); } public static bool InputIsValid(string inDir, string outDir, float fpsOut, int interp, int tilesize) { - bool passes = true; + bool passes = true; bool isFile = !IOUtils.IsPathDirectory(inDir); diff --git a/Code/Main/VfrDedupe.cs b/Code/Main/VfrDedupe.cs index 14c5602..3481875 100644 --- a/Code/Main/VfrDedupe.cs +++ b/Code/Main/VfrDedupe.cs @@ -14,18 +14,18 @@ namespace Flowframes.Main { class VfrDedupe { - public static async Task CreateTimecodeFiles(string framesPath, bool loopEnabled, bool firstFrameFix, int times, bool noTimestamps) + public static async Task CreateTimecodeFiles(string framesPath, bool loopEnabled, int times, bool noTimestamps) { Logger.Log("Generating timecodes..."); if(times <= 0) { - await CreateTimecodeFile(framesPath, loopEnabled, 2, firstFrameFix, false, noTimestamps); - await CreateTimecodeFile(framesPath, loopEnabled, 4, firstFrameFix, true, noTimestamps); - await CreateTimecodeFile(framesPath, loopEnabled, 8, firstFrameFix, true, noTimestamps); + await CreateTimecodeFile(framesPath, loopEnabled, 2, false, noTimestamps); + await CreateTimecodeFile(framesPath, loopEnabled, 4, true, noTimestamps); + await CreateTimecodeFile(framesPath, loopEnabled, 8, true, noTimestamps); } else { - await CreateTimecodeFile(framesPath, loopEnabled, times, firstFrameFix, false, noTimestamps); + await CreateTimecodeFile(framesPath, loopEnabled, times, false, noTimestamps); } frameFiles = null; Logger.Log($"Generating timecodes... Done.", false, true); @@ -33,7 +33,7 @@ namespace Flowframes.Main static FileInfo[] frameFiles; - public static async Task CreateTimecodeFile(string framesPath, bool loopEnabled, int interpFactor, bool firstFrameFix, bool notFirstRun, bool noTimestamps) + public static async Task CreateTimecodeFile(string framesPath, bool loopEnabled, int interpFactor, bool notFirstRun, bool noTimestamps) { if (Interpolate.canceled) return; Logger.Log($"Generating timecodes for {interpFactor}x...", false, true); @@ -88,7 +88,7 @@ namespace Flowframes.Main { string durationStr = ((durationPerInterpFrame / 1000f) * 1).ToString("0.00000", CultureInfo.InvariantCulture); - if (discardThisFrame) + if (discardThisFrame && totalFileCount > 1) // Never discard 1st frame { int lastNum = totalFileCount - 1; for (int dupeCount = 1; dupeCount < interpFramesAmount; dupeCount++) @@ -111,14 +111,6 @@ namespace Flowframes.Main if (notFirstRun) return; // Skip all steps that only need to be done once - if (firstFrameFix) - { - string[] lines = IOUtils.ReadLines(vfrFile); - File.WriteAllText(vfrFile, lines[0].Replace($"{"1".PadLeft(Padding.interpFrames, '0')}.png", $"{"0".PadLeft(Padding.interpFrames, '0')}.png")); - File.AppendAllText(vfrFile, "\n" + lines[1] + "\n"); - File.AppendAllLines(vfrFile, lines); - } - if (Config.GetBool("enableLoop")) { int lastFileNumber = frameFiles.Last().Name.GetInt(); diff --git a/Code/OS/AiProcess.cs b/Code/OS/AiProcess.cs index ca25e78..af2159a 100644 --- a/Code/OS/AiProcess.cs +++ b/Code/OS/AiProcess.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Flowframes.OS; using Flowframes.UI; using Flowframes.Main; +using Flowframes.Data; namespace Flowframes { @@ -70,7 +71,10 @@ namespace Flowframes await Task.Delay(1); if (Interpolate.canceled) return; - Magick.MagickDedupe.ZeroPadDir(outPath, InterpolateUtils.lastExt, 8); + + if (!Interpolate.currentlyUsingAutoEnc) + IOUtils.ZeroPadDir(outPath, InterpolateUtils.lastExt, Padding.interpFrames); + AiFinished("DAIN"); } @@ -109,7 +113,9 @@ namespace Flowframes } if (Interpolate.canceled) return; - Magick.MagickDedupe.ZeroPadDir(outPath, InterpolateUtils.lastExt, 8); + + if (!Interpolate.currentlyUsingAutoEnc) + IOUtils.ZeroPadDir(outPath, InterpolateUtils.lastExt, Padding.interpFrames); AiFinished("CAIN"); } @@ -178,7 +184,11 @@ namespace Flowframes processTimeMulti.Restart(); Logger.Log("Running RIFE...", false); - string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()} -t {tilesize} -g {Config.Get("ncnnGpus")} -f {InterpolateUtils.lastExt} -j 4:{Config.Get("ncnnThreads")}:4"; + bool useAutoEnc = Interpolate.currentlyUsingAutoEnc; + if(times > 2) + AutoEncode.paused = true; // Disable autoenc until the last iteration + + string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()}"; await RunRifePartial(args); if (times == 4 || times == 8) // #2 @@ -189,7 +199,9 @@ namespace Flowframes IOUtils.TryDeleteIfExists(run1ResultsPath); Directory.Move(outPath, run1ResultsPath); Directory.CreateDirectory(outPath); - args = $" -v -i {run1ResultsPath.Wrap()} -o {outPath.Wrap()} -t {tilesize} -g {Config.Get("ncnnGpus")} -f {InterpolateUtils.lastExt} -j 4:{Config.Get("ncnnThreads")}:4"; + if (useAutoEnc && times == 4) + AutoEncode.paused = false; + args = $" -v -i {run1ResultsPath.Wrap()} -o {outPath.Wrap()}"; await RunRifePartial(args); IOUtils.TryDeleteIfExists(run1ResultsPath); } @@ -202,13 +214,20 @@ namespace Flowframes IOUtils.TryDeleteIfExists(run2ResultsPath); Directory.Move(outPath, run2ResultsPath); Directory.CreateDirectory(outPath); - args = $" -v -i {run2ResultsPath.Wrap()} -o {outPath.Wrap()} -t {tilesize} -g {Config.Get("ncnnGpus")} -f {InterpolateUtils.lastExt} -j 4:{Config.Get("ncnnThreads")}:4"; + if (useAutoEnc && times == 8) + AutoEncode.paused = false; + args = $" -v -i {run2ResultsPath.Wrap()} -o {outPath.Wrap()}"; await RunRifePartial(args); IOUtils.TryDeleteIfExists(run2ResultsPath); } if (Interpolate.canceled) return; - Magick.MagickDedupe.ZeroPadDir(outPath, InterpolateUtils.lastExt, 8); + + if (!Interpolate.currentlyUsingAutoEnc) + { + Logger.Log($"zero padding {outPath} with ext \"{InterpolateUtils.lastExt}\" to length {Padding.interpFrames}"); + IOUtils.ZeroPadDir(outPath, InterpolateUtils.lastExt, Padding.interpFrames); + } AiFinished("RIFE"); } @@ -217,7 +236,7 @@ namespace Flowframes { Process rifeNcnn = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd()); AiStarted(rifeNcnn, 1500); - rifeNcnn.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeNcnn).Wrap()} & rife-ncnn-vulkan.exe {args} -f {InterpolateUtils.lastExt} -j 4:{Config.Get("ncnnThreads")}:4"; + rifeNcnn.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeNcnn).Wrap()} & rife-ncnn-vulkan.exe {args} -g {Config.Get("ncnnGpus")} -f {InterpolateUtils.lastExt} -j 4:{Config.Get("ncnnThreads")}:4"; Logger.Log("cmd.exe " + rifeNcnn.StartInfo.Arguments, true); if (!OSUtils.ShowHiddenCmd()) {