From 6cb95a2374a77d33e6a1b8616e19d06a8d474519 Mon Sep 17 00:00:00 2001 From: n00mkrad Date: Wed, 29 Sep 2021 11:22:43 +0200 Subject: [PATCH] Finished vchunk-based saving/resuming, needs some more testing --- Code/Data/InterpSettings.cs | 2 + Code/Form1.cs | 51 +++++++++-------- Code/Main/AutoEncode.cs | 6 +- Code/Main/AutoEncodeResume.cs | 104 +++++++++++++++++++++++++++++++++- Code/Main/Interpolate.cs | 16 ++++-- Code/Main/ResumeUtils.cs | 60 ++++++++++---------- 6 files changed, 175 insertions(+), 64 deletions(-) diff --git a/Code/Data/InterpSettings.cs b/Code/Data/InterpSettings.cs index 9844c79..99339b1 100644 --- a/Code/Data/InterpSettings.cs +++ b/Code/Data/InterpSettings.cs @@ -40,6 +40,8 @@ namespace Flowframes public string framesExt; public string interpExt; + public InterpSettings() { } + public InterpSettings(string inPathArg, string outPathArg, AI aiArg, Fraction inFpsDetectedArg, Fraction inFpsArg, int interpFactorArg, float itsScale, Interpolate.OutMode outModeArg, ModelCollection.ModelInfo modelArg) { inPath = inPathArg; diff --git a/Code/Form1.cs b/Code/Form1.cs index e9b50e0..38ed9ab 100644 --- a/Code/Form1.cs +++ b/Code/Form1.cs @@ -95,7 +95,7 @@ namespace Flowframes void HandleArgs() { - foreach(string arg in Program.args) + foreach (string arg in Program.args) { if (arg.StartsWith("out=")) outputTbox.Text = arg.Split('=').Last().Trim(); @@ -123,7 +123,7 @@ namespace Flowframes } - void UnlockInterpFactorIfEnabled () + void UnlockInterpFactorIfEnabled() { if (!Config.GetBool(Config.Key.allowCustomInterpFactor)) return; @@ -131,7 +131,7 @@ namespace Flowframes interpFactorCombox.DropDownStyle = ComboBoxStyle.DropDown; } - void RemovePreviewIfDisabled () + void RemovePreviewIfDisabled() { if (!Config.GetBool(Config.Key.disablePreview)) return; @@ -144,7 +144,7 @@ namespace Flowframes } public HTTabControl GetMainTabControl() { return mainTabControl; } - public TextBox GetInputFpsTextbox () { return fpsInTbox; } + public TextBox GetInputFpsTextbox() { return fpsInTbox; } public Button GetPauseBtn() { return pauseBtn; } public bool IsInFocus() { return (ActiveForm == this); } @@ -164,7 +164,7 @@ namespace Flowframes public InterpSettings GetCurrentSettings() { SetTab("interpolate"); - return new InterpSettings(inputTbox.Text.Trim(), outputTbox.Text.Trim(), GetAi(), currInFpsDetected, currInFps, + return new InterpSettings(inputTbox.Text.Trim(), outputTbox.Text.Trim(), GetAi(), currInFpsDetected, currInFps, interpFactorCombox.GetInt(), outSpeedCombox.GetInt().Clamp(1, 64), GetOutMode(), GetModel(GetAi())); } @@ -196,7 +196,8 @@ namespace Flowframes inputTbox.Text = entry.inPath; outputTbox.Text = entry.outPath; interpFactorCombox.Text = entry.interpFactor.ToString(); - aiCombox.SelectedIndex = Implementations.networks.IndexOf(entry.ai); + //aiCombox.SelectedIndex = Implementations.networks.IndexOf(entry.ai); + aiCombox.SelectedIndex = Implementations.networks.IndexOf(Implementations.networks.Where(x => x.aiName == entry.ai.aiName).FirstOrDefault()); SetOutMode(entry.outMode); } @@ -206,7 +207,7 @@ namespace Flowframes statusLabel.Text = str; } - public string GetStatus () + public string GetStatus() { return statusLabel.Text; } @@ -226,7 +227,7 @@ namespace Flowframes public long currInDuration; public long currInDurationCut; - public void UpdateInputInfo () + public void UpdateInputInfo() { string str = $"Size: {(!currInRes.IsEmpty ? $"{currInRes.Width}x{currInRes.Height}" : "Unknown")} - "; str += $"Rate: {(currInFpsDetected.GetFloat() > 0f ? $"{currInFpsDetected} ({currInFpsDetected.GetFloat()})" : "Unknown")} - "; @@ -235,7 +236,7 @@ namespace Flowframes inputInfo.Text = str; } - public void InterpolationDone () + public void InterpolationDone() { SetStatus("Done interpolating!"); @@ -243,7 +244,7 @@ namespace Flowframes CompletionAction(); } - public void CompletionAction () + public void CompletionAction() { if (Program.args.Contains("quit-when-done")) Application.Exit(); @@ -261,7 +262,7 @@ namespace Flowframes new TimeoutForm(completionAction.Text, OsUtils.Shutdown).ShowDialog(); } - public void ResetInputInfo () + public void ResetInputInfo() { currInRes = new Size(); currInFpsDetected = new Fraction(); @@ -328,7 +329,7 @@ namespace Flowframes { return AiModels.GetModels(currentAi).models[aiModel.SelectedIndex]; } - catch + catch { return null; } @@ -350,7 +351,7 @@ namespace Flowframes { int targetIndex = 0; - for(int i = 0; i < outModeCombox.Items.Count; i++) + for (int i = 0; i < outModeCombox.Items.Count; i++) { string currentItem = outModeCombox.Items[i].ToString().ToLower(); if (mode == Interpolate.OutMode.VidMkv && currentItem.Contains("mkv")) targetIndex = i; @@ -422,7 +423,7 @@ namespace Flowframes string aiName = GetAi().aiName.Replace("_", "-"); } - public void ValidateFactor () + public void ValidateFactor() { interpFactorCombox.Text = $"x{MainUiFunctions.ValidateInterpFactor(interpFactorCombox.GetInt())}"; } @@ -460,14 +461,14 @@ namespace Flowframes interpFactorCombox.Items.Add($"x{factor}"); interpFactorCombox.SelectedIndex = 0; - - if(initialized) + + if (initialized) ConfigParser.SaveComboxIndex(aiCombox); interpFactorCombox_SelectedIndexChanged(null, null); } - public void UpdateAiModelCombox () + public void UpdateAiModelCombox() { aiModel = UiUtils.LoadAiModelsIntoGui(aiModel, GetAi()); } @@ -509,7 +510,7 @@ namespace Flowframes bool start = Program.initialRun && Program.args.Contains("start"); - if (files.Length > 1) + if (files.Length > 1) { queueBtn_Click(null, null); if (BatchProcessing.currentBatchForm != null) @@ -521,11 +522,11 @@ namespace Flowframes Logger.Log("Selected video/directory: " + Path.GetFileName(files[0]), true); inputTbox.Text = files[0]; - bool resume = (IoUtils.GetAmountOfFiles(Path.Combine(files[0], Paths.resumeDir), true) > 0); - ResumeUtils.resumeNextRun = resume; + bool resume = (IoUtils.GetAmountOfFiles(Path.Combine(files[0], Paths.resumeDir), true, "*.json") > 0); + AutoEncodeResume.resumeNextRun = resume; if (resume) - ResumeUtils.LoadTempFolder(files[0]); + AutoEncodeResume.LoadTempFolder(files[0]); trimCombox.SelectedIndex = 0; @@ -536,7 +537,7 @@ namespace Flowframes private void cancelBtn_Click(object sender, EventArgs e) { DialogResult dialog = MessageBox.Show($"Are you sure you want to cancel the interpolation?", "Are you sure?", MessageBoxButtons.YesNo); - + if (dialog == DialogResult.Yes) { SetTab("interpolation"); @@ -601,7 +602,7 @@ namespace Flowframes public void UpdateStepByStepControls() { - if(stepSelector.SelectedIndex < 0) + if (stepSelector.SelectedIndex < 0) stepSelector.SelectedIndex = 0; bool stepByStep = Config.GetInt(Config.Key.processingMode) == 1; @@ -644,7 +645,7 @@ namespace Flowframes #region Quick Settings - public void SaveQuickSettings (object sender, EventArgs e) + public void SaveQuickSettings(object sender, EventArgs e) { if (!quickSettingsInitialized) return; @@ -661,7 +662,7 @@ namespace Flowframes ConfigParser.SaveGuiElement(maxFps); } - public void LoadQuickSettings (object sender = null, EventArgs e = null) + public void LoadQuickSettings(object sender = null, EventArgs e = null) { ConfigParser.LoadGuiElement(maxVidHeight); ConfigParser.LoadComboxIndex(dedupMode); diff --git a/Code/Main/AutoEncode.cs b/Code/Main/AutoEncode.cs index d5ed00d..569c392 100644 --- a/Code/Main/AutoEncode.cs +++ b/Code/Main/AutoEncode.cs @@ -45,7 +45,9 @@ namespace Flowframes.Main public static async Task MainLoop(string interpFramesPath) { - AutoEncodeResume.Reset(); + if(!AutoEncodeResume.resumeNextRun) + AutoEncodeResume.Reset(); + debug = Config.GetBool("autoEncDebug", false); try @@ -63,7 +65,7 @@ namespace Flowframes.Main unencodedFrameLines.Clear(); Logger.Log($"[AE] Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true); - int chunkNo = 1; + int chunkNo = AutoEncodeResume.encodedChunks + 1; string encFile = Path.Combine(interpFramesPath.GetParentDir(), Paths.GetFrameOrderFilename(Interpolate.current.interpFactor)); interpFramesLines = IoUtils.ReadLines(encFile).Select(x => x.Split('/').Last().Remove("'").Split('#').First()).ToArray(); // Array with frame filenames diff --git a/Code/Main/AutoEncodeResume.cs b/Code/Main/AutoEncodeResume.cs index a889f42..1eb5a9a 100644 --- a/Code/Main/AutoEncodeResume.cs +++ b/Code/Main/AutoEncodeResume.cs @@ -20,6 +20,11 @@ namespace Flowframes.Main public static int encodedChunks = 0; public static int encodedFrames = 0; + public static bool resumeNextRun; + public static string interpSettingsFilename = "settings.json"; + public static string chunksFilename = "chunks.json"; + public static string inputFramesFilename = "input-frames.json"; + public static void Reset () { processedInputFrames = new List(); @@ -31,14 +36,109 @@ namespace Flowframes.Main { string saveDir = Path.Combine(I.current.tempFolder, Paths.resumeDir); Directory.CreateDirectory(saveDir); - string chunksJsonPath = Path.Combine(saveDir, "chunks.json"); + + string chunksJsonPath = Path.Combine(saveDir, chunksFilename); Dictionary saveData = new Dictionary(); saveData.Add("encodedChunks", encodedChunks.ToString()); saveData.Add("encodedFrames", encodedFrames.ToString()); File.WriteAllText(chunksJsonPath, JsonConvert.SerializeObject(saveData, Formatting.Indented)); - string inputFramesJsonPath = Path.Combine(saveDir, "input-frames.json"); + string inputFramesJsonPath = Path.Combine(saveDir, inputFramesFilename); File.WriteAllText(inputFramesJsonPath, JsonConvert.SerializeObject(processedInputFrames, Formatting.Indented)); + + string settingsJsonPath = Path.Combine(saveDir, interpSettingsFilename); + File.WriteAllText(settingsJsonPath, JsonConvert.SerializeObject(I.current, Formatting.Indented)); + } + + public static void LoadTempFolder(string tempFolderPath) + { + try + { + Logger.Log($"Resume: Loading temp folder"); + string resumeFolderPath = Path.Combine(tempFolderPath, Paths.resumeDir); + string settingsJsonPath = Path.Combine(resumeFolderPath, interpSettingsFilename); + InterpSettings interpSettings = JsonConvert.DeserializeObject(File.ReadAllText(settingsJsonPath)); + Program.mainForm.LoadBatchEntry(interpSettings); + Logger.Log($"Resume: Loaded temp folder"); + } + catch(Exception e) + { + Logger.Log($"Failed to load resume data: {e.Message}\n{e.StackTrace}"); + resumeNextRun = false; + } + } + + public static async Task PrepareResumedRun() // Remove already interpolated frames, return true if interpolation should be skipped + { + if (!resumeNextRun) return false; + + try + { + string chunkJsonPath = Path.Combine(I.current.tempFolder, Paths.resumeDir, chunksFilename); + string inFramesJsonPath = Path.Combine(I.current.tempFolder, Paths.resumeDir, inputFramesFilename); + + dynamic chunksData = JsonConvert.DeserializeObject(File.ReadAllText(chunkJsonPath)); + encodedChunks = chunksData.encodedChunks; + encodedFrames = chunksData.encodedFrames; + + List processedInputFrames = JsonConvert.DeserializeObject>(File.ReadAllText(inFramesJsonPath)); + int uniqueInputFrames = processedInputFrames.Distinct().Count(); + + Logger.Log($"Preparing resumed run - Already encoded {encodedFrames} frames in {encodedChunks} chunks, processed {uniqueInputFrames} input frames"); + + foreach (string inputFrameName in processedInputFrames) + { + string inputFrameFullPath = Path.Combine(I.current.tempFolder, Paths.framesDir, inputFrameName); + IoUtils.TryDeleteIfExists(inputFrameFullPath); + } + + int inputFramesLeft = IoUtils.GetAmountOfFiles(Path.Combine(I.current.tempFolder, Paths.framesDir), false); + + Logger.Log($"Deleted already processed input frames - {inputFramesLeft} left to interpolate"); + + if(inputFramesLeft < 2) + { + string videoChunksFolder = Path.Combine(I.current.tempFolder, Paths.chunksDir); + + if(IoUtils.GetAmountOfFiles(videoChunksFolder, true, "*.*") > 0) + { + Logger.Log($"No more frames left to interpolate - Merging existing video chunks instead."); + await Export.ChunksToVideos(I.current.tempFolder, videoChunksFolder, I.current.outPath); + await I.Done(); + } + else + { + I.Cancel("There are no more frames left to interpolate in this temp folder!"); + } + + return true; + } + + return false; + } + catch (Exception e) + { + Logger.Log($"Failed to prepare resumed run: {e.Message}\n{e.StackTrace}"); + I.Cancel("Failed to resume interpolation. Check the logs for details."); + resumeNextRun = false; + return true; + } + + // string stateFilepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, resumeFilename); + // ResumeState state = new ResumeState(File.ReadAllText(stateFilepath)); + // + // string fileMapFilepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, filenameMapFilename); + // List inputFrameLines = File.ReadAllLines(fileMapFilepath).Where(l => l.Trim().Length > 3).ToList(); + // List inputFrames = inputFrameLines.Select(l => Path.Combine(I.current.framesFolder, l.Split('|')[1])).ToList(); + // + // for (int i = 0; i < state.interpolatedInputFrames; i++) + // { + // IoUtils.TryDeleteIfExists(inputFrames[i]); + // if (i % 1000 == 0) await Task.Delay(1); + // } + // + // Directory.Move(I.current.interpFolder, I.current.interpFolder + Paths.prevSuffix); // Move existing interp frames + // Directory.CreateDirectory(I.current.interpFolder); // Re-create empty interp folder } } } diff --git a/Code/Main/Interpolate.cs b/Code/Main/Interpolate.cs index 9c67bf0..dec99d3 100644 --- a/Code/Main/Interpolate.cs +++ b/Code/Main/Interpolate.cs @@ -38,7 +38,7 @@ namespace Flowframes Program.mainForm.SetWorking(true); if (!Utils.InputIsValid(current.inPath, current.outPath, current.inFps, current.interpFactor, current.outMode)) return; // General input checks if (!Utils.CheckAiAvailable(current.ai, current.model)) return; // Check if selected AI pkg is installed - if (!ResumeUtils.resumeNextRun && !Utils.CheckDeleteOldTempFolder()) return; // Try to delete temp folder if an old one exists + if (!AutoEncodeResume.resumeNextRun && !Utils.CheckDeleteOldTempFolder()) return; // Try to delete temp folder if an old one exists if (!Utils.CheckPathValid(current.inPath)) return; // Check if input path/file is valid if (!(await Utils.CheckEncoderValid())) return; // Check NVENC compat Utils.ShowWarnings(current.interpFactor, current.ai); @@ -46,7 +46,7 @@ namespace Flowframes current.stepByStep = false; Program.mainForm.SetStatus("Starting..."); - if (!ResumeUtils.resumeNextRun) + if (!AutoEncodeResume.resumeNextRun) { await GetFrames(); if (canceled) return; @@ -55,7 +55,8 @@ namespace Flowframes } if (canceled) return; - await ResumeUtils.PrepareResumedRun(); + bool skip = await AutoEncodeResume.PrepareResumedRun(); + if (skip || canceled) return; //Task.Run(() => Utils.DeleteInterpolatedInputFrames()); await RunAi(current.interpFolder, current.ai); if (canceled) return; @@ -67,14 +68,19 @@ namespace Flowframes if (Config.GetBool(Config.Key.keepTempFolder)) await Task.Run(async () => { await FrameRename.Unrename(); }); + await Done(); + } + + public static async Task Done () + { await Cleanup(); Program.mainForm.SetWorking(false); Logger.Log("Total processing time: " + FormatUtils.Time(sw.Elapsed)); sw.Stop(); - if(!BatchProcessing.busy) + if (!BatchProcessing.busy) OsUtils.ShowNotificationIfInBackground("Flowframes", $"Finished interpolation after {FormatUtils.Time(sw.Elapsed)}."); - + Program.mainForm.InterpolationDone(); } diff --git a/Code/Main/ResumeUtils.cs b/Code/Main/ResumeUtils.cs index bc54dcf..487a4e1 100644 --- a/Code/Main/ResumeUtils.cs +++ b/Code/Main/ResumeUtils.cs @@ -16,7 +16,7 @@ namespace Flowframes.Main class ResumeUtils { - public static bool resumeNextRun; + // public static bool resumeNextRun; public static float timeBetweenSaves = 10; public static int minFrames = 100; @@ -72,36 +72,36 @@ namespace Flowframes.Main File.WriteAllText(filepath, I.current.Serialize()); } - public static void LoadTempFolder (string tempFolderPath) - { - string resumeFolderPath = Path.Combine(tempFolderPath, Paths.resumeDir); - string interpSettingsPath = Path.Combine(resumeFolderPath, interpSettingsFilename); - InterpSettings interpSettings = new InterpSettings(File.ReadAllText(interpSettingsPath)); - Program.mainForm.LoadBatchEntry(interpSettings); - } + // public static void LoadTempFolder (string tempFolderPath) + // { + // string resumeFolderPath = Path.Combine(tempFolderPath, Paths.resumeDir); + // string interpSettingsPath = Path.Combine(resumeFolderPath, interpSettingsFilename); + // InterpSettings interpSettings = new InterpSettings(File.ReadAllText(interpSettingsPath)); + // Program.mainForm.LoadBatchEntry(interpSettings); + // } - public static async Task PrepareResumedRun () - { - if (!resumeNextRun) return; - - string stateFilepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, resumeFilename); - ResumeState state = new ResumeState(File.ReadAllText(stateFilepath)); - - string fileMapFilepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, filenameMapFilename); - List inputFrameLines = File.ReadAllLines(fileMapFilepath).Where(l => l.Trim().Length > 3).ToList(); - List inputFrames = inputFrameLines.Select(l => Path.Combine(I.current.framesFolder, l.Split('|')[1])).ToList(); - - for (int i = 0; i < state.interpolatedInputFrames; i++) - { - IoUtils.TryDeleteIfExists(inputFrames[i]); - if (i % 1000 == 0) await Task.Delay(1); - } - - Directory.Move(I.current.interpFolder, I.current.interpFolder + Paths.prevSuffix); // Move existing interp frames - Directory.CreateDirectory(I.current.interpFolder); // Re-create empty interp folder - - LoadFilenameMap(); - } + // public static async Task PrepareResumedRun () + // { + // if (!resumeNextRun) return; + // + // string stateFilepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, resumeFilename); + // ResumeState state = new ResumeState(File.ReadAllText(stateFilepath)); + // + // string fileMapFilepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, filenameMapFilename); + // List inputFrameLines = File.ReadAllLines(fileMapFilepath).Where(l => l.Trim().Length > 3).ToList(); + // List inputFrames = inputFrameLines.Select(l => Path.Combine(I.current.framesFolder, l.Split('|')[1])).ToList(); + // + // for (int i = 0; i < state.interpolatedInputFrames; i++) + // { + // IoUtils.TryDeleteIfExists(inputFrames[i]); + // if (i % 1000 == 0) await Task.Delay(1); + // } + // + // Directory.Move(I.current.interpFolder, I.current.interpFolder + Paths.prevSuffix); // Move existing interp frames + // Directory.CreateDirectory(I.current.interpFolder); // Re-create empty interp folder + // + // LoadFilenameMap(); + // } static void LoadFilenameMap() {