mirror of
https://github.com/n00mkrad/flowframes.git
synced 2026-07-11 13:02:56 +02:00
Update .NET 5 code
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
120
Code5/Main/ResumeUtils.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user