From 024c723fd29d99c54df69c46ca3d94b714590986 Mon Sep 17 00:00:00 2001 From: n00mkrad Date: Tue, 31 Aug 2021 00:43:22 +0200 Subject: [PATCH] AutoEnc now works with image sequences, WIP, needs more testing --- Code/IO/Config.cs | 3 +- Code/Main/AutoEncode.cs | 34 ++++++++++++---------- Code/Main/CreateVideo.cs | 55 ++++++++++++++++++++++++++--------- Code/Main/InterpolateUtils.cs | 8 ++--- Code/Media/FfmpegEncode.cs | 8 +++-- changelog.full.txt | 1 + 6 files changed, 70 insertions(+), 39 deletions(-) diff --git a/Code/IO/Config.cs b/Code/IO/Config.cs index 7f864e4..0f7753d 100644 --- a/Code/IO/Config.cs +++ b/Code/IO/Config.cs @@ -314,9 +314,8 @@ namespace Flowframes.IO autoEncBackupMode, autoEncDebug, autoEncMode, - autoEncSafeBufferFlavrCuda, + autoEncSafeBufferCuda, autoEncSafeBufferNcnn, - autoEncSafeBufferRifeCuda, aviCodec, aviColors, clearLogOnInput, diff --git a/Code/Main/AutoEncode.cs b/Code/Main/AutoEncode.cs index 8295df9..d33c366 100644 --- a/Code/Main/AutoEncode.cs +++ b/Code/Main/AutoEncode.cs @@ -36,14 +36,11 @@ namespace Flowframes.Main safetyBufferFrames = 90; - if (Interpolate.current.ai.aiName.ToUpper().Contains("NCNN")) + if (Interpolate.current.ai.backend == AI.Backend.Ncnn) safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferNcnn, 150); - if (Interpolate.current.ai.aiName == Implementations.rifeCuda.aiName) - safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferRifeCuda, 90); - - if (Interpolate.current.ai.aiName == Implementations.flavrCuda.aiName) - safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferFlavrCuda, 90); + if (Interpolate.current.ai.backend == AI.Backend.Pytorch) + safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferCuda, 90); } public static async Task MainLoop(string interpFramesPath) @@ -54,8 +51,10 @@ namespace Flowframes.Main { UpdateChunkAndBufferSizes(); + bool imgSeq = Interpolate.current.outMode.ToString().ToLower().StartsWith("img"); interpFramesFolder = interpFramesPath; videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir); + if (Interpolate.currentlyUsingAutoEnc) Directory.CreateDirectory(videoChunksFolder); @@ -63,12 +62,12 @@ namespace Flowframes.Main unencodedFrameLines.Clear(); Logger.Log($"[AE] Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true); - int videoIndex = 1; + int chunkIndex = 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 while (!Interpolate.canceled && GetInterpFramesAmount() < 2) - await Task.Delay(2000); + await Task.Delay(1000); int lastEncodedFrameNum = 0; @@ -128,12 +127,12 @@ namespace Flowframes.Main } busy = true; - string outpath = Path.Combine(videoChunksFolder, "chunks", $"{videoIndex.ToString().PadLeft(4, '0')}{FfmpegUtils.GetExt(Interpolate.current.outMode)}"); - int firstLineNum = frameLinesToEncode.First(); - int lastLineNum = frameLinesToEncode.Last(); - Logger.Log($"[AE] Encoding Chunk #{videoIndex} to '{outpath}' using line {firstLineNum} ({Path.GetFileName(interpFramesLines[firstLineNum])}) through {lastLineNum} ({Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))})", true, false, "ffmpeg"); + string outpath = Path.Combine(videoChunksFolder, "chunks", $"{chunkIndex.ToString().PadLeft(4, '0')}{FfmpegUtils.GetExt(Interpolate.current.outMode)}"); + string firstFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.First()].Trim()); + string lastFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()].Trim()); + Logger.Log($"[AE] Encoding Chunk #{chunkIndex} to using line {frameLinesToEncode.First()} ({firstFile}) through {frameLinesToEncode.Last()} ({lastFile})", true, false, "ffmpeg"); - await CreateVideo.EncodeChunk(outpath, Interpolate.current.outMode, firstLineNum, frameLinesToEncode.Count); + await CreateVideo.EncodeChunk(outpath, Interpolate.current.interpFolder, Interpolate.current.outMode, frameLinesToEncode.First(), frameLinesToEncode.Count); if (Interpolate.canceled) return; @@ -144,12 +143,12 @@ namespace Flowframes.Main encodedFrameLines.AddRange(frameLinesToEncode); - Logger.Log("[AE] Done Encoding Chunk #" + videoIndex, true, false, "ffmpeg"); + Logger.Log("[AE] Done Encoding Chunk #" + chunkIndex, true, false, "ffmpeg"); lastEncodedFrameNum = (frameLinesToEncode.Last() + 1); - videoIndex++; + chunkIndex++; - if(Config.GetInt(Config.Key.autoEncBackupMode) > 0) + if(!imgSeq && Config.GetInt(Config.Key.autoEncBackupMode) > 0) { if (aiRunning && (currentMuxTask == null || (currentMuxTask != null && currentMuxTask.IsCompleted))) currentMuxTask = Task.Run(() => CreateVideo.ChunksToVideos(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outPath, true)); @@ -174,6 +173,9 @@ namespace Flowframes.Main while (currentMuxTask != null && !currentMuxTask.IsCompleted) await Task.Delay(100); + if (imgSeq) + return; + await CreateVideo.ChunksToVideos(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outPath); } catch (Exception e) diff --git a/Code/Main/CreateVideo.cs b/Code/Main/CreateVideo.cs index 4919a45..df0945b 100644 --- a/Code/Main/CreateVideo.cs +++ b/Code/Main/CreateVideo.cs @@ -48,7 +48,6 @@ namespace Flowframes.Main return; } - await Task.Delay(10); Program.mainForm.SetStatus("Creating output video from frames..."); try @@ -88,17 +87,17 @@ namespace Flowframes.Main IoUtils.RenameExistingFolder(outputFolderPath); Logger.Log($"Exporting {desiredFormat.ToUpper()} frames to '{Path.GetFileName(outputFolderPath)}'..."); - if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move as the frames are already in the desired format + if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format await CopyOutputFrames(framesPath, framesFile, outputFolderPath, fpsLimit); - else // Encode with ffmpeg - await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, I.current.outFps, new Fraction(), desiredFormat); + else // Encode if frames are not in desired format + await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 0, I.current.outFps, new Fraction(), desiredFormat); } if (fpsLimit) { string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(true, false)); Logger.Log($"Exporting {desiredFormat.ToUpper()} frames to '{Path.GetFileName(outputFolderPath)}' (Resampled to {maxFps} FPS)..."); - await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, I.current.outFps, maxFps, desiredFormat); + await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 0, I.current.outFps, maxFps, desiredFormat); } if (!stepByStep) @@ -222,7 +221,7 @@ namespace Flowframes.Main await Loop(outPath, await GetLoopTimes()); } - public static async Task EncodeChunk(string outPath, I.OutMode mode, int firstFrameNum, int framesAmount) + public static async Task EncodeChunk(string outPath, string interpDir, I.OutMode mode, int firstFrameNum, int framesAmount) { string framesFileFull = Path.Combine(I.current.tempFolder, Paths.GetFrameOrderFilename(I.current.interpFactor)); string framesFileChunk = Path.Combine(I.current.tempFolder, Paths.GetFrameOrderFilenameChunk(firstFrameNum, firstFrameNum + framesAmount)); @@ -238,15 +237,43 @@ namespace Flowframes.Main bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0; - if (!dontEncodeFullFpsVid) - await FfmpegEncode.FramesToVideo(framesFileChunk, outPath, mode, I.current.outFps, new Fraction(), I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode - - if (fpsLimit) + if (mode.ToString().ToLower().StartsWith("img")) // Image Sequence output mode, not video { - string filename = Path.GetFileName(outPath); - string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix; - outPath = Path.Combine(newParentDir, filename); - await FfmpegEncode.FramesToVideo(framesFileChunk, outPath, mode, I.current.outFps, maxFps, I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps + //await FfmpegEncode.FramesToFrames(framesFileChunk, outPath, I.current.outFps, maxFps, Config.Get(Config.Key.imgSeqFormat).ToUpper(), AvProcess.LogMode.Hidden); + + string desiredFormat = Config.Get(Config.Key.imgSeqFormat); + string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(interpDir)[0]).Remove(".").ToUpper(); + + if (!dontEncodeFullFpsVid) + { + string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(false, false)); + int startNumber = IoUtils.GetAmountOfFiles(outputFolderPath, false) + 1; + + if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format + await CopyOutputFrames(interpDir, framesFileChunk, outputFolderPath, fpsLimit); + else // Encode if frames are not in desired format + await FfmpegEncode.FramesToFrames(framesFileChunk, outputFolderPath, startNumber, I.current.outFps, new Fraction(), desiredFormat, AvProcess.LogMode.Hidden); + } + + if (fpsLimit) + { + string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(true, false)); + int startNumber = IoUtils.GetAmountOfFiles(outputFolderPath, false) + 1; + await FfmpegEncode.FramesToFrames(framesFileChunk, outputFolderPath, startNumber, I.current.outFps, maxFps, desiredFormat, AvProcess.LogMode.Hidden); + } + } + else + { + if (!dontEncodeFullFpsVid) + await FfmpegEncode.FramesToVideo(framesFileChunk, outPath, mode, I.current.outFps, new Fraction(), I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode + + if (fpsLimit) + { + string filename = Path.GetFileName(outPath); + string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix; + outPath = Path.Combine(newParentDir, filename); + await FfmpegEncode.FramesToVideo(framesFileChunk, outPath, mode, I.current.outFps, maxFps, I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps + } } } diff --git a/Code/Main/InterpolateUtils.cs b/Code/Main/InterpolateUtils.cs index 6ff9055..6e6c78d 100644 --- a/Code/Main/InterpolateUtils.cs +++ b/Code/Main/InterpolateUtils.cs @@ -279,21 +279,21 @@ namespace Flowframes.Main { AutoEncode.UpdateChunkAndBufferSizes(); - if (!current.outMode.ToString().ToLower().Contains("vid") || current.outMode.ToString().ToLower().Contains("gif")) + if (Config.GetInt(Config.Key.cmdDebugMode) > 0) { - Logger.Log($"Not Using AutoEnc: Out Mode is not video ({current.outMode.ToString()})", true); + Logger.Log($"Not Using AutoEnc: CMD window is shown (cmdDebugMode > 0)", true); return false; } if (stepByStep && !Config.GetBool(Config.Key.sbsAllowAutoEnc)) { - Logger.Log($"Not Using AutoEnc: Using step-by-step mode, but 'sbsAllowAutoEnc' is false.", true); + Logger.Log($"Not Using AutoEnc: Using step-by-step mode, but 'sbsAllowAutoEnc' is false", true); return false; } if (!stepByStep && Config.GetInt(Config.Key.autoEncMode) == 0) { - Logger.Log($"Not Using AutoEnc: 'autoEncMode' is 0.", true); + Logger.Log($"Not Using AutoEnc: 'autoEncMode' is 0", true); return false; } diff --git a/Code/Media/FfmpegEncode.cs b/Code/Media/FfmpegEncode.cs index 07a5458..fd28f0e 100644 --- a/Code/Media/FfmpegEncode.cs +++ b/Code/Media/FfmpegEncode.cs @@ -65,11 +65,12 @@ namespace Flowframes.Media return ""; } - public static async Task FramesToFrames(string framesFile, string outDir, Fraction fps, Fraction resampleFps, string format = "png", LogMode logMode = LogMode.OnlyLastLine) + public static async Task FramesToFrames(string framesFile, string outDir, int startNo, Fraction fps, Fraction resampleFps, string format = "png", LogMode logMode = LogMode.OnlyLastLine) { Directory.CreateDirectory(outDir); string inArg = $"-f concat -i {Path.GetFileName(framesFile)}"; string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix); + format = format.ToLower(); if (Config.GetBool(Config.Key.allowSymlinkEncoding, true) && Symlinks.SymlinksAllowed()) { @@ -77,11 +78,12 @@ namespace Flowframes.Media inArg = $"-i {Path.GetFileName(framesFile) + Paths.symlinksSuffix}/%{Padding.interpFrames}d{GetConcatFileExt(framesFile)}"; } + string sn = $"-start_number {startNo}"; string rate = fps.ToString().Replace(",", "."); string vf = (resampleFps.GetFloat() < 0.1f) ? "" : $"-vf fps=fps={resampleFps}"; string compression = format == "png" ? pngCompr : "-q:v 1"; - string codec = format.ToLower() == "webp" ? "-c:v libwebp" : ""; // Specify libwebp to avoid putting all frames into single AWEBP - string args = $"-vsync 0 -r {rate} {inArg} {codec} {compression} {vf} \"{outDir}/%{Padding.interpFrames}d.{format}\""; + 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); IoUtils.TryDeleteIfExists(linksDir); } diff --git a/changelog.full.txt b/changelog.full.txt index c29b30b..ae00b01 100644 --- a/changelog.full.txt +++ b/changelog.full.txt @@ -8,6 +8,7 @@ Flowframes 1.32.0 Changelog: - Added config option to enable custom interpolation factor (if compatible) - ProRes now supports Alpha output when used with profile 4444 or 4444xq - Fixed ProRes being locked to YUV420 colors on all presets +- Fixed WEBP image sequence export not working correctly Flowframes 1.31.1 Changelog: