diff --git a/Code/AudioVideo/FFmpegCommands.cs b/Code/AudioVideo/FFmpegCommands.cs index 4382f1a..0f70e0a 100644 --- a/Code/AudioVideo/FFmpegCommands.cs +++ b/Code/AudioVideo/FFmpegCommands.cs @@ -58,7 +58,7 @@ namespace Flowframes public static async Task ExtractSingleFrame(string inputFile, string outputPath, int frameNum, bool hdr, bool delSrc) { string hdrStr = hdr ? hdrFilter : ""; - string args = $"-i {inputFile.Wrap()} {hdrStr }-vf \"select=eq(n\\,{frameNum})\" -vframes 1 {outputPath.Wrap()}"; + string args = $"-i {inputFile.Wrap()} {pngComprArg} {hdrStr }-vf \"select=eq(n\\,{frameNum})\" -vframes 1 {outputPath.Wrap()}"; await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden); if (delSrc) DeleteSource(inputFile); diff --git a/Code/IO/IOUtils.cs b/Code/IO/IOUtils.cs index 092987e..1d054f8 100644 --- a/Code/IO/IOUtils.cs +++ b/Code/IO/IOUtils.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -435,5 +436,17 @@ namespace Flowframes.IO return false; } } + + public static string GetFileMd5 (string filename) + { + using (var md5 = MD5.Create()) + { + using (var stream = File.OpenRead(filename)) + { + var hash = md5.ComputeHash(stream); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + } + } } } diff --git a/Code/Main/AutoEncode.cs b/Code/Main/AutoEncode.cs index 407d6dc..4cd2c6d 100644 --- a/Code/Main/AutoEncode.cs +++ b/Code/Main/AutoEncode.cs @@ -12,8 +12,8 @@ namespace Flowframes.Main { static string interpFramesFolder; static string videoChunksFolder; - static int chunkSize = 125; // Encode every n frames - static int safetyBufferFrames = 25; + public static int chunkSize = 125; // Encode every n frames + public static int safetyBufferFrames = 25; public static List encodedFrames = new List(); public static List unencodedFrames = new List(); @@ -86,7 +86,7 @@ namespace Flowframes.Main public static bool HasWorkToDo () { - if (Interpolate.canceled) return false; + if (Interpolate.canceled || interpFramesFolder == null) return false; return ((AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited) || encodedFrames.Count < GetInterpFramesAmount()); } diff --git a/Code/Main/Interpolate.cs b/Code/Main/Interpolate.cs index 677e116..6f28d6b 100644 --- a/Code/Main/Interpolate.cs +++ b/Code/Main/Interpolate.cs @@ -28,7 +28,8 @@ namespace Flowframes public static float currentInFps; public static float currentOutFps; public static OutMode currentOutMode; - + public static bool currentInputIsFrames; + public static bool currentlyUsingAutoEnc; public static int lastInterpFactor; public static string lastInputPath; public static string nextOutPath; @@ -59,10 +60,11 @@ namespace Flowframes if(!Utils.CheckPathValid(inPath)) return; // Check if input path/file is valid Utils.PathAsciiCheck(inPath, outDir); lastAi = ai; + currentInputIsFrames = IOUtils.IsPathDirectory(inPath); Program.mainForm.SetStatus("Starting..."); Program.mainForm.SetWorking(true); await Task.Delay(10); - if (!IOUtils.IsPathDirectory(inPath)) // Input is video - extract frames first + if (!currentInputIsFrames) // Input is video - extract frames first await ExtractFrames(inPath, currentFramesPath); else IOUtils.Copy(inPath, currentFramesPath); @@ -81,7 +83,8 @@ namespace Flowframes await RunAi(interpFramesDir, targetFrameCount, tilesize, ai); if (canceled) return; Program.mainForm.SetProgress(100); - //await CreateVideo.FramesToVideo(interpFramesDir, nextOutPath, outMode); // TODO: DISABLE NORMAL ENCODING IF PARALLEL ENC WAS USED + if(!currentlyUsingAutoEnc) + await CreateVideo.FramesToVideo(interpFramesDir, nextOutPath, outMode); Cleanup(interpFramesDir); Program.mainForm.SetWorking(false); Logger.Log("Total processing time: " + FormatUtils.Time(sw.Elapsed)); @@ -145,7 +148,7 @@ namespace Flowframes Cancel("Failed to extract frames from input video!"); } - string hasPreprocessedFile = Path.Combine(currentTempDir, ".preprocessed"); + string hasPreprocessedFile = Path.Combine(currentTempDir, ".preprocessed"); // TODO: DUMP THIS WHOLE IDEA if (File.Exists(hasPreprocessedFile)) return; if (Config.GetInt("dedupMode") == 1) @@ -175,6 +178,7 @@ namespace Flowframes public static async Task RunAi(string outpath, int targetFrames, int tilesize, AI ai) { + currentlyUsingAutoEnc = IOUtils.GetAmountOfFiles(currentFramesPath, false) >= (AutoEncode.chunkSize + AutoEncode.safetyBufferFrames) * 1.2f; Directory.CreateDirectory(outpath); List tasks = new List(); @@ -191,7 +195,8 @@ namespace Flowframes if (ai.aiName == Networks.rifeNcnn.aiName) tasks.Add(AiProcess.RunRifeNcnnMulti(currentFramesPath, outpath, tilesize, interpFactor)); - tasks.Add(AutoEncode.MainLoop(outpath)); + if(currentlyUsingAutoEnc) + tasks.Add(AutoEncode.MainLoop(outpath)); await Task.WhenAll(tasks); } diff --git a/Code/Main/InterpolateSteps.cs b/Code/Main/InterpolateSteps.cs index 7dd6286..6d70331 100644 --- a/Code/Main/InterpolateSteps.cs +++ b/Code/Main/InterpolateSteps.cs @@ -60,6 +60,8 @@ namespace Flowframes.Main if (string.IsNullOrWhiteSpace(currentFramesPath)) currentFramesPath = Path.Combine(currentTempDir, Paths.framesDir); + + currentInterpFramesDir = Path.Combine(currentTempDir, Paths.interpDir); } public static async Task ExtractSceneChanges () @@ -106,14 +108,18 @@ namespace Flowframes.Main public static async Task DoInterpolate () { + IOUtils.TryDeleteIfExists(currentInterpFramesDir); + foreach (string ini in Directory.GetFiles(currentTempDir, "*.ini", SearchOption.TopDirectoryOnly)) + IOUtils.TryDeleteIfExists(ini); + await PostProcessFrames(true); string interpFramesDir = Path.Combine(currentTempDir, Paths.interpDir); - if (!IOUtils.TryDeleteIfExists(interpFramesDir)) - { - InterpolateUtils.ShowMessage("Failed to delete old \"interpolated-frames folder\" - Make sure none of the files are opened in another program!", "Error"); - return; - } + // if (!IOUtils.TryDeleteIfExists(interpFramesDir)) + // { + // InterpolateUtils.ShowMessage("Failed to delete old \"interpolated-frames folder\" - Make sure none of the files are opened in another program!", "Error"); + // return; + // } lastInterpFactor = interpFactor; int frames = IOUtils.GetAmountOfFiles(currentFramesPath, false, "*.png"); int targetFrameCount = frames * lastInterpFactor; @@ -127,7 +133,6 @@ namespace Flowframes.Main public static async Task CreateOutputVid () { - currentInterpFramesDir = Path.Combine(currentTempDir, Paths.interpDir); string outPath = Path.Combine(currentOutPath, Path.GetFileNameWithoutExtension(currentInPath) + IOUtils.GetAiSuffix(currentAi, lastInterpFactor) + InterpolateUtils.GetExt(currentOutMode)); await CreateVideo.FramesToVideo(currentInterpFramesDir, outPath, currentOutMode); } diff --git a/Code/Main/VfrDedupe.cs b/Code/Main/VfrDedupe.cs index 143c4fc..a725e12 100644 --- a/Code/Main/VfrDedupe.cs +++ b/Code/Main/VfrDedupe.cs @@ -47,7 +47,11 @@ namespace Flowframes.Main string scnFramesPath = Path.Combine(framesPath.GetParentDir(), Paths.scenesDir); string interpPath = Paths.interpDir; // framesPath.Replace(@"\", "/") + "-interpolated"; - List sceneFrames = Directory.GetFiles(scnFramesPath).Select(file => Path.GetFileName(file)).ToList(); + List sceneFrames = new List(); + Logger.Log($"Scn path {scnFramesPath} exists: {Directory.Exists(scnFramesPath)}"); + if (Directory.Exists(scnFramesPath)) + sceneFrames = Directory.GetFiles(scnFramesPath).Select(file => Path.GetFileName(file)).ToList(); + Logger.Log($"Found {sceneFrames.Count} scn frames"); int lastFrameDuration = 1; diff --git a/Code/OS/AiProcess.cs b/Code/OS/AiProcess.cs index 5a6807f..f2a49f6 100644 --- a/Code/OS/AiProcess.cs +++ b/Code/OS/AiProcess.cs @@ -25,6 +25,7 @@ namespace Flowframes static void AiStarted (Process proc, int startupTimeMs, string defaultExt = "png") { + SwapFilenames(false); lastStartupTimeMs = startupTimeMs; InterpolateUtils.lastExt = defaultExt; if (Config.GetBool("jpegInterps")) InterpolateUtils.lastExt = "jpg"; @@ -37,12 +38,32 @@ namespace Flowframes { Program.mainForm.SetProgress(100); string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}"; - if (AutoEncode.HasWorkToDo()) + if (Interpolate.currentlyUsingAutoEnc && AutoEncode.HasWorkToDo()) logStr += " - Waiting for encoding to finish..."; Logger.Log(logStr); processTime.Stop(); } + static Dictionary filenameMap = new Dictionary(); // TODO: Store on disk instead for crashes? + public static void SwapFilenames (bool restore) // Renames files with a counter or restores original timecode names via MD5/filename mapping + { + Stopwatch filenameSwapSw = new Stopwatch(); + if (!restore) // Rename with counter and store original names + { + filenameSwapSw.Start(); + filenameMap.Clear(); + foreach (string file in Directory.GetFiles(Path.Combine(Interpolate.currentFramesPath))) + filenameMap.Add(Path.GetFileName(file), IOUtils.GetFileMd5(file)); + Logger.Log($"Stored file/md5 pairs in {filenameSwapSw.ElapsedMilliseconds}ms"); + } + else + { + filenameSwapSw.Start(); + // add restore code! + Logger.Log($"Restored original file/md5 pairs in {filenameSwapSw.ElapsedMilliseconds}ms"); + } + } + public static async Task RunDainNcnn(string framesPath, string outPath, int targetFrames, int tilesize) { string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()} -n {targetFrames} -t {tilesize} -g {Config.Get("ncnnGpus")}"; @@ -137,7 +158,7 @@ namespace Flowframes public static async Task RunRifeCuda(string framesPath, int interpFactor) { string script = "interp-parallel.py"; - if(Config.GetInt("rifeMode") == 0 || IOUtils.GetAmountOfFiles(framesPath, false) < 10) + if(Config.GetInt("rifeMode") == 0 || IOUtils.GetAmountOfFiles(framesPath, false) < 6) script = "interp-basic.py"; string rifeDir = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.rifeCuda.fileName)); @@ -163,33 +184,6 @@ namespace Flowframes AiFinished("RIFE"); } - public static async Task RunRifeNcnn (string framesPath, string outPath, int interpFactor, int tilesize) - { - string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()} -t {tilesize} -g {Config.Get("ncnnGpus")} -f {InterpolateUtils.lastExt} -j 4:{Config.Get("ncnnThreads")}:4"; - Process rifeNcnn = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd()); - AiStarted(rifeNcnn, 750); - rifeNcnn.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeNcnn).Wrap()} & rife-ncnn-vulkan.exe {args}"; - Logger.Log("Running RIFE...", false); - Logger.Log("cmd.exe " + rifeNcnn.StartInfo.Arguments, true); - if (!OSUtils.ShowHiddenCmd()) - { - rifeNcnn.OutputDataReceived += (sender, outLine) => { LogOutput("[O] " + outLine.Data, "rife-ncnn-log.txt"); }; - rifeNcnn.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "rife-ncnn-log.txt"); }; - } - rifeNcnn.Start(); - if (!OSUtils.ShowHiddenCmd()) - { - rifeNcnn.BeginOutputReadLine(); - rifeNcnn.BeginErrorReadLine(); - } - while (!rifeNcnn.HasExited) await Task.Delay(1); - - if (Interpolate.canceled) return; - Magick.MagickDedupe.ZeroPadDir(outPath, InterpolateUtils.lastExt, 8); - - AiFinished("RIFE"); - } - public static async Task RunRifeNcnnMulti(string framesPath, string outPath, int tilesize, int times) { processTimeMulti.Restart(); diff --git a/Code/UI/MainUiFunctions.cs b/Code/UI/MainUiFunctions.cs index 5dc53c5..449b15f 100644 --- a/Code/UI/MainUiFunctions.cs +++ b/Code/UI/MainUiFunctions.cs @@ -36,20 +36,42 @@ namespace Flowframes.UI else Logger.Log($"Video FPS: {fpsStr} - Total Number Of Frames: {FFmpegCommands.GetFrameCount(path)}"); await Task.Delay(10); - Size res = FFmpegCommands.GetSize(path); - Logger.Log($"Video Resolution: {res.Width}x{res.Height}"); + await PrintResolution(path); MagickDedupe.ClearCache(); await Task.Delay(10); InterpolateUtils.SetPreviewImg(await GetThumbnail(path)); } - static async Task GetThumbnail (string videoPath) + static async Task PrintResolution (string path) + { + Size res = new Size(); + if (!IOUtils.IsPathDirectory(path)) // If path is video + { + res = FFmpegCommands.GetSize(path); + } + else // Path is frame folder + { + Image thumb = await GetThumbnail(path); + res = new Size(thumb.Width, thumb.Height); + } + if (res.Width > 1 && res.Height > 1) + Logger.Log($"Input Resolution: {res.Width}x{res.Height}"); + } + + static async Task GetThumbnail (string path) { string imgOnDisk = Path.Combine(Paths.GetDataPath(), "thumb-temp.png"); try { - await FFmpegCommands.ExtractSingleFrame(videoPath, imgOnDisk, 1, false, false); - return IOUtils.GetImage(imgOnDisk); + if (!IOUtils.IsPathDirectory(path)) // If path is video - Extract first frame + { + await FFmpegCommands.ExtractSingleFrame(path, imgOnDisk, 1, false, false); + return IOUtils.GetImage(imgOnDisk); + } + else // Path is frame folder - Get first frame + { + return IOUtils.GetImage(Directory.GetFiles(path)[0]); + } } catch (Exception e) {