1
mirror of https://github.com/n00mkrad/flowframes synced 2024-11-16 19:10:31 +01:00

ffmpeg dupes workaround, SBS ReverseRenaming fix

This commit is contained in:
N00MKRAD 2020-12-07 00:41:07 +01:00
parent 4d2da35727
commit 3125670bb0
6 changed files with 44 additions and 17 deletions

View File

@ -95,26 +95,38 @@ namespace Flowframes
DeleteSource(inputDir); DeleteSource(inputDir);
} }
public static async Task FramesToMp4Vfr(string framesFile, string outPath, bool useH265, int crf, float fps, int looptimes = -1) public static async Task FramesToMp4Vfr(string framesFile, string outPath, bool useH265, int crf, float fps, bool inRate)
{ {
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 loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : "";
string presetStr = $"-preset {Config.Get("ffEncPreset")}"; string presetStr = $"-preset {Config.Get("ffEncPreset")}";
string vsyncStr = Config.GetInt("vfrMode") == 0 ? "-vsync 1" : "-vsync 2"; string vsyncStr = Config.GetInt("vfrMode") == 0 ? "-vsync 1" : "-vsync 2";
string vfrFilename = Path.GetFileName(framesFile); string vfrFilename = Path.GetFileName(framesFile);
string args = $" {loopStr} {vsyncStr} -f concat -r {fps.ToString().Replace(",", ".")} -i {vfrFilename} -c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
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); await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.OnlyLastLine);
} }
public static async Task FramesToMp4VfrChunk(string framesFile, string outPath, bool useH265, int crf, float fps) public static async Task FramesToMp4VfrChunk(string framesFile, string outPath, bool useH265, int crf, float fps, bool inRate)
{ {
//Logger.Log($"Encoding MP4 chunk 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 vsyncStr = Config.GetInt("vfrMode") == 0 ? "-vsync 1" : "-vsync 2";
string vfrFilename = Path.GetFileName(framesFile); string vfrFilename = Path.GetFileName(framesFile);
string args = $" {vsyncStr} -f concat -r {fps.ToString().Replace(",", ".")} -i {vfrFilename} -c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
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); await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.Hidden);
} }

View File

@ -84,7 +84,7 @@ namespace Flowframes.Main
int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf"); int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf");
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-x{i.lastInterpFactor}.ini"); string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-x{i.lastInterpFactor}.ini");
await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, fps, -1); await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, fps, i.constantFrameRate);
/* DELETE THIS AS SOON AS I'M SURE I CAN USE VFR WITH TIMING DISABLED /* DELETE THIS AS SOON AS I'M SURE I CAN USE VFR WITH TIMING DISABLED
if (Config.GetInt("timingMode") == 1 && Config.GetInt("dedupMode") != 0) if (Config.GetInt("timingMode") == 1 && Config.GetInt("dedupMode") != 0)
@ -174,7 +174,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); await FFmpegCommands.FramesToMp4VfrChunk(vfrFile, outPath, h265, crf, i.currentOutFps, i.constantFrameRate);
IOUtils.TryDeleteIfExists(vfrFile); IOUtils.TryDeleteIfExists(vfrFile);
} }

View File

@ -29,6 +29,7 @@ namespace Flowframes
public static float currentInFps; public static float currentInFps;
public static float currentOutFps; public static float currentOutFps;
public static int currentInputFrameCount; public static int currentInputFrameCount;
public static bool constantFrameRate;
public static OutMode currentOutMode; public static OutMode currentOutMode;
public static bool currentInputIsFrames; public static bool currentInputIsFrames;
public static bool currentlyUsingAutoEnc; public static bool currentlyUsingAutoEnc;
@ -63,6 +64,7 @@ namespace Flowframes
Utils.PathAsciiCheck(inPath, outDir); Utils.PathAsciiCheck(inPath, outDir);
lastAi = ai; lastAi = ai;
currentInputIsFrames = IOUtils.IsPathDirectory(inPath); currentInputIsFrames = IOUtils.IsPathDirectory(inPath);
currentInputFrameCount = Utils.GetInputFrameCount(inPath);
Program.mainForm.SetStatus("Starting..."); Program.mainForm.SetStatus("Starting...");
Program.mainForm.SetWorking(true); Program.mainForm.SetWorking(true);
await Task.Delay(10); await Task.Delay(10);
@ -144,16 +146,19 @@ namespace Flowframes
public static async Task PostProcessFrames (bool sbsMode = false) public static async Task PostProcessFrames (bool sbsMode = false)
{ {
currentInputFrameCount = IOUtils.GetAmountOfFiles(currentFramesPath, false, "*.png"); if (!Directory.Exists(currentFramesPath) || currentInputFrameCount <= 0 || IOUtils.GetAmountOfFiles(currentFramesPath, false, "*.png") < 2)
Cancel("Extracted frames folder is empty!");
if (!Directory.Exists(currentFramesPath) || currentInputFrameCount <= 0)
Cancel("Input frames folder is empty!");
if (Config.GetInt("dedupMode") == 1) if (Config.GetInt("dedupMode") == 1)
await MagickDedupe.Run(currentFramesPath); await MagickDedupe.Run(currentFramesPath);
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}");
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

@ -71,9 +71,7 @@ namespace Flowframes.Main
currentOutPath = e.outPath; currentOutPath = e.outPath;
currentTempDir = InterpolateUtils.GetTempFolderLoc(currentInPath, currentOutPath); currentTempDir = InterpolateUtils.GetTempFolderLoc(currentInPath, currentOutPath);
currentFramesPath = Path.Combine(currentTempDir, Paths.framesDir); currentFramesPath = Path.Combine(currentTempDir, Paths.framesDir);
currentInterpFramesDir = Path.Combine(currentTempDir, Paths.interpDir); currentInterpFramesDir = Path.Combine(currentTempDir, Paths.interpDir);
currentInputIsFrames = IOUtils.IsPathDirectory(currentInPath); currentInputIsFrames = IOUtils.IsPathDirectory(currentInPath);
} }
catch catch
@ -103,6 +101,7 @@ 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();
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); Size resolution = IOUtils.GetVideoRes(currentInPath);
@ -138,7 +137,7 @@ namespace Flowframes.Main
public static async Task DoInterpolate() public static async Task DoInterpolate()
{ {
currentFramesPath = Path.Combine(currentTempDir, Paths.framesDir); currentFramesPath = Path.Combine(currentTempDir, Paths.framesDir);
if (!Directory.Exists(currentFramesPath)) if (!Directory.Exists(currentFramesPath) || IOUtils.GetAmountOfFiles(currentFramesPath, false, "*.png") < 2)
{ {
InterpolateUtils.ShowMessage("There are no extracted frames that can be interpolated!\nDid you run the extraction step?", "Error"); InterpolateUtils.ShowMessage("There are no extracted frames that can be interpolated!\nDid you run the extraction step?", "Error");
return; return;
@ -149,10 +148,13 @@ namespace Flowframes.Main
return; return;
} }
currentInputFrameCount = InterpolateUtils.GetInputFrameCount(currentInPath);
foreach (string ini in Directory.GetFiles(currentTempDir, "*.ini", SearchOption.TopDirectoryOnly)) foreach (string ini in Directory.GetFiles(currentTempDir, "*.ini", SearchOption.TopDirectoryOnly))
IOUtils.TryDeleteIfExists(ini); IOUtils.TryDeleteIfExists(ini);
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
await PostProcessFrames(true); await PostProcessFrames(true);
lastInterpFactor = interpFactor; lastInterpFactor = interpFactor;

View File

@ -64,6 +64,14 @@ namespace Flowframes.Main
bigPreviewForm.SetImage(img); bigPreviewForm.SetImage(img);
} }
public static int GetInputFrameCount(string path)
{
if (IOUtils.IsPathDirectory(path))
return IOUtils.GetAmountOfFiles(path, false);
else
return FFmpegCommands.GetFrameCount(path);
}
public static int GetProgressWaitTime(int numFrames) public static int GetProgressWaitTime(int numFrames)
{ {
float hddMultiplier = 2f; float hddMultiplier = 2f;

View File

@ -33,9 +33,9 @@ namespace Flowframes.UI
if (!Program.lastInputPathIsSsd) if (!Program.lastInputPathIsSsd)
Logger.Log("Your file seems to be on an HDD or USB device. It is recommended to interpolate videos on an SSD drive for best performance."); Logger.Log("Your file seems to be on an HDD or USB device. It is recommended to interpolate videos on an SSD drive for best performance.");
if (IOUtils.IsPathDirectory(path)) if (IOUtils.IsPathDirectory(path))
Logger.Log($"Video FPS (Loaded from fps.ini): {fpsStr} - Total Number Of Frames: {IOUtils.GetAmountOfFiles(path, false)}"); Logger.Log($"Video FPS (Loaded from fps.ini): {fpsStr} - Total Number Of Frames: {InterpolateUtils.GetInputFrameCount(path)}");
else else
Logger.Log($"Video FPS: {fpsStr} - Total Number Of Frames: {FFmpegCommands.GetFrameCount(path)}"); Logger.Log($"Video FPS: {fpsStr} - Total Number Of Frames: {InterpolateUtils.GetInputFrameCount(path)}");
await Task.Delay(10); await Task.Delay(10);
await PrintResolution(path); await PrintResolution(path);
MagickDedupe.ClearCache(); MagickDedupe.ClearCache();