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 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 pngComprArg = "-compression_level 3";
@@ -39,14 +39,11 @@ namespace Flowframes
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
IOUtils.CreateDir(frameFolderPath);
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 args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {scnDetect}{divisionFilter} {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\"";
}
string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} {vf} {sizeStr} \"{frameFolderPath}/%{pad}d.png\"";
AvProcess.LogMode logMode = Interpolate.currentInputFrameCount > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
await AvProcess.RunFfmpeg(args, logMode);
await Task.Delay(1);
@@ -99,39 +96,15 @@ namespace Flowframes
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}...");
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.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);
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()}";
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), logMode);
}
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)
{
int nums = IOUtils.GetFilenameCounterLength(Directory.GetFiles(inputDir, "*.png")[0], prefix);
string filter = "";
if(opti) filter = "-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\"";
string filter = opti ? "-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\"";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
if (delSrc)

View File

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

View File

@@ -14,7 +14,7 @@ namespace Flowframes.Main
static string interpFramesFolder;
static string videoChunksFolder;
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> unencodedFrames = new List<string>();
@@ -32,6 +32,8 @@ namespace Flowframes.Main
unencodedFrames.Clear();
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;

View File

@@ -69,16 +69,7 @@ namespace Flowframes.Main
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.lastInterpFactor}x.ini");
if (mode == i.OutMode.VidGif)
{
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)
{
@@ -86,7 +77,7 @@ namespace Flowframes.Main
bool h265 = Config.GetInt("mp4Enc") == 1;
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);
if (changeFps > 0)
@@ -95,8 +86,6 @@ namespace Flowframes.Main
Program.mainForm.SetStatus("Creating video with desired frame rate...");
await FFmpegCommands.ConvertFramerate(outPath, currentOutFile, h265, crf, changeFps, !keepOriginalFpsVid);
await MergeAudio(i.lastInputPath, currentOutFile);
//if (looptimes > 0)
// await Loop(newOutPath, looptimes);
}
if (looptimes > 0)
@@ -164,7 +153,7 @@ namespace Flowframes.Main
string vfrFile = Path.Combine(i.currentTempDir, $"vfr-chunk-temp.ini");
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);
}

View File

@@ -106,27 +106,10 @@ namespace Flowframes
await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(currentTempDir, Paths.scenesDir));
await Task.Delay(10);
}
Program.mainForm.SetStatus("Extracting frames from video...");
Size resolution = IOUtils.GetVideoRes(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;
}
*/
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, Utils.GetOutputResolution(inPath));
if (extractAudio)
{
string audioFile = Path.Combine(currentTempDir, "audio.m4a");
@@ -163,11 +146,6 @@ namespace Flowframes
else
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;
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");
return;
}
AiProcess.filenameMap.Clear();
bool extractAudio = true;
Program.mainForm.SetStatus("Extracting frames from video...");
Size resolution = IOUtils.GetVideoRes(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);
}
await FFmpegCommands.VideoToFrames(currentInPath, currentFramesPath, Config.GetInt("dedupMode") == 2, false, InterpolateUtils.GetOutputResolution(currentInPath));
if (extractAudio)
{
string audioFile = Path.Combine(currentTempDir, "audio.m4a");

View File

@@ -260,5 +260,30 @@ namespace Flowframes.Main
MessageBox.Show(msg, title);
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
}
}
}