diff --git a/Code/Magick/Dedupe.cs b/Code/Magick/Dedupe.cs index 3ec2384..ccb3abc 100644 --- a/Code/Magick/Dedupe.cs +++ b/Code/Magick/Dedupe.cs @@ -9,6 +9,7 @@ using ImageMagick; using Flowframes.OS; using Flowframes.Data; using System.Drawing; +using Paths = Flowframes.IO.Paths; namespace Flowframes.Magick { @@ -205,7 +206,7 @@ namespace Flowframes.Magick return bufferSize; } - public static async Task CreateDupesFileMpdecimate (string framesPath, int lastFrameNum) + public static async Task CreateDupesFile (string framesPath, int lastFrameNum) { string infoFile = Path.Combine(framesPath.GetParentDir(), $"dupes.ini"); string fileContent = ""; @@ -221,6 +222,9 @@ namespace Flowframes.Magick int diff = frameNum2 - frameNum1; int dupes = diff - 1; + //if (File.Exists(Path.Combine(framesPath.GetParentDir(), Paths.scenesDir, frameFiles[i].Name))) + // dupes = 0; + fileContent += $"{Path.GetFileNameWithoutExtension(frameFiles[i].Name)}:{dupes}\n"; } diff --git a/Code/Main/AutoEncode.cs b/Code/Main/AutoEncode.cs index 5b56566..41594d2 100644 --- a/Code/Main/AutoEncode.cs +++ b/Code/Main/AutoEncode.cs @@ -32,85 +32,98 @@ namespace Flowframes.Main public static async Task MainLoop(string interpFramesPath) { - UpdateChunkAndBufferSizes(); - - interpFramesFolder = interpFramesPath; - videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir); - if (Interpolate.currentlyUsingAutoEnc) - Directory.CreateDirectory(videoChunksFolder); - - encodedFrameLines.Clear(); - unencodedFrameLines.Clear(); - - Logger.Log($"Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true); - int videoIndex = 1; - string encFile = Path.Combine(interpFramesPath.GetParentDir(), $"vfr-{Interpolate.current.interpFactor}x.ini"); - interpFramesLines = IOUtils.ReadLines(encFile).Select(x => x.Split('/').Last().Remove("'")).ToArray(); // Array with frame filenames - - while (!Interpolate.canceled && GetInterpFramesAmount() < 2) - await Task.Delay(1000); - - while (HasWorkToDo()) // Loop while proc is running and not all frames have been encoded + try { - if (Interpolate.canceled) return; + UpdateChunkAndBufferSizes(); - if (paused) - { - await Task.Delay(100); - continue; - } - - //Logger.Log($"{unencodedFrameLines.Count} unencoded frame lines, {encodedFrameLines.Count} encoded frame lines", true, false, "ffmpeg"); + interpFramesFolder = interpFramesPath; + videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir); + if (Interpolate.currentlyUsingAutoEnc) + Directory.CreateDirectory(videoChunksFolder); + encodedFrameLines.Clear(); unencodedFrameLines.Clear(); - for(int vfrLine = 0; vfrLine < interpFramesLines.Length; vfrLine++) + + Logger.Log($"Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true); + int videoIndex = 1; + string encFile = Path.Combine(interpFramesPath.GetParentDir(), $"vfr-{Interpolate.current.interpFactor}x.ini"); + interpFramesLines = IOUtils.ReadLines(encFile).Select(x => x.Split('/').Last().Remove("'").Split('#').First()).ToArray(); // Array with frame filenames + + while (!Interpolate.canceled && GetInterpFramesAmount() < 2) { - if (File.Exists(Path.Combine(interpFramesPath, interpFramesLines[vfrLine])) && !encodedFrameLines.Contains(vfrLine)) - unencodedFrameLines.Add(vfrLine); + // Logger.Log("autoenc waiting for 2s because GetInterpFramesAmount() < 2"); + await Task.Delay(2000); } - bool aiRunning = !AiProcess.currentAiProcess.HasExited; - - if (unencodedFrameLines.Count >= (chunkSize + safetyBufferFrames) || !aiRunning) // Encode every n frames, or after process has exited + while (HasWorkToDo()) // Loop while proc is running and not all frames have been encoded { - busy = true; + if (Interpolate.canceled) return; - List frameLinesToEncode = aiRunning ? unencodedFrameLines.Take(chunkSize).ToList() : unencodedFrameLines; // Take all remaining frames if process is done - //Logger.Log($"{unencodedFrameLines.Count} unencoded frame lines, {IOUtils.GetAmountOfFiles(interpFramesFolder, false)} frames in interp folder", true, false, "ffmpeg"); - - string outpath = Path.Combine(videoChunksFolder, "chunks", $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}"); - int firstFrameNum = frameLinesToEncode[0]; - Logger.Log($"Encoding Chunk #{videoIndex} to '{outpath}' using {Path.GetFileName(interpFramesLines[frameLinesToEncode.First()])} through {Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))}", true, false, "ffmpeg"); - await CreateVideo.EncodeChunk(outpath, Interpolate.current.outMode, firstFrameNum, frameLinesToEncode.Count); - - if(Interpolate.canceled) return; - - if (Config.GetInt("autoEncMode") == 2) + if (paused) { - foreach (int frame in frameLinesToEncode) - { - if(!FrameIsStillNeeded(interpFramesLines[frame], frame)) // Make sure frames are no longer needed (e.g. for dupes) before deleting! - { - string framePath = Path.Combine(interpFramesPath, interpFramesLines[frame]); - File.WriteAllText(framePath, "THIS IS A DUMMY FILE - DO NOT DELETE ME"); // Overwrite to save space without breaking progress counter - } - } + Logger.Log("autoenc paused"); + await Task.Delay(100); + continue; } - encodedFrameLines.AddRange(frameLinesToEncode); - //Logger.Log($"Adding {frameLinesToEncode.Count} frameLinesToEncode to encodedFrameLines, new count is {encodedFrameLines.Count}"); + unencodedFrameLines.Clear(); + for (int vfrLine = 0; vfrLine < interpFramesLines.Length; vfrLine++) + { + if (File.Exists(Path.Combine(interpFramesPath, interpFramesLines[vfrLine])) && !encodedFrameLines.Contains(vfrLine)) + unencodedFrameLines.Add(vfrLine); + } - Logger.Log("Done Encoding Chunk #" + videoIndex, true, false, "ffmpeg"); - videoIndex++; - busy = false; + // Logger.Log($"{unencodedFrameLines.Count} unencoded frame lines, {encodedFrameLines.Count} encoded frame lines", false, false, "ffmpeg"); + + bool aiRunning = !AiProcess.currentAiProcess.HasExited; + + if (unencodedFrameLines.Count > 0 && (unencodedFrameLines.Count >= (chunkSize + safetyBufferFrames) || !aiRunning)) // Encode every n frames, or after process has exited + { + busy = true; + + Logger.Log("unencodedFrameLines: " + unencodedFrameLines.Count, true, false, "autoenc"); + List frameLinesToEncode = aiRunning ? unencodedFrameLines.Take(chunkSize).ToList() : unencodedFrameLines; // Take all remaining frames if process is done + Logger.Log("frameLinesToEncode: " + frameLinesToEncode.Count, true, false, "autoenc"); + + string outpath = Path.Combine(videoChunksFolder, "chunks", $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}"); + int firstFrameNum = frameLinesToEncode[0]; + Logger.Log($"Encoding Chunk #{videoIndex} to '{outpath}' using {Path.GetFileName(interpFramesLines[frameLinesToEncode.First()])} through {Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))}", true, false, "ffmpeg"); + await CreateVideo.EncodeChunk(outpath, Interpolate.current.outMode, firstFrameNum, frameLinesToEncode.Count); + + if (Interpolate.canceled) return; + + if (Config.GetInt("autoEncMode") == 2) + { + foreach (int frame in frameLinesToEncode) + { + if (!FrameIsStillNeeded(interpFramesLines[frame], frame)) // Make sure frames are no longer needed (e.g. for dupes) before deleting! + { + string framePath = Path.Combine(interpFramesPath, interpFramesLines[frame]); + File.WriteAllText(framePath, "THIS IS A DUMMY FILE - DO NOT DELETE ME"); // Overwrite to save space without breaking progress counter + } + } + } + + encodedFrameLines.AddRange(frameLinesToEncode); + //Logger.Log($"Adding {frameLinesToEncode.Count} frameLinesToEncode to encodedFrameLines, new count is {encodedFrameLines.Count}"); + + Logger.Log("Done Encoding Chunk #" + videoIndex, true, false, "ffmpeg"); + videoIndex++; + busy = false; + } + await Task.Delay(50); } - await Task.Delay(50); + + if (Interpolate.canceled) return; + + IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back + await CreateVideo.ChunksToVideos(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outFilename); + } + catch (Exception e) + { + Logger.Log($"AutoEnc Error: {e.Message}. Stack Trace:\n{e.StackTrace}"); + Interpolate.Cancel("Auto-Encode encountered an error."); } - - if (Interpolate.canceled) return; - - IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back - await CreateVideo.ChunksToVideos(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outFilename); } static bool FrameIsStillNeeded (string frameName, int frameIndex) @@ -123,7 +136,7 @@ namespace Flowframes.Main public static bool HasWorkToDo () { if (Interpolate.canceled || interpFramesFolder == null) return false; - //Logger.Log($"HasWorkToDo - Process Running: {(AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited)} - encodedFrameLines.Count: {encodedFrameLines.Count} - interpFramesLines.Length: {interpFramesLines.Length}"); + // Logger.Log($"HasWorkToDo - Process Running: {(AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited)} - encodedFrameLines.Count: {encodedFrameLines.Count} - interpFramesLines.Length: {interpFramesLines.Length}"); return ((AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited) || encodedFrameLines.Count < interpFramesLines.Length); } diff --git a/Code/Main/FrameTiming.cs b/Code/Main/FrameTiming.cs index 97cc9e8..6a2475a 100644 --- a/Code/Main/FrameTiming.cs +++ b/Code/Main/FrameTiming.cs @@ -217,7 +217,7 @@ namespace Flowframes.Main if (debug) Logger.Log($"Writing frame {totalFileCount} [Discarding Next]", true); //fileContent += $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n"; - fileContent = WriteFrameWithDupes(dupesAmount, fileContent, totalFileCount, interpPath, ext, debug); + fileContent = WriteFrameWithDupes(dupesAmount, fileContent, totalFileCount, interpPath, ext, debug, $"[i={i}] [{frameFiles[i].Name}] scn: discarding next [{frameFiles[i+1].Name}]"); totalFileCount++; if (debug) Logger.Log("Discarding interp frames with out num " + totalFileCount); @@ -225,7 +225,7 @@ namespace Flowframes.Main { if (debug) Logger.Log($"Writing frame {totalFileCount} which is actually repeated frame {lastNum}"); //fileContent += $"file '{interpPath}/{lastNum.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n"; - fileContent = WriteFrameWithDupes(dupesAmount, fileContent, lastNum, interpPath, ext, debug); + fileContent = WriteFrameWithDupes(dupesAmount, fileContent, lastNum, interpPath, ext, debug, $"[{inputFilenameNoExt}] scn: repeated last frame"); totalFileCount++; } @@ -233,7 +233,7 @@ namespace Flowframes.Main } else { - fileContent = WriteFrameWithDupes(dupesAmount, fileContent, totalFileCount, interpPath, ext, debug); + fileContent = WriteFrameWithDupes(dupesAmount, fileContent, totalFileCount, interpPath, ext, debug, $"[{inputFilenameNoExt}]"); totalFileCount++; } } @@ -242,10 +242,14 @@ namespace Flowframes.Main await Task.Delay(1); } - // Use average frame duration for last frame - TODO: Use real duration?? - //string durationStrLast = ((totalDuration / (totalFileCount - 1)) / timebase).ToString("0.0000000", CultureInfo.InvariantCulture); - fileContent += $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n"; - totalFileCount++; + if(debug) Logger.Log("target: " + ((frameFiles.Length * interpFactor) - (interpFactor - 1)), true); + if(debug) Logger.Log("totalFileCount: " + totalFileCount, true); + + if (totalFileCount < (frameFiles.Length * interpFactor) - (interpFactor - 1)) + { + fileContent += $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n"; + totalFileCount++; + } string finalFileContent = fileContent.Trim(); if(loop) @@ -268,12 +272,12 @@ namespace Flowframes.Main } } - static string WriteFrameWithDupes (int dupesAmount, string fileContent, int frameNum, string interpPath, string ext, bool debug) + static string WriteFrameWithDupes (int dupesAmount, string fileContent, int frameNum, string interpPath, string ext, bool debug, string note = "") { for (int writtenDupes = -1; writtenDupes < dupesAmount; writtenDupes++) // Write duplicates { if (debug) Logger.Log($"Writing frame {frameNum} (writtenDupes {writtenDupes})", true, false); - fileContent += $"file '{interpPath}/{frameNum.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n"; + fileContent += $"file '{interpPath}/{frameNum.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'{(debug ? ($"# Dupe {writtenDupes+1} {note}") : "" )}\n"; } return fileContent; } diff --git a/Code/Main/Interpolate.cs b/Code/Main/Interpolate.cs index 8289a82..c99b0cb 100644 --- a/Code/Main/Interpolate.cs +++ b/Code/Main/Interpolate.cs @@ -55,7 +55,7 @@ namespace Flowframes await PostProcessFrames(); if (canceled) return; int frames = IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png"); - int targetFrameCount = frames * current.interpFactor; + int targetFrameCount = (frames * current.interpFactor) - (current.interpFactor - 1); Utils.GetProgressByFrameAmount(current.interpFolder, targetFrameCount); if (canceled) return; Program.mainForm.SetStatus("Running AI..."); @@ -132,7 +132,7 @@ namespace Flowframes Dedupe.ClearCache(); if (Config.GetInt("dedupMode") == 2 || Config.GetInt("dedupMode") == 1) - await Dedupe.CreateDupesFileMpdecimate(current.framesFolder, currentInputFrameCount); + await Dedupe.CreateDupesFile(current.framesFolder, currentInputFrameCount); if (canceled) return; diff --git a/Code/OS/AiProcess.cs b/Code/OS/AiProcess.cs index cea5e98..d487764 100644 --- a/Code/OS/AiProcess.cs +++ b/Code/OS/AiProcess.cs @@ -49,14 +49,22 @@ namespace Flowframes logStr += " - Waiting for encoding to finish..."; Logger.Log(logStr); processTime.Stop(); + + Stopwatch timeSinceFfmpegRan = new Stopwatch(); + timeSinceFfmpegRan.Restart(); + while (Interpolate.currentlyUsingAutoEnc && Program.busy) { if (AvProcess.lastProcess != null && !AvProcess.lastProcess.HasExited && AvProcess.lastTask == AvProcess.TaskType.Encode) { + timeSinceFfmpegRan.Restart(); string lastLine = AvProcess.lastOutputFfmpeg.SplitIntoLines().Last(); Logger.Log(lastLine.Trim().TrimWhitespaces(), false, Logger.GetLastLine().Contains("frame")); } - await Task.Delay(1000); + if (timeSinceFfmpegRan.ElapsedMilliseconds > 3000) + break; + // Logger.Log($"AiProcess loop - Program.busy = {Program.busy}"); + await Task.Delay(500); } }