Fixed VFR frame drops, bigger AutoEnc safety buffer for NCNN, cleanup

This commit is contained in:
N00MKRAD
2020-12-15 14:46:33 +01:00
parent e7dae90c92
commit 4510bd6015
8 changed files with 78 additions and 121 deletions

View File

@@ -13,7 +13,7 @@ namespace Flowframes
{ {
static string hdrFilter = @"-vf select=gte(n\,%frNum%),zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p"; static string hdrFilter = @"-vf select=gte(n\,%frNum%),zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p";
static string videoEncArgs = "-pix_fmt yuv420p -movflags +faststart -vf \"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\""; static string videoEncArgs = "-pix_fmt yuv420p -movflags +faststart";
static string divisionFilter = "\"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\""; static string divisionFilter = "\"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\"";
static string pngComprArg = "-compression_level 3"; static string pngComprArg = "-compression_level 3";
@@ -39,14 +39,11 @@ namespace Flowframes
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : ""; string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
IOUtils.CreateDir(frameFolderPath); IOUtils.CreateDir(frameFolderPath);
string timecodeStr = timecodes ? "-copyts -r 1000 -frame_pts true" : ""; string timecodeStr = timecodes ? "-copyts -r 1000 -frame_pts true" : "";
string scnDetect = sceneDetect ? $"\"select='gt(scene,{Config.GetFloatString("scnDetectValue")})'\"," : ""; string scnDetect = sceneDetect ? $"-vf \"select='gt(scene,{Config.GetFloatString("scnDetectValue")})'\"" : "";
string mpStr = deDupe ? ((Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr) : "";
string vf = (scnDetect.Length > 2 || mpStr.Length > 2) ? $"-vf {scnDetect},{mpStr}".ListCommaFix() : "";
string pad = Padding.inputFrames.ToString(); string pad = Padding.inputFrames.ToString();
string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {scnDetect}{divisionFilter} {sizeStr} \"{frameFolderPath}/%{pad}d.png\""; string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} {vf} {sizeStr} \"{frameFolderPath}/%{pad}d.png\"";
if (deDupe)
{
string mpStr = (Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr;
args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {scnDetect}{mpStr},{divisionFilter} {sizeStr} \"{frameFolderPath}/%{pad}d.png\"";
}
AvProcess.LogMode logMode = Interpolate.currentInputFrameCount > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden; AvProcess.LogMode logMode = Interpolate.currentInputFrameCount > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
await AvProcess.RunFfmpeg(args, logMode); await AvProcess.RunFfmpeg(args, logMode);
await Task.Delay(1); await Task.Delay(1);
@@ -99,39 +96,15 @@ namespace Flowframes
DeleteSource(inputDir); DeleteSource(inputDir);
} }
public static async Task FramesToMp4Vfr(string framesFile, string outPath, bool useH265, int crf, float fps, bool inRate) public static async Task FramesToMp4Vfr(string framesFile, string outPath, bool useH265, int crf, float fps, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine)
{ {
if(logMode != AvProcess.LogMode.Hidden)
Logger.Log($"Encoding MP4 video with CRF {crf}..."); Logger.Log($"Encoding MP4 video with CRF {crf}...");
string enc = useH265 ? "libx265" : "libx264"; string enc = useH265 ? "libx265" : "libx264";
string presetStr = $"-preset {Config.Get("ffEncPreset")}"; string presetStr = $"-preset {Config.Get("ffEncPreset")}";
string vsyncStr = Config.GetInt("vfrMode") == 0 ? "-vsync 1" : "-vsync 2";
string vfrFilename = Path.GetFileName(framesFile); string vfrFilename = Path.GetFileName(framesFile);
string args = $"-vsync 2 -f concat -i {vfrFilename} -r {fps.ToString().Replace(",", ".")} -c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
string args = $" {vsyncStr} -f concat "; await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), logMode);
if (inRate)
args += $"-r {fps.ToString().Replace(",", ".")} -i {vfrFilename} ";
else
args += $"-i {vfrFilename} -r {fps.ToString().Replace(",", ".")} ";
args += $"-c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.OnlyLastLine);
}
public static async Task FramesToMp4VfrChunk(string framesFile, string outPath, bool useH265, int crf, float fps, bool inRate)
{
string enc = useH265 ? "libx265" : "libx264";
string presetStr = $"-preset {Config.Get("ffEncPreset")}";
string vsyncStr = Config.GetInt("vfrMode") == 0 ? "-vsync 1" : "-vsync 2";
string vfrFilename = Path.GetFileName(framesFile);
string args = $" {vsyncStr} -f concat ";
if (inRate)
args += $"-r {fps.ToString().Replace(",", ".")} -i {vfrFilename} ";
else
args += $"-i {vfrFilename} -r {fps.ToString().Replace(",", ".")} ";
args += $"-c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.Hidden);
} }
public static async Task ConcatVideos(string concatFile, string outPath, float fps, int looptimes = -1) public static async Task ConcatVideos(string concatFile, string outPath, float fps, int looptimes = -1)
@@ -158,8 +131,7 @@ namespace Flowframes
public static async void FramesToApng(string inputDir, bool opti, int fps, string prefix, bool delSrc = false) public static async void FramesToApng(string inputDir, bool opti, int fps, string prefix, bool delSrc = false)
{ {
int nums = IOUtils.GetFilenameCounterLength(Directory.GetFiles(inputDir, "*.png")[0], prefix); int nums = IOUtils.GetFilenameCounterLength(Directory.GetFiles(inputDir, "*.png")[0], prefix);
string filter = ""; string filter = opti ? "-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\"" : "";
if(opti) filter = "-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\"";
string args = "-framerate " + fps + " -i \"" + inputDir + "\\" + prefix + "%0" + nums + "d.png\" -f apng -plays 0 " + filter + " \"" + inputDir + "-anim.png\""; string args = "-framerate " + fps + " -i \"" + inputDir + "\\" + prefix + "%0" + nums + "d.png\" -f apng -plays 0 " + filter + " \"" + inputDir + "-anim.png\"";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine); await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
if (delSrc) if (delSrc)

View File

@@ -155,5 +155,10 @@ namespace Flowframes
return str.Remove(place, stringToReplace.Length).Insert(place, replaceWith); return str.Remove(place, stringToReplace.Length).Insert(place, replaceWith);
} }
public static string ListCommaFix (this string str)
{
return str.Replace(" , ", " ").Replace(" ,", " ").Replace(", ", " ");
}
} }
} }

View File

@@ -177,8 +177,7 @@ namespace Flowframes.Magick
} }
} }
string testStr = ""; string testStr = testRun ? " [TestRun]" : "";
if (testRun) testStr = " [TestRun]";
if (Interpolate.canceled) return; if (Interpolate.canceled) return;
if (skipped) if (skipped)
@@ -238,16 +237,13 @@ namespace Flowframes.Magick
string line = dupeFrameLines[i]; string line = dupeFrameLines[i];
sourceFrameNum++; sourceFrameNum++;
string paddedFilename = "";
string sourceFramePath = "";
string[] kvp = line.Split(':'); string[] kvp = line.Split(':');
int currentInFrame = kvp[0].GetInt(); int currentInFrame = kvp[0].GetInt();
int currentDupesAmount = kvp[1].GetInt(); int currentDupesAmount = kvp[1].GetInt();
// Copy Source Frame // Copy Source Frame
paddedFilename = sourceFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + $".{ext}"; string paddedFilename = sourceFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + $".{ext}";
sourceFramePath = Path.Combine(path, paddedFilename); string sourceFramePath = Path.Combine(path, paddedFilename);
if(debugLog) Logger.Log("[Source] Moving " + Path.GetFileName(sourceFramePath) + " => " + outFrameNum + $".{ext}"); if(debugLog) Logger.Log("[Source] Moving " + Path.GetFileName(sourceFramePath) + " => " + outFrameNum + $".{ext}");
if (!IOUtils.TryCopy(sourceFramePath, Path.Combine(tempSubFolder, outFrameNum + $".{ext}"))) if (!IOUtils.TryCopy(sourceFramePath, Path.Combine(tempSubFolder, outFrameNum + $".{ext}")))
break; break;

View File

@@ -14,7 +14,7 @@ namespace Flowframes.Main
static string interpFramesFolder; static string interpFramesFolder;
static string videoChunksFolder; static string videoChunksFolder;
public static int chunkSize = 125; // Encode every n frames public static int chunkSize = 125; // Encode every n frames
public static int safetyBufferFrames = 25; public static int safetyBufferFrames = 50; // Ignore latest n frames to avoid using images that haven't been fully encoded yet
public static List<string> encodedFrames = new List<string>(); public static List<string> encodedFrames = new List<string>();
public static List<string> unencodedFrames = new List<string>(); public static List<string> unencodedFrames = new List<string>();
@@ -32,6 +32,8 @@ namespace Flowframes.Main
unencodedFrames.Clear(); unencodedFrames.Clear();
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.currentFramesPath, false, "*.png") * Interpolate.lastInterpFactor); chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.currentFramesPath, false, "*.png") * Interpolate.lastInterpFactor);
safetyBufferFrames = Interpolate.lastAi.aiName.ToUpper().Contains("NCNN") ? 60 : 30; // Use bigger safety buffer for NCNN
Logger.Log($"Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true);
int videoIndex = 1; int videoIndex = 1;

View File

@@ -69,16 +69,7 @@ namespace Flowframes.Main
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.lastInterpFactor}x.ini"); string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.lastInterpFactor}x.ini");
if (mode == i.OutMode.VidGif) if (mode == i.OutMode.VidGif)
{
await FFmpegCommands.FramesToGifVfr(vfrFile, outPath, true, Config.GetInt("gifColors")); await FFmpegCommands.FramesToGifVfr(vfrFile, outPath, true, Config.GetInt("gifColors"));
// TODO: Remove old code once new code works well
// if (new DirectoryInfo(framesPath).GetFiles()[0].Extension != ".png")
// {
// Logger.Log("Converting output frames to PNG to encode with Gifski...");
// await Converter.Convert(framesPath, ImageMagick.MagickFormat.Png00, 20, "png", false);
// }
// await GifskiCommands.CreateGifFromFrames(i.currentOutFps.RoundToInt(), Config.GetInt("gifskiQ"), framesPath, outPath);
}
if (mode == i.OutMode.VidMp4) if (mode == i.OutMode.VidMp4)
{ {
@@ -86,7 +77,7 @@ namespace Flowframes.Main
bool h265 = Config.GetInt("mp4Enc") == 1; bool h265 = Config.GetInt("mp4Enc") == 1;
int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf"); int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf");
await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, fps, i.constantFrameRate); await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, fps);
await MergeAudio(i.lastInputPath, outPath); await MergeAudio(i.lastInputPath, outPath);
if (changeFps > 0) if (changeFps > 0)
@@ -95,8 +86,6 @@ namespace Flowframes.Main
Program.mainForm.SetStatus("Creating video with desired frame rate..."); Program.mainForm.SetStatus("Creating video with desired frame rate...");
await FFmpegCommands.ConvertFramerate(outPath, currentOutFile, h265, crf, changeFps, !keepOriginalFpsVid); await FFmpegCommands.ConvertFramerate(outPath, currentOutFile, h265, crf, changeFps, !keepOriginalFpsVid);
await MergeAudio(i.lastInputPath, currentOutFile); await MergeAudio(i.lastInputPath, currentOutFile);
//if (looptimes > 0)
// await Loop(newOutPath, looptimes);
} }
if (looptimes > 0) if (looptimes > 0)
@@ -164,7 +153,7 @@ namespace Flowframes.Main
string vfrFile = Path.Combine(i.currentTempDir, $"vfr-chunk-temp.ini"); string vfrFile = Path.Combine(i.currentTempDir, $"vfr-chunk-temp.ini");
File.WriteAllLines(vfrFile, IOUtils.ReadLines(vfrFileOriginal).Skip(firstFrameNum * 2).Take(framesAmount * 2)); File.WriteAllLines(vfrFile, IOUtils.ReadLines(vfrFileOriginal).Skip(firstFrameNum * 2).Take(framesAmount * 2));
await FFmpegCommands.FramesToMp4VfrChunk(vfrFile, outPath, h265, crf, i.currentOutFps, i.constantFrameRate); await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, i.currentOutFps, AvProcess.LogMode.Hidden);
IOUtils.TryDeleteIfExists(vfrFile); IOUtils.TryDeleteIfExists(vfrFile);
} }

View File

@@ -106,27 +106,10 @@ namespace Flowframes
await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(currentTempDir, Paths.scenesDir)); await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(currentTempDir, Paths.scenesDir));
await Task.Delay(10); await Task.Delay(10);
} }
Program.mainForm.SetStatus("Extracting frames from video..."); Program.mainForm.SetStatus("Extracting frames from video...");
Size resolution = IOUtils.GetVideoRes(inPath); await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, Utils.GetOutputResolution(inPath));
int maxHeight = Config.GetInt("maxVidHeight");
if (resolution.Height > maxHeight)
{
float factor = (float)maxHeight / resolution.Height;
int width = (resolution.Width * factor).RoundToInt();
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, new Size(width, maxHeight));
}
else
{
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false);
}
/*
if (AvProcess.lastOutputFfmpeg.ToLower().Contains("invalid"))
{
Cancel("Failed to read input video.");
return;
}
*/
if (extractAudio) if (extractAudio)
{ {
string audioFile = Path.Combine(currentTempDir, "audio.m4a"); string audioFile = Path.Combine(currentTempDir, "audio.m4a");
@@ -163,11 +146,6 @@ namespace Flowframes
else else
MagickDedupe.ClearCache(); MagickDedupe.ClearCache();
int frameCountAfterDedupe = IOUtils.GetAmountOfFiles(currentFramesPath, false, "*.png");
int dupesPercent = 100 - (((float)frameCountAfterDedupe / currentInputFrameCount) * 100f).RoundToInt();
constantFrameRate = dupesPercent < 5f; // Ignore VFR timings for CFR input. TODO: Figure out how to avoid dupes when using VFR timings
Logger.Log($"{dupesPercent}% of frames are dupes, so constantFrameRate = {constantFrameRate}", true);
if (canceled) return; if (canceled) return;
bool useTimestamps = Config.GetInt("timingMode") == 1; // TODO: Auto-Disable timestamps if input frames are sequential, not timestamped bool useTimestamps = Config.GetInt("timingMode") == 1; // TODO: Auto-Disable timestamps if input frames are sequential, not timestamped

View File

@@ -101,22 +101,12 @@ namespace Flowframes.Main
InterpolateUtils.ShowMessage("Failed to delete existing frames folder - Make sure no file is opened in another program!", "Error"); InterpolateUtils.ShowMessage("Failed to delete existing frames folder - Make sure no file is opened in another program!", "Error");
return; return;
} }
AiProcess.filenameMap.Clear(); AiProcess.filenameMap.Clear();
bool extractAudio = true; bool extractAudio = true;
Program.mainForm.SetStatus("Extracting frames from video..."); Program.mainForm.SetStatus("Extracting frames from video...");
Size resolution = IOUtils.GetVideoRes(currentInPath); await FFmpegCommands.VideoToFrames(currentInPath, currentFramesPath, Config.GetInt("dedupMode") == 2, false, InterpolateUtils.GetOutputResolution(currentInPath));
int maxHeight = Config.GetInt("maxVidHeight");
if (resolution.Height > maxHeight)
{
float factor = (float)maxHeight / resolution.Height;
int width = (resolution.Width * factor).RoundToInt();
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
await FFmpegCommands.VideoToFrames(currentInPath, currentFramesPath, Config.GetInt("dedupMode") == 2, false, new Size(width, maxHeight));
}
else
{
await FFmpegCommands.VideoToFrames(currentInPath, currentFramesPath, Config.GetInt("dedupMode") == 2, false);
}
if (extractAudio) if (extractAudio)
{ {
string audioFile = Path.Combine(currentTempDir, "audio.m4a"); string audioFile = Path.Combine(currentTempDir, "audio.m4a");

View File

@@ -260,5 +260,30 @@ namespace Flowframes.Main
MessageBox.Show(msg, title); MessageBox.Show(msg, title);
Logger.Log("Message: " + msg, true); Logger.Log("Message: " + msg, true);
} }
public static Size GetOutputResolution (string inputPath)
{
Size resolution = IOUtils.GetVideoRes(inputPath);
int maxHeight = RoundDiv2(Config.GetInt("maxVidHeight"));
if (resolution.Height > maxHeight)
{
float factor = (float)maxHeight / resolution.Height;
Logger.Log($"Un-rounded downscaled size: {(resolution.Width * factor).ToString("0.00")}x{Config.GetInt("maxVidHeight")}", true);
int width = RoundDiv2((resolution.Width * factor).RoundToInt());
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
return new Size(width, maxHeight);
}
else
{
return resolution;
}
}
public static int RoundDiv2(int n) // Round to a number that's divisible by 2 (for h264)
{
int a = (n / 2) * 2; // Smaller multiple
int b = a + 2; // Larger multiple
return (n - a > b - n) ? b : a; // Return of closest of two
}
} }
} }