Fixed and improved FPS limiting feature, added AVI codec options

This commit is contained in:
N00MKRAD 2021-01-06 17:41:18 +01:00
parent 98c6aedac6
commit 14df754538
9 changed files with 164 additions and 89 deletions

View File

@ -93,26 +93,31 @@ namespace Flowframes
}
public static async Task FramesToVideoConcat(string framesFile, string outPath, Interpolate.OutMode outMode, float fps, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine, bool isChunk = false)
{
await FramesToVideoConcat(framesFile, outPath, outMode, fps, 0, logMode, isChunk);
}
public static async Task FramesToVideoConcat(string framesFile, string outPath, Interpolate.OutMode outMode, float fps, float resampleFps, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine, bool isChunk = false)
{
if (logMode != AvProcess.LogMode.Hidden)
Logger.Log($"Encoding video...");
string encArgs = Utils.GetEncArgs(Utils.GetCodec(outMode)) + " -pix_fmt yuv420p ";
if (!isChunk) encArgs += $"-movflags +faststart";
Logger.Log((resampleFps <= 0) ? $"Encoding video..." : $"Encoding video resampled to {resampleFps.ToString().Replace(",", ".")} FPS...");
Directory.CreateDirectory(outPath.GetParentDir());
string encArgs = Utils.GetEncArgs(Utils.GetCodec(outMode));
if (!isChunk) encArgs += $" -movflags +faststart";
string vfrFilename = Path.GetFileName(framesFile);
//string vsync = (Interpolate.current.interpFactor == 2) ? "-vsync 1" : "-vsync 2";
string rate = fps.ToString().Replace(",", ".");
string vf = (resampleFps <= 0) ? "" : $"-vf fps=fps={resampleFps.ToString().Replace(",", ".")}";
string extraArgs = Config.Get("ffEncArgs");
string args = $"-loglevel error -vsync 0 -f concat -r {rate} -i {vfrFilename} {encArgs} {extraArgs} -threads {Config.GetInt("ffEncThreads")} {outPath.Wrap()}";
//string args = $"-vsync 0 -f concat -i {vfrFilename} {encArgs} {extraArgs} -threads {Config.GetInt("ffEncThreads")} {outPath.Wrap()}";
string args = $"-loglevel error -vsync 0 -f concat -r {rate} -i {vfrFilename} {encArgs} {vf} {extraArgs} -threads {Config.GetInt("ffEncThreads")} {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), logMode);
}
public static async Task ConcatVideos(string concatFile, string outPath, float fps, int looptimes = -1)
public static async Task ConcatVideos(string concatFile, string outPath, int looptimes = -1)
{
Logger.Log($"Merging videos...");
string loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : "";
string vfrFilename = Path.GetFileName(concatFile);
string args = $" {loopStr} -vsync 1 -f concat -r {fps.ToString().Replace(",", ".")} -i {vfrFilename} -c copy -pix_fmt yuv420p {outPath.Wrap()}";
string args = $" {loopStr} -vsync 1 -f concat -i {vfrFilename} -c copy -pix_fmt yuv420p -movflags +faststart {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, concatFile.GetParentDir(), AvProcess.LogMode.Hidden);
}
@ -127,13 +132,16 @@ namespace Flowframes
DeleteSource(inputPath);
}
public static async Task FramesToGifConcat(string framesFile, string outPath, float fps, bool palette, int colors = 64)
public static async Task FramesToGifConcat(string framesFile, string outPath, float fps, bool palette, int colors = 64, float resampleFps = -1, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine)
{
Logger.Log($"Encoding GIF...");
if (logMode != AvProcess.LogMode.Hidden)
Logger.Log((resampleFps <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.ToString().Replace(",", ".")} FPS...");
string vfrFilename = Path.GetFileName(framesFile);
string filter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither=floyd_steinberg:diff_mode=rectangle\"" : "";
string rate = fps.ToString().Replace(",", ".");
string args = $"-loglevel error -f concat -r {rate} -i {vfrFilename.Wrap()} -f gif {filter} {outPath.Wrap()}";
string paletteFilter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither=floyd_steinberg:diff_mode=rectangle\"" : "";
string fpsFilter = (resampleFps <= 0) ? "" : $"fps=fps={resampleFps.ToStringDot()}";
string vf = FormatUtils.ConcatStrings(new string[] { paletteFilter, fpsFilter });
string rate = fps.ToStringDot();
string args = $"-loglevel error -f concat -r {rate} -i {vfrFilename.Wrap()} -f gif {vf} {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.OnlyLastLine);
}

View File

@ -40,7 +40,7 @@ namespace Flowframes.AudioVideo
case Codec.H265: return "libx265";
case Codec.VP9: return "libvpx-vp9";
case Codec.ProRes: return "prores_ks";
case Codec.AviRaw: return "rawvideo";
case Codec.AviRaw: return Config.Get("aviCodec");
}
return "libx264";
}
@ -51,12 +51,12 @@ namespace Flowframes.AudioVideo
if(codec == Codec.H264)
{
args += $"-crf {Config.GetInt("h264Crf")} -preset {Config.Get("ffEncPreset")}";
args += $"-crf {Config.GetInt("h264Crf")} -preset {Config.Get("ffEncPreset")} -pix_fmt yuv420p";
}
if (codec == Codec.H265)
{
args += $"-crf {Config.GetInt("h265Crf")} -preset {Config.Get("ffEncPreset")}";
args += $"-crf {Config.GetInt("h265Crf")} -preset {Config.Get("ffEncPreset")} -pix_fmt yuv420p";
}
if (codec == Codec.VP9)
@ -64,12 +64,17 @@ namespace Flowframes.AudioVideo
int crf = Config.GetInt("vp9Crf");
string qualityStr = (crf > 0) ? $"-crf {crf}" : "-lossless 1";
string cpuUsed = Config.GetInt("vp9Speed", 3).ToString();
args += $"{qualityStr} -cpu-used {cpuUsed} -tile-columns 2 -tile-rows 2 -row-mt 1";
args += $"{qualityStr} -cpu-used {cpuUsed} -tile-columns 2 -tile-rows 2 -row-mt 1 -pix_fmt yuv420p";
}
if(codec == Codec.ProRes)
{
args += $"-profile:v {Config.GetInt("proResProfile")}";
args += $"-profile:v {Config.GetInt("proResProfile")} -pix_fmt yuv420p";
}
if (codec == Codec.AviRaw)
{
args += $"-pix_fmt {Config.Get("aviColors")}";
}
return args;

View File

@ -165,5 +165,20 @@ namespace Flowframes
{
return str.Split('#')[0].SplitBy("//")[0];
}
public static string FilenameSuffix(this string path, string suffix)
{
string filename = Path.ChangeExtension(path, null);
string ext = Path.GetExtension(path);
return filename + suffix + ext;
}
public static string ToStringDot (this float f, string format = "")
{
if(string.IsNullOrWhiteSpace(format))
return f.ToString().Replace(",", ".");
else
return f.ToString(format).Replace(",", ".");
}
}
}

View File

@ -151,6 +151,9 @@
this.cmdDebugMode = new System.Windows.Forms.ComboBox();
this.titleLabel = new System.Windows.Forms.Label();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.label60 = new System.Windows.Forms.Label();
this.aviCodec = new System.Windows.Forms.ComboBox();
this.aviColors = new System.Windows.Forms.ComboBox();
this.settingsTabList.SuspendLayout();
this.generalTab.SuspendLayout();
this.tabListPage2.SuspendLayout();
@ -963,6 +966,9 @@
// vidExportTab
//
this.vidExportTab.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.vidExportTab.Controls.Add(this.aviColors);
this.vidExportTab.Controls.Add(this.aviCodec);
this.vidExportTab.Controls.Add(this.label60);
this.vidExportTab.Controls.Add(this.proResProfile);
this.vidExportTab.Controls.Add(this.label59);
this.vidExportTab.Controls.Add(this.panel11);
@ -1161,8 +1167,8 @@
this.maxFpsMode.ForeColor = System.Drawing.Color.White;
this.maxFpsMode.FormattingEnabled = true;
this.maxFpsMode.Items.AddRange(new object[] {
"Save Video With Limited FPS",
"Save Both With Full FPS And Create Second Video With Limited FPS"});
"Only Create Video With Limited FPS",
"Create Videos With Full FPS And Limited FPS (2x Slower Encoding!)"});
this.maxFpsMode.Location = new System.Drawing.Point(280, 127);
this.maxFpsMode.Name = "maxFpsMode";
this.maxFpsMode.Size = new System.Drawing.Size(400, 21);
@ -1231,7 +1237,7 @@
//
this.label18.AutoSize = true;
this.label18.ForeColor = System.Drawing.Color.Silver;
this.label18.Location = new System.Drawing.Point(393, 361);
this.label18.Location = new System.Drawing.Point(393, 391);
this.label18.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7);
this.label18.Name = "label18";
this.label18.Size = new System.Drawing.Size(208, 13);
@ -1250,7 +1256,7 @@
"64 (Medium)",
"32 (Low)",
"16 (Very Low)"});
this.gifColors.Location = new System.Drawing.Point(280, 357);
this.gifColors.Location = new System.Drawing.Point(280, 387);
this.gifColors.Name = "gifColors";
this.gifColors.Size = new System.Drawing.Size(100, 21);
this.gifColors.TabIndex = 41;
@ -1258,7 +1264,7 @@
// label17
//
this.label17.AutoSize = true;
this.label17.Location = new System.Drawing.Point(10, 360);
this.label17.Location = new System.Drawing.Point(10, 390);
this.label17.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7);
this.label17.Name = "label17";
this.label17.Size = new System.Drawing.Size(154, 13);
@ -1663,6 +1669,49 @@
this.titleLabel.TabIndex = 1;
this.titleLabel.Text = "Settings";
//
// label60
//
this.label60.AutoSize = true;
this.label60.Location = new System.Drawing.Point(10, 360);
this.label60.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7);
this.label60.Name = "label60";
this.label60.Size = new System.Drawing.Size(130, 13);
this.label60.TabIndex = 69;
this.label60.Text = "AVI: Codec / Color Space";
//
// aviCodec
//
this.aviCodec.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.aviCodec.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.aviCodec.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.aviCodec.ForeColor = System.Drawing.Color.White;
this.aviCodec.FormattingEnabled = true;
this.aviCodec.Items.AddRange(new object[] {
"ffv1",
"huffyuv",
"rawvideo"});
this.aviCodec.Location = new System.Drawing.Point(280, 357);
this.aviCodec.Name = "aviCodec";
this.aviCodec.Size = new System.Drawing.Size(200, 21);
this.aviCodec.TabIndex = 70;
//
// aviColors
//
this.aviColors.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.aviColors.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.aviColors.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.aviColors.ForeColor = System.Drawing.Color.White;
this.aviColors.FormattingEnabled = true;
this.aviColors.Items.AddRange(new object[] {
"yuv420p",
"yuv422p",
"yuv444p",
"rgb24"});
this.aviColors.Location = new System.Drawing.Point(486, 357);
this.aviColors.Name = "aviColors";
this.aviColors.Size = new System.Drawing.Size(194, 21);
this.aviColors.TabIndex = 71;
//
// SettingsForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -1822,5 +1871,8 @@
private System.Windows.Forms.Label label57;
private System.Windows.Forms.ComboBox proResProfile;
private System.Windows.Forms.Label label59;
private System.Windows.Forms.ComboBox aviColors;
private System.Windows.Forms.ComboBox aviCodec;
private System.Windows.Forms.Label label60;
}
}

View File

@ -77,6 +77,8 @@ namespace Flowframes.Forms
ConfigParser.SaveGuiElement(vp9Crf);
ConfigParser.SaveComboxIndex(proResProfile);
ConfigParser.SaveGuiElement(gifColors);
ConfigParser.SaveGuiElement(aviCodec);
ConfigParser.SaveGuiElement(aviColors);
ConfigParser.SaveGuiElement(maxFps);
ConfigParser.SaveComboxIndex(maxFpsMode);
ConfigParser.SaveComboxIndex(loopMode);
@ -122,6 +124,8 @@ namespace Flowframes.Forms
ConfigParser.LoadGuiElement(vp9Crf);
ConfigParser.LoadComboxIndex(proResProfile);
ConfigParser.LoadGuiElement(gifColors);
ConfigParser.LoadGuiElement(aviCodec);
ConfigParser.LoadGuiElement(aviColors);
ConfigParser.LoadGuiElement(maxFps);
ConfigParser.LoadComboxIndex(maxFpsMode);
ConfigParser.LoadComboxIndex(loopMode);

View File

@ -126,6 +126,8 @@ namespace Flowframes.IO
if (key == "h265Crf") return WriteDefault(key, "24");
if (key == "vp9Crf") return WriteDefault(key, "28");
if (key == "proResProfile") return WriteDefault(key, "2");
if (key == "aviCodec") return WriteDefault(key, "ffv1");
if (key == "aviColors") return WriteDefault(key, "yuv420p");
if (key == "gifColors") return WriteDefault(key, "128 (High)");
if (key == "minVidLength") return WriteDefault(key, "2");
// AI

View File

@ -24,24 +24,25 @@ namespace Flowframes.Main
public static bool paused;
public static void UpdateSafetyBufferSize ()
public static void UpdateChunkAndBufferSizes ()
{
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.current.framesFolder, false, "*.png") * Interpolate.current.interpFactor);
safetyBufferFrames = Interpolate.current.ai.aiName.ToUpper().Contains("NCNN") ? 60 : 30; // Use bigger safety buffer for NCNN
}
public static async Task MainLoop(string interpFramesPath)
{
UpdateSafetyBufferSize();
UpdateChunkAndBufferSizes();
interpFramesFolder = interpFramesPath;
videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir);
if (Interpolate.currentlyUsingAutoEnc)
Directory.CreateDirectory(videoChunksFolder);
encodedFrameLines.Clear();
unencodedFrameLines.Clear();
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.current.framesFolder, false, "*.png") * Interpolate.current.interpFactor);
Logger.Log($"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");
interpFramesLines = IOUtils.ReadLines(encFile).Select(x => x.Split('/').Last().Remove("'")).ToArray(); // Array with frame filenames
@ -73,8 +74,6 @@ namespace Flowframes.Main
unencodedFrameLines.Add(vfrLine);
}
Directory.CreateDirectory(videoChunksFolder);
bool aiRunning = !AiProcess.currentAiProcess.HasExited;
if (unencodedFrameLines.Count >= (chunkSize + safetyBufferFrames) || !aiRunning) // Encode every n frames, or after process has exited
@ -83,12 +82,10 @@ namespace Flowframes.Main
List<int> frameLinesToEncode = aiRunning ? unencodedFrameLines.Take(chunkSize).ToList() : unencodedFrameLines; // Take all remaining frames if process is done
//Logger.Log($"{unencodedFrameLines.Count} unencoded frame lines, {IOUtils.GetAmountOfFiles(interpFramesFolder, false)} frames in interp folder", true, false, "ffmpeg");
Logger.Log($"Encoding Chunk #{videoIndex} using {Path.GetFileName(interpFramesLines[frameLinesToEncode.First()])} through {Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))}", true, false, "ffmpeg");
//IOUtils.ZeroPadDir(framesToEncode, Padding.interpFrames); // Zero-pad frames before encoding to make sure filenames match with VFR file
string outpath = Path.Combine(videoChunksFolder, $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}");
string outpath = Path.Combine(videoChunksFolder, "chunks", $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}");
int firstFrameNum = frameLinesToEncode[0];
Logger.Log($"Encoding Chunk #{videoIndex} to '{outpath}' using {Path.GetFileName(interpFramesLines[frameLinesToEncode.First()])} through {Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))}", true, false, "ffmpeg");
await CreateVideo.EncodeChunk(outpath, Interpolate.current.outMode, firstFrameNum, frameLinesToEncode.Count);
if(Interpolate.canceled) return;
@ -117,15 +114,9 @@ namespace Flowframes.Main
if (Interpolate.canceled) return;
string concatFile = Path.Combine(interpFramesPath.GetParentDir(), "chunks-concat.ini");
string concatFileContent = "";
foreach (string vid in IOUtils.GetFilesSorted(videoChunksFolder))
concatFileContent += $"file '{Paths.chunksDir}/{Path.GetFileName(vid)}'\n";
File.WriteAllText(concatFile, concatFileContent);
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
await CreateVideo.ChunksToVideo(videoChunksFolder, concatFile, Interpolate.current.outFilename);
await CreateVideo.ChunksToVideos(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outFilename);
}
public static bool HasWorkToDo ()

View File

@ -46,13 +46,16 @@ namespace Flowframes.Main
Program.mainForm.SetStatus("Creating output video from frames...");
try
{
int maxFps = Config.GetInt("maxFps");
if (maxFps == 0) maxFps = 500;
float maxFps = Config.GetFloat("maxFps");
bool fpsLimit = maxFps != 0 && i.current.outFps > maxFps;
if (i.current.outFps > maxFps)
await Encode(mode, path, outPath, i.current.outFps, maxFps, (Config.GetInt("maxFpsMode") == 1));
else
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt("maxFpsMode") == 0;
if(!dontEncodeFullFpsVid)
await Encode(mode, path, outPath, i.current.outFps);
if (fpsLimit)
await Encode(mode, path, outPath.FilenameSuffix($"-{maxFps.ToStringDot("0.00")}fps"), i.current.outFps, maxFps);
}
catch (Exception e)
{
@ -95,56 +98,51 @@ namespace Flowframes.Main
}
}
static async Task Encode(i.OutMode mode, string framesPath, string outPath, float fps, float changeFps = -1, bool keepOriginalFpsVid = true)
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");
if (mode == i.OutMode.VidGif)
{
await FFmpegCommands.FramesToGifConcat(vfrFile, outPath, fps, true, Config.GetInt("gifColors"));
await FFmpegCommands.FramesToGifConcat(vfrFile, outPath, fps, true, Config.GetInt("gifColors"), resampleFps);
}
else
{
int looptimes = GetLoopTimes();
bool h265 = Config.GetInt("mp4Enc") == 1;
int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf");
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, fps);
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, fps, resampleFps);
await MergeAudio(i.current.inPath, outPath);
if (changeFps > 0)
{
currentOutFile = IOUtils.FilenameSuffix(outPath, $"-{changeFps.ToString("0")}fps");
Program.mainForm.SetStatus("Creating video with desired frame rate...");
await FFmpegCommands.ConvertFramerate(outPath, currentOutFile, h265, crf, changeFps, !keepOriginalFpsVid);
await MergeAudio(i.current.inPath, currentOutFile);
}
int looptimes = GetLoopTimes();
if (looptimes > 0)
await Loop(currentOutFile, looptimes);
}
}
public static async Task ChunksToVideo(string chunksPath, string vfrFile, string outPath)
public static async Task ChunksToVideos(string tempFolder, string chunksFolder, string baseOutPath)
{
if (IOUtils.GetAmountOfFiles(chunksPath, false, $"*{FFmpegUtils.GetExt(i.current.outMode)}") < 1)
if (IOUtils.GetAmountOfFiles(chunksFolder, true, $"*{FFmpegUtils.GetExt(i.current.outMode)}") < 1)
{
i.Cancel("No video chunks found - An error must have occured during chunk encoding!", AiProcess.hasShownError);
return;
}
await Task.Delay(10);
Program.mainForm.SetStatus("Merging video chunks...");
try
{
int maxFps = Config.GetInt("maxFps");
if (maxFps == 0) maxFps = 500;
DirectoryInfo chunksDir = new DirectoryInfo(chunksFolder);
foreach(DirectoryInfo dir in chunksDir.GetDirectories())
{
string suffix = dir.Name.Replace("chunks", "");
string tempConcatFile = Path.Combine(tempFolder, $"chunks-concat{suffix}.ini");
string concatFileContent = "";
foreach (string vid in IOUtils.GetFilesSorted(dir.FullName))
concatFileContent += $"file '{Paths.chunksDir}/{dir.Name}/{Path.GetFileName(vid)}'\n";
File.WriteAllText(tempConcatFile, concatFileContent);
if (i.current.outFps > maxFps)
await MergeChunks(chunksPath, vfrFile, outPath, i.current.outFps, maxFps, (Config.GetInt("maxFpsMode") == 1));
else
await MergeChunks(chunksPath, vfrFile, outPath, i.current.outFps);
Logger.Log($"CreateVideo: Running MergeChunks() for vfrFile '{Path.GetFileName(tempConcatFile)}'", true);
await MergeChunks(tempConcatFile, baseOutPath.FilenameSuffix(suffix));
}
}
catch (Exception e)
{
@ -153,28 +151,14 @@ namespace Flowframes.Main
}
}
static async Task MergeChunks(string chunksPath, string vfrFile, string outPath, float fps, float changeFps = -1, bool keepOriginalFpsVid = true)
static async Task MergeChunks(string vfrFile, string outPath)
{
int looptimes = GetLoopTimes();
bool h265 = Config.GetInt("mp4Enc") == 1;
int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf");
await FFmpegCommands.ConcatVideos(vfrFile, outPath, fps, -1);
await FFmpegCommands.ConcatVideos(vfrFile, outPath, -1);
await MergeAudio(i.current.inPath, outPath);
int looptimes = GetLoopTimes();
if (looptimes > 0)
await Loop(outPath, looptimes);
if (changeFps > 0)
{
string newOutPath = IOUtils.FilenameSuffix(outPath, $"-{changeFps.ToString("0")}fps");
Program.mainForm.SetStatus("Creating video with desired frame rate...");
await FFmpegCommands.ConvertFramerate(outPath, newOutPath, h265, crf, changeFps, !keepOriginalFpsVid);
await MergeAudio(i.current.inPath, newOutPath);
if (looptimes > 0)
await Loop(newOutPath, looptimes);
}
}
public static async Task EncodeChunk(string outPath, i.OutMode mode, int firstFrameNum, int framesAmount)
@ -183,7 +167,21 @@ namespace Flowframes.Main
string vfrFile = Path.Combine(i.current.tempFolder, $"vfr-chunk-{firstFrameNum}-{firstFrameNum + framesAmount}.ini");
File.WriteAllLines(vfrFile, IOUtils.ReadLines(vfrFileOriginal).Skip(firstFrameNum).Take(framesAmount));
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, i.current.outFps, AvProcess.LogMode.Hidden, true);
float maxFps = Config.GetFloat("maxFps");
bool fpsLimit = maxFps != 0 && i.current.outFps > maxFps;
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt("maxFpsMode") == 0;
if(!dontEncodeFullFpsVid)
await FFmpegCommands.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
}
}
static async Task Loop(string outPath, int looptimes)

View File

@ -333,7 +333,7 @@ namespace Flowframes.Main
public static bool UseAutoEnc (bool stepByStep, InterpSettings current)
{
AutoEncode.UpdateSafetyBufferSize();
AutoEncode.UpdateChunkAndBufferSizes();
if (!current.outMode.ToString().ToLower().Contains("vid"))
{
@ -356,7 +356,7 @@ namespace Flowframes.Main
int inFrames = IOUtils.GetAmountOfFiles(current.framesFolder, false);
if (inFrames * current.interpFactor < (AutoEncode.chunkSize + AutoEncode.safetyBufferFrames) * 1.2f)
{
Logger.Log($"Not Using AutoEnc: Input frames ({inFrames}) * factor ({current.interpFactor}) is smaller (chunkSize ({AutoEncode.chunkSize}) + safetyBufferFrames ({AutoEncode.safetyBufferFrames}) * 1.2f", true);
Logger.Log($"Not Using AutoEnc: Input frames ({inFrames}) * factor ({current.interpFactor}) is smaller than (chunkSize ({AutoEncode.chunkSize}) + safetyBufferFrames ({AutoEncode.safetyBufferFrames}) * 1.2f)", true);
return false;
}