Update .NET 5 code

This commit is contained in:
Dankrushen
2021-02-08 15:39:10 -05:00
parent b4251f9f88
commit 8a1c7d3295
47 changed files with 1684 additions and 1061 deletions

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.MiscUtils;
@@ -28,7 +28,7 @@ namespace Flowframes.Main
public static void UpdateChunkAndBufferSizes ()
{
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.current.framesFolder, false, "*.png") * Interpolate.current.interpFactor);
chunkSize = GetChunkSize((IOUtils.GetAmountOfFiles(Interpolate.current.framesFolder, false, "*.png") * Interpolate.current.interpFactor).RoundToInt());
bool isNcnn = Interpolate.current.ai.aiName.ToUpper().Contains("NCNN");
safetyBufferFrames = isNcnn ? Config.GetInt("autoEncSafeBufferNcnn", 90) : Config.GetInt("autoEncSafeBufferCuda", 30); // Use bigger safety buffer for NCNN
}
@@ -49,7 +49,7 @@ namespace Flowframes.Main
Logger.Log($"[AutoEnc] 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");
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)
@@ -77,7 +77,6 @@ namespace Flowframes.Main
if (unencodedFrameLines.Count > 0 && (unencodedFrameLines.Count >= (chunkSize + safetyBufferFrames) || !aiRunning)) // Encode every n frames, or after process has exited
{
List<int> frameLinesToEncode = aiRunning ? unencodedFrameLines.Take(chunkSize).ToList() : unencodedFrameLines; // Take all remaining frames if process is done
string lastOfChunk = Path.Combine(interpFramesPath, interpFramesLines[frameLinesToEncode.Last()]);
@@ -90,7 +89,6 @@ namespace Flowframes.Main
busy = true;
string outpath = Path.Combine(videoChunksFolder, "chunks", $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}");
//int firstFrameNum = frameLinesToEncode[0];
int firstLineNum = frameLinesToEncode.First();
int lastLineNum = frameLinesToEncode.Last();
Logger.Log($"[AutoEnc] Encoding Chunk #{videoIndex} to '{outpath}' using line {firstLineNum} ({Path.GetFileName(interpFramesLines[firstLineNum])}) through {lastLineNum} ({Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))})", true, false, "ffmpeg");
@@ -99,7 +97,7 @@ namespace Flowframes.Main
if (Interpolate.canceled) return;
if (Config.GetInt("autoEncMode") == 2)
if (aiRunning && Config.GetInt("autoEncMode") == 2)
Task.Run(() => DeleteOldFramesAsync(interpFramesPath, frameLinesToEncode));
if (Interpolate.canceled) return;
@@ -116,8 +114,6 @@ namespace Flowframes.Main
}
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)
@@ -132,16 +128,20 @@ namespace Flowframes.Main
Logger.Log("[AutoEnc] Starting DeleteOldFramesAsync.", true, false, "ffmpeg");
Stopwatch sw = new Stopwatch();
sw.Restart();
int counter = 0;
foreach (int frame in frameLinesToEncode)
{
bool delete = !FrameIsStillNeeded(interpFramesLines[frame], frame);
if (delete) // Make sure frames are no longer needed (e.g. for dupes) before deleting!
if (!FrameIsStillNeeded(interpFramesLines[frame], frame)) // Make sure frames are no longer needed (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
await Task.Delay(1);
IOUtils.OverwriteFileWithText(framePath); // Overwrite to save space without breaking progress counter
}
if(counter % 1000 == 0)
await Task.Delay(1);
counter++;
}
Logger.Log("[AutoEnc] DeleteOldFramesAsync finished in " + FormatUtils.TimeSw(sw), true, false, "ffmpeg");

View File

@@ -13,7 +13,7 @@ using System.Windows.Forms;
using Padding = Flowframes.Data.Padding;
using i = Flowframes.Interpolate;
using System.Diagnostics;
using Flowframes.AudioVideo;
using Flowframes.Media;
namespace Flowframes.Main
{
@@ -21,13 +21,13 @@ namespace Flowframes.Main
{
static string currentOutFile; // Keeps track of the out file, in case it gets renamed (FPS limiting, looping, etc) before finishing export
public static async Task Export(string path, string outPath, i.OutMode mode)
public static async Task Export(string path, string outPath, i.OutMode mode, bool stepByStep)
{
if (!mode.ToString().ToLower().Contains("vid")) // Copy interp frames out of temp folder and skip video export for image seq export
{
try
{
await CopyOutputFrames(path, Path.GetFileNameWithoutExtension(outPath));
await CopyOutputFrames(path, Path.GetFileNameWithoutExtension(outPath), stepByStep);
}
catch(Exception e)
{
@@ -63,7 +63,7 @@ namespace Flowframes.Main
}
}
static async Task CopyOutputFrames (string framesPath, string folderName)
static async Task CopyOutputFrames (string framesPath, string folderName, bool dontMove)
{
Program.mainForm.SetStatus("Copying output frames...");
string copyPath = Path.Combine(i.current.outPath, folderName);
@@ -73,7 +73,7 @@ namespace Flowframes.Main
Stopwatch sw = new Stopwatch();
sw.Restart();
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.current.interpFactor}x.ini");
string vfrFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(i.current.interpFactor));
string[] vfrLines = IOUtils.ReadLines(vfrFile);
for (int idx = 1; idx <= vfrLines.Length; idx++)
@@ -83,7 +83,7 @@ namespace Flowframes.Main
string framePath = Path.Combine(framesPath, inFilename);
string outFilename = Path.Combine(copyPath, idx.ToString().PadLeft(Padding.interpFrames, '0')) + Path.GetExtension(framePath);
if ((idx < vfrLines.Length) && vfrLines[idx].Contains(inFilename)) // If file is re-used in the next line, copy instead of move
if (dontMove || ((idx < vfrLines.Length) && vfrLines[idx].Contains(inFilename))) // If file is re-used in the next line, copy instead of move
File.Copy(framePath, outFilename);
else
File.Move(framePath, outFilename);
@@ -100,20 +100,17 @@ namespace Flowframes.Main
static async Task Encode(i.OutMode mode, string framesPath, string outPath, float fps, float resampleFps = -1)
{
currentOutFile = outPath;
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.current.interpFactor}x.ini");
string vfrFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(i.current.interpFactor));
if (mode == i.OutMode.VidGif)
{
await FFmpegCommands.FramesToGifConcat(vfrFile, outPath, fps, true, Config.GetInt("gifColors"), resampleFps);
await FfmpegEncode.FramesToGifConcat(vfrFile, outPath, fps, true, Config.GetInt("gifColors"), resampleFps);
}
else
{
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, fps, resampleFps);
await FfmpegEncode.FramesToVideoConcat(vfrFile, outPath, mode, fps, resampleFps);
await MergeAudio(i.current.inPath, outPath);
int looptimes = GetLoopTimes();
if (looptimes > 0)
await Loop(currentOutFile, looptimes);
await Loop(currentOutFile, GetLoopTimes());
}
}
@@ -152,18 +149,15 @@ namespace Flowframes.Main
static async Task MergeChunks(string vfrFile, string outPath)
{
await FFmpegCommands.ConcatVideos(vfrFile, outPath, -1);
await FfmpegCommands.ConcatVideos(vfrFile, outPath, -1);
await MergeAudio(i.current.inPath, outPath);
int looptimes = GetLoopTimes();
if (looptimes > 0)
await Loop(outPath, looptimes);
await Loop(outPath, GetLoopTimes());
}
public static async Task EncodeChunk(string outPath, i.OutMode mode, int firstFrameNum, int framesAmount)
{
string vfrFileOriginal = Path.Combine(i.current.tempFolder, $"vfr-{i.current.interpFactor}x.ini");
string vfrFile = Path.Combine(i.current.tempFolder, $"vfr-chunk-{firstFrameNum}-{firstFrameNum + framesAmount}.ini");
string vfrFileOriginal = Path.Combine(i.current.tempFolder, Paths.GetFrameOrderFilename(i.current.interpFactor));
string vfrFile = Path.Combine(i.current.tempFolder, Paths.GetFrameOrderFilenameChunk(firstFrameNum, firstFrameNum + framesAmount));
File.WriteAllLines(vfrFile, IOUtils.ReadLines(vfrFileOriginal).Skip(firstFrameNum).Take(framesAmount));
float maxFps = Config.GetFloat("maxFps");
@@ -172,21 +166,22 @@ namespace Flowframes.Main
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt("maxFpsMode") == 0;
if(!dontEncodeFullFpsVid)
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, i.current.outFps, AvProcess.LogMode.Hidden, true); // Encode
await FfmpegEncode.FramesToVideoConcat(vfrFile, outPath, mode, i.current.outFps, AvProcess.LogMode.Hidden, true); // Encode
if (fpsLimit)
{
string filename = Path.GetFileName(outPath);
string newParentDir = outPath.GetParentDir() + "-" + maxFps.ToStringDot("0.00") + "fps";
outPath = Path.Combine(newParentDir, filename);
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, i.current.outFps, maxFps, AvProcess.LogMode.Hidden, true); // Encode with limited fps
await FfmpegEncode.FramesToVideoConcat(vfrFile, outPath, mode, i.current.outFps, maxFps, AvProcess.LogMode.Hidden, true); // Encode with limited fps
}
}
static async Task Loop(string outPath, int looptimes)
{
if (looptimes < 1 || !Config.GetBool("enableLoop")) return;
Logger.Log($"Looping {looptimes} {(looptimes == 1 ? "time" : "times")} to reach target length of {Config.GetInt("minOutVidLength")}s...");
await FFmpegCommands.LoopVideo(outPath, looptimes, Config.GetInt("loopMode") == 0);
await FfmpegCommands.LoopVideo(outPath, looptimes, Config.GetInt("loopMode") == 0);
}
static int GetLoopTimes()
@@ -194,7 +189,7 @@ namespace Flowframes.Main
int times = -1;
int minLength = Config.GetInt("minOutVidLength");
int minFrameCount = (minLength * i.current.outFps).RoundToInt();
int outFrames = i.currentInputFrameCount * i.current.interpFactor;
int outFrames = (i.currentInputFrameCount * i.current.interpFactor).RoundToInt();
if (outFrames / i.current.outFps < minLength)
times = (int)Math.Ceiling((double)minFrameCount / (double)outFrames);
times--; // Not counting the 1st play (0 loops)
@@ -213,7 +208,7 @@ namespace Flowframes.Main
audioFileBasePath = Path.Combine(i.current.tempFolder.GetParentDir(), "audio");
if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath)))
await FFmpegCommands.ExtractAudio(inputPath, audioFileBasePath); // Extract from sourceVideo to audioFile unless it already exists
await FfmpegAudioAndMetadata.ExtractAudio(inputPath, audioFileBasePath); // Extract from sourceVideo to audioFile unless it already exists
if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath)) || new FileInfo(IOUtils.GetAudioFile(audioFileBasePath)).Length < 4096)
{
@@ -221,7 +216,7 @@ namespace Flowframes.Main
return;
}
await FFmpegCommands.MergeAudioAndSubs(outVideo, IOUtils.GetAudioFile(audioFileBasePath), i.current.tempFolder); // Merge from audioFile into outVideo
await FfmpegAudioAndMetadata.MergeAudioAndSubs(outVideo, IOUtils.GetAudioFile(audioFileBasePath), i.current.tempFolder); // Merge from audioFile into outVideo
}
catch (Exception e)
{

View File

@@ -14,16 +14,17 @@ namespace Flowframes.Main
{
class FrameOrder
{
public enum Mode { CFR, VFR }
public static int timebase = 10000;
static Stopwatch benchmark = new Stopwatch();
public static async Task CreateFrameOrderFile(string framesPath, bool loopEnabled, int times)
public static async Task CreateFrameOrderFile(string framesPath, bool loopEnabled, float times)
{
Logger.Log("Generating frame order information...");
try
{
benchmark.Restart();
await CreateEncFile(framesPath, loopEnabled, times, false);
Logger.Log($"Generating frame order information... Done.", false, true);
Logger.Log($"Generated frame order info file in {benchmark.ElapsedMilliseconds} ms", true);
}
catch (Exception e)
{
@@ -45,7 +46,7 @@ namespace Flowframes.Main
}
}
public static async Task CreateEncFile (string framesPath, bool loopEnabled, int interpFactor, bool notFirstRun)
public static async Task CreateEncFile (string framesPath, bool loopEnabled, float interpFactor, bool notFirstRun)
{
if (Interpolate.canceled) return;
Logger.Log($"Generating frame order information for {interpFactor}x...", false, true);
@@ -55,7 +56,7 @@ namespace Flowframes.Main
string ext = InterpolateUtils.GetOutExt();
FileInfo[] frameFiles = new DirectoryInfo(framesPath).GetFiles($"*.png");
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{interpFactor}x.ini");
string vfrFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(interpFactor));
string fileContent = "";
string dupesFile = Path.Combine(framesPath.GetParentDir(), $"dupes.ini");
LoadDupesFile(dupesFile);
@@ -69,41 +70,32 @@ namespace Flowframes.Main
bool debug = Config.GetBool("frameOrderDebug", false);
int interpFramesAmount = (int)interpFactor; // TODO: This code won't work with fractional factors
int totalFileCount = 0;
for (int i = 0; i < (frameFiles.Length - 1); i++)
{
if (Interpolate.canceled) return;
int interpFramesAmount = interpFactor;
string inputFilenameNoExt = Path.GetFileNameWithoutExtension(frameFiles[i].Name);
int dupesAmount = dupesDict.ContainsKey(inputFilenameNoExt) ? dupesDict[inputFilenameNoExt] : 0;
if(debug) Logger.Log($"{Path.GetFileNameWithoutExtension(frameFiles[i].Name)} has {dupesAmount} dupes", true);
bool discardThisFrame = (sceneDetection && (i + 2) < frameFiles.Length && sceneFrames.Contains(Path.GetFileNameWithoutExtension(frameFiles[i + 1].Name))); // i+2 is in scene detection folder, means i+1 is ugly interp frame
// If loop is enabled, account for the extra frame added to the end for loop continuity
if (loopEnabled && i == (frameFiles.Length - 2))
interpFramesAmount = interpFramesAmount * 2;
if (debug) Logger.Log($"Writing out frames for in frame {i} which has {dupesAmount} dupes", true);
// Generate frames file lines
for (int frm = 0; frm < interpFramesAmount; frm++)
for (int frm = 0; frm < interpFramesAmount; frm++) // Generate frames file lines
{
//if (debug) Logger.Log($"Writing out frame {frm+1}/{interpFramesAmount}", true);
if (discardThisFrame) // If frame is scene cut frame
{
//if (debug) Logger.Log($"Writing frame {totalFileCount} [Discarding Next]", true);
totalFileCount++;
int lastNum = totalFileCount;
fileContent = WriteFrameWithDupes(dupesAmount, fileContent, totalFileCount, interpPath, ext, debug, $"[In: {inputFilenameNoExt}] [{((frm == 0) ? " Source " : $"Interp {frm}")}] [DiscardNext]");
//if (debug) Logger.Log("Discarding interp frames with out num " + totalFileCount);
for (int dupeCount = 1; dupeCount < interpFramesAmount; dupeCount++)
{
totalFileCount++;
if (debug) Logger.Log($"Writing frame {totalFileCount} which is actually repeated frame {lastNum}", true);
fileContent = WriteFrameWithDupes(dupesAmount, fileContent, lastNum, interpPath, ext, debug, $"[In: {inputFilenameNoExt}] [DISCARDED]");
}
@@ -116,20 +108,21 @@ namespace Flowframes.Main
}
}
if ((i + 1) % 100 == 0)
if (i % 250 == 0)
{
if (i % 1000 == 0)
Logger.Log($"Generating frame order information... {i}/{frameFiles.Length}.", false, true);
await Task.Delay(1);
}
}
// if(debug) Logger.Log("target: " + ((frameFiles.Length * interpFactor) - (interpFactor - 1)), true);
// if(debug) Logger.Log("totalFileCount: " + totalFileCount, true);
totalFileCount++;
fileContent += $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n";
string finalFileContent = fileContent.Trim();
if(loop)
finalFileContent = finalFileContent.Remove(finalFileContent.LastIndexOf("\n"));
File.WriteAllText(vfrFile, finalFileContent);
fileContent = fileContent.Remove(fileContent.LastIndexOf("\n"));
File.WriteAllText(vfrFile, fileContent);
if (notFirstRun) return; // Skip all steps that only need to be done once

View File

@@ -1,4 +1,5 @@
using Flowframes;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Magick;
@@ -25,68 +26,81 @@ namespace Flowframes
public static int currentInputFrameCount;
public static bool currentlyUsingAutoEnc;
public static InterpSettings current;
public static bool canceled = false;
static Stopwatch sw = new Stopwatch();
public static async Task Start()
{
if (!BatchProcessing.busy && Program.busy) return;
canceled = false;
if (!Utils.InputIsValid(current.inPath, current.outPath, current.outFps, current.interpFactor, current.outMode)) return; // General input checks
if (!Utils.CheckAiAvailable(current.ai)) return; // Check if selected AI pkg is installed
if (!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
Utils.PathAsciiCheck(current.inPath, current.outPath);
if (!ResumeUtils.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
Utils.PathAsciiCheck(current.outPath, "output path");
currentInputFrameCount = await Utils.GetInputFrameCountAsync(current.inPath);
current.stepByStep = false;
Program.mainForm.SetStatus("Starting...");
Program.mainForm.SetWorking(true);
await GetFrames();
if (canceled) return;
sw.Restart();
await PostProcessFrames();
if (!ResumeUtils.resumeNextRun)
{
await GetFrames();
if (canceled) return;
sw.Restart();
await PostProcessFrames(false);
}
if (canceled) return;
await ResumeUtils.PrepareResumedRun();
//Task.Run(() => Utils.DeleteInterpolatedInputFrames());
await RunAi(current.interpFolder, current.ai);
if (canceled) return;
Program.mainForm.SetProgress(100);
if(!currentlyUsingAutoEnc)
await CreateVideo.Export(current.interpFolder, current.outFilename, current.outMode);
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
Cleanup(current.interpFolder);
await CreateVideo.Export(current.interpFolder, current.outFilename, current.outMode, false);
await IOUtils.ReverseRenaming(current.framesFolder, AiProcess.filenameMap); // Get timestamps back
AiProcess.filenameMap.Clear();
await Cleanup();
Program.mainForm.SetWorking(false);
Logger.Log("Total processing time: " + FormatUtils.Time(sw.Elapsed));
sw.Stop();
Program.mainForm.SetStatus("Done interpolating!");
}
public static async Task GetFrames ()
public static async Task GetFrames (bool stepByStep = false)
{
if (!current.inputIsFrames) // Input is video - extract frames first
{
current.alpha = (Config.GetBool("enableAlpha", false) && (Path.GetExtension(current.inPath).ToLower() == ".gif"));
await ExtractFrames(current.inPath, current.framesFolder, current.alpha);
}
current.RefreshAlpha();
if (!current.inputIsFrames) // Extract if input is video, import if image sequence
await ExtractFrames(current.inPath, current.framesFolder, current.alpha, !stepByStep);
else
await FfmpegExtract.ImportImages(current.inPath, current.framesFolder, current.alpha, await Utils.GetOutputResolution(current.inPath, true));
if (current.alpha)
{
current.alpha = (Config.GetBool("enableAlpha", false) && Path.GetExtension(IOUtils.GetFilesSorted(current.inPath).First()).ToLower() == ".gif");
await FFmpegCommands.ImportImages(current.inPath, current.framesFolder, current.alpha, await Utils.GetOutputResolution(current.inPath, true));
Program.mainForm.SetStatus("Extracting transparency...");
Logger.Log("Extracting transparency... (1/2)");
await FfmpegAlpha.ExtractAlphaDir(current.framesFolder, current.framesFolder + Paths.alphaSuffix);
Logger.Log("Extracting transparency... (2/2)", false, true);
await FfmpegAlpha.RemoveAlpha(current.framesFolder, current.framesFolder);
}
}
public static async Task ExtractFrames(string inPath, string outPath, bool alpha, bool allowSceneDetect = true, bool extractAudio = true)
public static async Task ExtractFrames(string inPath, string outPath, bool alpha, bool sceneDetect)
{
if (Config.GetBool("scnDetect"))
if (sceneDetect && Config.GetBool("scnDetect"))
{
Program.mainForm.SetStatus("Extracting scenes from video...");
await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(current.tempFolder, Paths.scenesDir), current.inFps);
await FfmpegExtract.ExtractSceneChanges(inPath, Path.Combine(current.tempFolder, Paths.scenesDir), current.inFps);
await Task.Delay(10);
}
if (canceled) return;
Program.mainForm.SetStatus("Extracting frames from video...");
bool mpdecimate = Config.GetInt("dedupMode") == 2;
await FFmpegCommands.VideoToFrames(inPath, outPath, alpha, current.inFps, mpdecimate, false, await Utils.GetOutputResolution(inPath, true), false);
await FfmpegExtract.VideoToFrames(inPath, outPath, alpha, current.inFps, mpdecimate, false, await Utils.GetOutputResolution(inPath, true, true));
if (mpdecimate)
{
@@ -100,17 +114,19 @@ namespace Flowframes
if(!Config.GetBool("allowConsecutiveSceneChanges", true))
Utils.FixConsecutiveSceneFrames(Path.Combine(current.tempFolder, Paths.scenesDir), current.framesFolder);
if (extractAudio)
{
string audioFile = Path.Combine(current.tempFolder, "audio");
if (audioFile != null && !File.Exists(audioFile))
await FFmpegCommands.ExtractAudio(inPath, audioFile);
}
if (canceled) return;
Program.mainForm.SetStatus("Extracting audio from video...");
string audioFile = Path.Combine(current.tempFolder, "audio");
await FFmpegCommands.ExtractSubtitles(inPath, current.tempFolder, current.outMode);
if (audioFile != null && !File.Exists(audioFile))
await FfmpegAudioAndMetadata.ExtractAudio(inPath, audioFile);
if (canceled) return;
Program.mainForm.SetStatus("Extracting subtitles from video...");
await FfmpegAudioAndMetadata.ExtractSubtitles(inPath, current.tempFolder, current.outMode);
}
public static async Task PostProcessFrames (bool sbsMode = false)
public static async Task PostProcessFrames (bool stepByStep)
{
if (canceled) return;
@@ -136,14 +152,22 @@ namespace Flowframes
if (canceled) return;
if(current.alpha)
await Converter.ExtractAlpha(current.framesFolder, current.framesFolder + "-a");
await FrameOrder.CreateFrameOrderFile(current.framesFolder, Config.GetBool("enableLoop"), current.interpFactor);
if (canceled) return;
AiProcess.filenameMap = IOUtils.RenameCounterDirReversible(current.framesFolder, "png", 1, 8);
try
{
Dictionary<string, string> renamedFilesDict = await IOUtils.RenameCounterDirReversibleAsync(current.framesFolder, "png", 1, Padding.inputFramesRenamed);
if(stepByStep)
AiProcess.filenameMap = renamedFilesDict.ToDictionary(x => Path.GetFileName(x.Key), x => Path.GetFileName(x.Value)); // Save rel paths
}
catch (Exception e)
{
Logger.Log($"Error renaming frame files: {e.Message}");
Cancel("Error renaming frame files. Check the log for details.");
}
}
public static async Task RunAi(string outpath, AI ai, bool stepByStep = false)
@@ -162,7 +186,7 @@ namespace Flowframes
tasks.Add(AiProcess.RunRifeCuda(current.framesFolder, current.interpFactor, current.model));
if (ai.aiName == Networks.rifeNcnn.aiName)
tasks.Add(AiProcess.RunRifeNcnn(current.framesFolder, outpath, current.interpFactor, current.model));
tasks.Add(AiProcess.RunRifeNcnn(current.framesFolder, outpath, (int)current.interpFactor, current.model));
if (ai.aiName == Networks.dainNcnn.aiName)
tasks.Add(AiProcess.RunDainNcnn(current.framesFolder, outpath, current.interpFactor, current.model, Config.GetInt("dainNcnnTilesize", 512)));
@@ -186,30 +210,44 @@ namespace Flowframes
canceled = true;
Program.mainForm.SetStatus("Canceled.");
Program.mainForm.SetProgress(0);
if (Config.GetInt("processingMode") == 0 && !Config.GetBool("keepTempFolder"))
IOUtils.TryDeleteIfExists(current.tempFolder);
if (!current.stepByStep && !Config.GetBool("keepTempFolder"))
{
if(false /* IOUtils.GetAmountOfFiles(Path.Combine(current.tempFolder, Paths.resumeDir), true) > 0 */) // TODO: Uncomment for 1.23
{
DialogResult dialogResult = MessageBox.Show($"Delete the temp folder (Yes) or keep it for resuming later (No)?", "Delete temporary files?", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
IOUtils.TryDeleteIfExists(current.tempFolder);
}
else
{
IOUtils.TryDeleteIfExists(current.tempFolder);
}
}
AutoEncode.busy = false;
Program.mainForm.SetWorking(false);
Program.mainForm.SetTab("interpolation");
if(!Logger.GetLastLine().Contains("Canceled interpolation."))
Logger.Log("Canceled interpolation.");
Logger.LogIfLastLineDoesNotContainMsg("Canceled interpolation.");
if (!string.IsNullOrWhiteSpace(reason) && !noMsgBox)
Utils.ShowMessage($"Canceled:\n\n{reason}");
}
public static void Cleanup(string interpFramesDir, bool ignoreKeepSetting = false)
public static async Task Cleanup(bool ignoreKeepSetting = false, int retriesLeft = 3, bool isRetry = false)
{
if (!ignoreKeepSetting && Config.GetBool("keepTempFolder")) return;
Logger.Log("Deleting temporary files...");
if ((!ignoreKeepSetting && Config.GetBool("keepTempFolder")) || !Program.busy) return;
if (!isRetry)
Logger.Log("Deleting temporary files...");
try
{
if (Config.GetBool("keepFrames"))
IOUtils.Copy(interpFramesDir, Path.Combine(current.tempFolder.GetParentDir(), Path.GetFileName(current.tempFolder).Replace("-temp", "-interpframes")));
Directory.Delete(current.tempFolder, true);
}
catch (Exception e)
{
Logger.Log("Cleanup Error: " + e.Message, true);
if(retriesLeft > 0)
{
await Task.Delay(1000);
await Cleanup(ignoreKeepSetting, retriesLeft - 1, true);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using System;
@@ -17,18 +17,14 @@ namespace Flowframes.Main
{
public enum Step { ExtractScnChanges, ExtractFrames, Interpolate, CreateVid, Reset }
//public static string current.inPath;
//public static string currentOutPath;
//public static string current.interpFolder;
//public static AI currentAi;
//public static OutMode currentOutMode;
public static async Task Run(string step)
{
Logger.Log($"[SBS] Running step '{step}'", true);
canceled = false;
Program.mainForm.SetWorking(true);
current = Program.mainForm.GetCurrentSettings();
current.RefreshAlpha();
current.stepByStep = false;
if (!InterpolateUtils.InputIsValid(current.inPath, current.outPath, current.outFps, current.interpFactor, current.outMode)) return; // General input checks
@@ -41,9 +37,7 @@ namespace Flowframes.Main
}
if (step.Contains("Extract Frames"))
{
await GetFrames();
}
await ExtractFramesStep();
if (step.Contains("Run Interpolation"))
await DoInterpolate();
@@ -55,6 +49,7 @@ namespace Flowframes.Main
await Reset();
Program.mainForm.SetWorking(false);
Program.mainForm.SetStatus("Done running step.");
Logger.Log("Done running this step.");
}
@@ -67,11 +62,11 @@ namespace Flowframes.Main
return;
}
Program.mainForm.SetStatus("Extracting scenes from video...");
await FFmpegCommands.ExtractSceneChanges(current.inPath, scenesPath, current.inFps);
await FfmpegExtract.ExtractSceneChanges(current.inPath, scenesPath, current.inFps);
await Task.Delay(10);
}
public static async Task ExtractVideoFrames()
public static async Task ExtractFramesStep()
{
if (!IOUtils.TryDeleteIfExists(current.framesFolder))
{
@@ -82,12 +77,13 @@ namespace Flowframes.Main
currentInputFrameCount = await InterpolateUtils.GetInputFrameCountAsync(current.inPath);
AiProcess.filenameMap.Clear();
await ExtractFrames(current.inPath, current.framesFolder, false, true);
await GetFrames(true);
}
public static async Task DoInterpolate()
{
current.framesFolder = Path.Combine(current.tempFolder, Paths.framesDir);
if (!Directory.Exists(current.framesFolder) || IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png") < 2)
{
InterpolateUtils.ShowMessage("There are no extracted frames that can be interpolated!\nDid you run the extraction step?", "Error");
@@ -101,28 +97,24 @@ namespace Flowframes.Main
currentInputFrameCount = await InterpolateUtils.GetInputFrameCountAsync(current.inPath);
foreach (string ini in Directory.GetFiles(current.tempFolder, "*.ini", SearchOption.TopDirectoryOnly))
IOUtils.TryDeleteIfExists(ini);
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
// TODO: Check if this works lol, remove if it does
//if (Config.GetBool("sbsAllowAutoEnc"))
// nextOutPath = Path.Combine(currentOutPath, Path.GetFileNameWithoutExtension(current.inPath) + IOUtils.GetAiSuffix(current.ai, current.interpFactor) + InterpolateUtils.GetExt(current.outMode));
await PostProcessFrames(true);
int frames = IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png");
int targetFrameCount = frames * current.interpFactor;
if (canceled) return;
Program.mainForm.SetStatus("Running AI...");
await RunAi(current.interpFolder, current.ai, true);
await IOUtils.ReverseRenaming(current.framesFolder, AiProcess.filenameMap); // Get timestamps back
AiProcess.filenameMap.Clear();
Program.mainForm.SetProgress(0);
}
public static async Task CreateOutputVid()
{
string[] outFrames = IOUtils.GetFilesSorted(current.interpFolder, $"*.{InterpolateUtils.GetOutExt()}");
if (outFrames.Length > 0 && !IOUtils.CheckImageValid(outFrames[0]))
{
InterpolateUtils.ShowMessage("Invalid frame files detected!\n\nIf you used Auto-Encode, this is normal, and you don't need to run " +
@@ -131,12 +123,12 @@ namespace Flowframes.Main
}
string outPath = Path.Combine(current.outPath, Path.GetFileNameWithoutExtension(current.inPath) + IOUtils.GetCurrentExportSuffix() + FFmpegUtils.GetExt(current.outMode));
await CreateVideo.Export(current.interpFolder, outPath, current.outMode);
await CreateVideo.Export(current.interpFolder, outPath, current.outMode, true);
}
public static async Task Reset()
{
Cleanup(current.interpFolder, true);
await Cleanup(true);
}
}
}

View File

@@ -1,4 +1,5 @@
using Flowframes.Data;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.Forms;
using Flowframes.IO;
using Flowframes.MiscUtils;
@@ -37,11 +38,11 @@ namespace Flowframes.Main
if (frameFolderInput)
{
string lastFramePath = IOUtils.GetFilesSorted(i.current.inPath, false).Last();
await FFmpegCommands.ExtractLastFrame(lastFramePath, targetPath, res);
await FfmpegExtract.ExtractLastFrame(lastFramePath, targetPath, res);
}
else
{
await FFmpegCommands.ExtractLastFrame(i.current.inPath, targetPath, res);
await FfmpegExtract.ExtractLastFrame(i.current.inPath, targetPath, res);
}
}
catch (Exception e)
@@ -60,7 +61,7 @@ namespace Flowframes.Main
public static int targetFrames;
public static string currentOutdir;
public static int currentFactor;
public static float currentFactor;
public static bool progressPaused = false;
public static bool progCheckRunning = false;
public static async void GetProgressByFrameAmount(string outdir, int target)
@@ -95,10 +96,13 @@ namespace Flowframes.Main
Program.mainForm.SetProgress(0);
}
public static int interpolatedInputFramesCount;
public static void UpdateInterpProgress(int frames, int target, string latestFramePath = "")
{
if (i.canceled) return;
interpolatedInputFramesCount = ((frames / i.current.interpFactor).RoundToInt() - 1);
ResumeUtils.Save();
frames = frames.Clamp(0, target);
int percent = (int)Math.Round(((float)frames / target) * 100f);
Program.mainForm.SetProgress(percent);
@@ -132,11 +136,28 @@ namespace Flowframes.Main
catch { }
}
public static async Task DeleteInterpolatedInputFrames ()
{
interpolatedInputFramesCount = 0;
string[] inputFrames = IOUtils.GetFilesSorted(i.current.framesFolder);
for (int i = 0; i < inputFrames.Length; i++)
{
while (Program.busy && (i + 10) > interpolatedInputFramesCount) await Task.Delay(1000);
if (!Program.busy) break;
if(i != 0 && i != inputFrames.Length - 1)
IOUtils.OverwriteFileWithText(inputFrames[i]);
if (i % 10 == 0) await Task.Delay(10);
}
}
public static void SetPreviewImg (Image img)
{
if (img == null)
return;
preview.Image = img;
if (bigPreviewForm != null)
bigPreviewForm.SetImage(img);
}
@@ -144,7 +165,14 @@ namespace Flowframes.Main
public static Dictionary<string, int> frameCountCache = new Dictionary<string, int>();
public static async Task<int> GetInputFrameCountAsync (string path)
{
string hash = await IOUtils.GetHashAsync(path, IOUtils.Hash.xxHash); // Get checksum for caching
int maxMb = Config.GetInt("storeHashedFramecountMaxSizeMb", 256);
string hash = "";
if (IOUtils.GetFilesize(path) >= 0 && IOUtils.GetFilesize(path) < maxMb * 1024 * 1024)
hash = await IOUtils.GetHashAsync(path, IOUtils.Hash.xxHash); // Get checksum for caching
else
Logger.Log($"GetInputFrameCountAsync: File bigger than {maxMb}mb, won't hash.", true);
if (hash.Length > 1 && frameCountCache.ContainsKey(hash))
{
Logger.Log($"FrameCountCache contains this hash ({hash}), using cached frame count.", true);
@@ -156,16 +184,18 @@ namespace Flowframes.Main
}
int frameCount = 0;
if (IOUtils.IsPathDirectory(path))
frameCount = IOUtils.GetAmountOfFiles(path, false);
else
frameCount = await FFmpegCommands.GetFrameCountAsync(path);
frameCount = await FfmpegCommands.GetFrameCountAsync(path);
if (hash.Length > 1 && frameCount > 5000) // Cache if >5k frames to avoid re-reading it every single time
{
Logger.Log($"Adding hash ({hash}) with frame count {frameCount} to cache.", true);
frameCountCache[hash] = frameCount; // Use CRC32 instead of path to avoid using cached value if file was changed
}
return frameCount;
}
@@ -213,7 +243,7 @@ namespace Flowframes.Main
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, Interpolate.OutMode outMode)
public static bool InputIsValid(string inDir, string outDir, float fpsOut, float factor, Interpolate.OutMode outMode)
{
bool passes = true;
@@ -229,7 +259,7 @@ namespace Flowframes.Main
ShowMessage("Output path is not valid!");
passes = false;
}
if (passes && interp != 2 && interp != 4 && interp != 8)
if (passes && /*factor != 2 && factor != 4 && factor != 8*/ factor > 16)
{
ShowMessage("Interpolation factor is not valid!");
passes = false;
@@ -239,9 +269,9 @@ namespace Flowframes.Main
ShowMessage("Invalid output frame rate!\nGIF does not properly support frame rates above 40 FPS.\nPlease use MP4, WEBM or another video format.");
passes = false;
}
if (passes && fpsOut < 1 || fpsOut > 500)
if (passes && fpsOut < 1 || fpsOut > 1000)
{
ShowMessage("Invalid output frame rate - Must be 1-500.");
ShowMessage("Invalid output frame rate - Must be 1-1000.");
passes = false;
}
if (!passes)
@@ -249,18 +279,10 @@ namespace Flowframes.Main
return passes;
}
public static void PathAsciiCheck (string inpath, string outpath)
{
bool shownMsg = false;
if (OSUtils.HasNonAsciiChars(inpath))
{
ShowMessage("Warning: Input path includes non-ASCII characters. This might cause problems.");
shownMsg = true;
}
if (!shownMsg && OSUtils.HasNonAsciiChars(outpath))
ShowMessage("Warning: Output path includes non-ASCII characters. This might cause problems.");
public static void PathAsciiCheck (string path, string pathTitle)
{
if (IOUtils.HasBadChars(path) || OSUtils.HasNonAsciiChars(path))
ShowMessage($"Warning: Your {pathTitle} includes special characters. This might cause problems.");
}
public static void GifCompatCheck (Interpolate.OutMode outMode, float fpsOut, int targetFrameCount)
@@ -327,9 +349,6 @@ namespace Flowframes.Main
{
if (videoPath == null || !IOUtils.IsFileValid(videoPath))
return false;
// string ext = Path.GetExtension(videoPath).ToLower();
// if (!Formats.supported.Contains(ext))
// return false;
return true;
}
@@ -340,13 +359,13 @@ namespace Flowframes.Main
Logger.Log("Message: " + msg, true);
}
public static async Task<Size> GetOutputResolution (string inputPath, bool print)
public static async Task<Size> GetOutputResolution (string inputPath, bool print, bool returnZeroIfUnchanged = false)
{
Size resolution = await IOUtils.GetVideoOrFramesRes(inputPath);
return GetOutputResolution(resolution, print);
return GetOutputResolution(resolution, print, returnZeroIfUnchanged);
}
public static Size GetOutputResolution(Size inputRes, bool print = false)
public static Size GetOutputResolution(Size inputRes, bool print = false, bool returnZeroIfUnchanged = false)
{
int maxHeight = RoundDiv2(Config.GetInt("maxVidHeight"));
if (inputRes.Height > maxHeight)
@@ -360,7 +379,11 @@ namespace Flowframes.Main
}
else
{
return new Size(RoundDiv2(inputRes.Width), RoundDiv2(inputRes.Height));
//return new Size(RoundDiv2(inputRes.Width), RoundDiv2(inputRes.Height));
if (returnZeroIfUnchanged)
return new Size();
else
return inputRes;
}
}

120
Code5/Main/ResumeUtils.cs Normal file
View File

@@ -0,0 +1,120 @@
using Flowframes.Data;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using I = Flowframes.Interpolate;
namespace Flowframes.Main
{
class ResumeUtils
{
public static bool resumeNextRun;
public static float timeBetweenSaves = 10;
public static int minFrames = 100;
public static int safetyDelayFrames = 50;
public static string resumeFilename = "resume.ini";
public static string interpSettingsFilename = "interpSettings.ini";
public static string filenameMapFilename = "frameFilenames.ini";
public static Stopwatch timeSinceLastSave = new Stopwatch();
public static void Save ()
{
if (timeSinceLastSave.IsRunning && timeSinceLastSave.ElapsedMilliseconds < (timeBetweenSaves * 1000f).RoundToInt()) return;
int frames = (int)Math.Round((float)InterpolateUtils.interpolatedInputFramesCount / I.current.interpFactor) - safetyDelayFrames;
if (frames < 1) return;
timeSinceLastSave.Restart();
Directory.CreateDirectory(Path.Combine(I.current.tempFolder, Paths.resumeDir));
SaveState(frames);
SaveInterpSettings();
SaveFilenameMap();
}
static void SaveState (int frames)
{
ResumeState state = new ResumeState(I.currentlyUsingAutoEnc, frames);
string filePath = Path.Combine(I.current.tempFolder, Paths.resumeDir, resumeFilename);
File.WriteAllText(filePath, state.ToString());
}
static async Task SaveFilenameMap ()
{
string filePath = Path.Combine(I.current.tempFolder, Paths.resumeDir, filenameMapFilename);
if (File.Exists(filePath) && IOUtils.GetFilesize(filePath) > 0)
return;
string fileContent = "";
int counter = 0;
foreach (KeyValuePair<string, string> entry in AiProcess.filenameMap)
{
if (counter % 1000 == 0) await Task.Delay(1);
fileContent += $"{entry.Key}|{entry.Value}\n";
counter++;
}
File.WriteAllText(filePath, fileContent);
}
static void SaveInterpSettings ()
{
string filepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, interpSettingsFilename);
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 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<string> inputFrameLines = File.ReadAllLines(fileMapFilepath).Where(l => l.Trim().Length > 3).ToList();
List<string> 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()
{
Dictionary<string, string> dict = new Dictionary<string, string>();
string filePath = Path.Combine(I.current.tempFolder, Paths.resumeDir, filenameMapFilename);
string[] dictLines = File.ReadAllLines(filePath);
foreach (string line in dictLines)
{
if (line.Length < 5) continue;
string[] keyValuePair = line.Split('|');
dict.Add(keyValuePair[0].Trim(), keyValuePair[1].Trim());
}
AiProcess.filenameMap = dict;
}
}
}