Fully implemented new output options, but no saving or custom q yet

This commit is contained in:
n00mkrad 2023-01-18 14:55:38 +01:00
parent 3fa47a70a4
commit 82ecb5e7ba
14 changed files with 215 additions and 80 deletions

View File

@ -18,5 +18,7 @@ namespace Flowframes.Data
public PixelFormat PixelFormatDefault { get; set; }
public bool IsImageSequence { get; set; } = false;
public string OverideExtension { get; set; } = "";
public List<string> QualityLevels { get; set; } = new List<string> ();
public int QualityDefault { get; set; } = 0;
}
}

View File

@ -14,7 +14,13 @@
public enum Codec { H264, H265, AV1, VP9, ProRes, Gif, Png, Jpeg, Webp, Ffv1, Huffyuv, Magicyuv, Rawvideo }
public enum Encoder { X264, X265, SvtAv1, VpxVp9, Nvenc264, Nvenc265, NvencAv1, ProResKs, Gif, Png, Jpeg, Webp, Ffv1, Huffyuv, Magicyuv, Rawvideo }
public enum PixelFormat { Yuv420P, Yuva420P, Yuv420P10Le, Yuv422P, Yuv422P10Le, Yuv444P, Yuv444P10Le, Yuva444P10Le, Rgb24, Rgba, Rgb8 };
public enum ProResProfiles { Proxy, Lt, Standard, Hq, Quad4, Quad4Xq }
public class Quality
{
public enum Common { Lossless, VeryHigh, High, Medium, Low, VeryLow }
public enum ProResProfile { Proxy, Lt, Standard, Hq, Quad4, Quad4Xq }
public enum GifColors { Max256, High128, Medium64, Low32, VeryLow16 }
}
}
}
}

View File

@ -6,10 +6,11 @@ using System.Threading.Tasks;
namespace Flowframes.Data
{
public class ExportSettings
public class OutputSettings
{
public Enums.Output.Format Format { get; set; }
public Enums.Encoding.Encoder Encoder { get; set; }
public Enums.Encoding.PixelFormat PixelFormat { get; set; }
public string Quality { get; set; } = "";
}
}

View File

@ -25,7 +25,7 @@ namespace Flowframes
public Fraction outFps;
public float outItsScale;
public float interpFactor;
public ExportSettings outSettings;
public OutputSettings outSettings;
public ModelCollection.ModelInfo model;
public string tempFolder;
@ -46,7 +46,7 @@ namespace Flowframes
public InterpSettings() { }
public InterpSettings(string inPathArg, string outPathArg, AI aiArg, Fraction inFpsDetectedArg, Fraction inFpsArg, float interpFactorArg, float itsScale, ExportSettings outSettingsArg, ModelCollection.ModelInfo modelArg)
public InterpSettings(string inPathArg, string outPathArg, AI aiArg, Fraction inFpsDetectedArg, Fraction inFpsArg, float interpFactorArg, float itsScale, OutputSettings outSettingsArg, ModelCollection.ModelInfo modelArg)
{
inPath = inPathArg;
outPath = outPathArg;
@ -95,7 +95,7 @@ namespace Flowframes
inFps = new Fraction();
interpFactor = 0;
outFps = new Fraction();
outSettings = new ExportSettings();
outSettings = new OutputSettings();
model = null;
alpha = false;
stepByStep = false;

View File

@ -50,5 +50,26 @@ namespace Flowframes.Data
{ Enums.Encoding.PixelFormat.Rgb8.ToString(), "RGB 256-color" },
{ Enums.Encoding.PixelFormat.Rgba.ToString(), "RGBA 8-bit" },
};
public static Dictionary<string, string> VideoQuality = new Dictionary<string, string>
{
{ Enums.Encoding.Quality.Common.Lossless.ToString(), "Lossless" },
{ Enums.Encoding.Quality.Common.VeryHigh.ToString(), "Very High" },
{ Enums.Encoding.Quality.Common.High.ToString(), "High" },
{ Enums.Encoding.Quality.Common.Medium.ToString(), "Medium" },
{ Enums.Encoding.Quality.Common.Low.ToString(), "Low" },
{ Enums.Encoding.Quality.Common.VeryLow.ToString(), "Very Low" },
{ Enums.Encoding.Quality.ProResProfile.Proxy.ToString(), "Proxy" },
{ Enums.Encoding.Quality.ProResProfile.Lt.ToString(), "LT" },
{ Enums.Encoding.Quality.ProResProfile.Standard.ToString(), "Standard" },
{ Enums.Encoding.Quality.ProResProfile.Hq.ToString(), "HQ" },
{ Enums.Encoding.Quality.ProResProfile.Quad4.ToString(), "4444" },
{ Enums.Encoding.Quality.ProResProfile.Quad4Xq.ToString(), "4444 XQ" },
{ Enums.Encoding.Quality.GifColors.Max256.ToString(), "Max (256)" },
{ Enums.Encoding.Quality.GifColors.High128.ToString(), "High (128)" },
{ Enums.Encoding.Quality.GifColors.Medium64.ToString(), "Medium (64)" },
{ Enums.Encoding.Quality.GifColors.Low32.ToString(), "Low (32)" },
{ Enums.Encoding.Quality.GifColors.VeryLow16.ToString(), "Very Low (16)" },
};
}
}

View File

@ -300,27 +300,37 @@ namespace Flowframes
public static void FillFromEnum<TEnum>(this ComboBox comboBox, Dictionary<string, string> stringMap = null, int defaultIndex = -1, List<TEnum> exclusionList = null) where TEnum : Enum
{
if (stringMap == null)
stringMap = new Dictionary<string, string>();
if (exclusionList == null)
exclusionList = new List<TEnum>();
comboBox.Items.Clear();
var entriesToAdd = Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Except(exclusionList);
comboBox.Items.AddRange(entriesToAdd.Select(x => stringMap.Get(x.ToString(), true)).ToArray());
if (defaultIndex >= 0)
comboBox.SelectedIndex = defaultIndex;
var strings = entriesToAdd.Select(x => stringMap.Get(x.ToString(), true));
comboBox.FillFromEnum(strings, stringMap, defaultIndex);
}
public static void FillFromEnum<TEnum>(this ComboBox comboBox, IEnumerable<TEnum> entries, Dictionary<string, string> stringMap = null, int defaultIndex = -1) where TEnum : Enum
{
var strings = entries.Select(x => stringMap.Get(x.ToString(), true));
comboBox.FillFromEnum(strings, stringMap, defaultIndex);
}
public static void FillFromEnum<TEnum>(this ComboBox comboBox, IEnumerable<TEnum> entries, Dictionary<string, string> stringMap, TEnum defaultEntry) where TEnum : Enum
{
if (stringMap == null)
stringMap = new Dictionary<string, string>();
comboBox.Items.Clear();
comboBox.Items.AddRange(entries.Select(x => stringMap.Get(x.ToString(), true)).ToArray());
comboBox.Text = stringMap.Get(defaultEntry.ToString(), true);
}
public static void FillFromEnum(this ComboBox comboBox, IEnumerable<string> entries, Dictionary<string, string> stringMap = null, int defaultIndex = -1)
{
if (stringMap == null)
stringMap = new Dictionary<string, string>();
comboBox.Items.Clear();
comboBox.Items.AddRange(entries.Select(x => stringMap.Get(x, true)).ToArray());
if (defaultIndex >= 0 && comboBox.Items.Count > 0)
comboBox.SelectedIndex = defaultIndex;

78
Code/Form1.Designer.cs generated
View File

@ -92,7 +92,8 @@
this.interpOptsTab = new System.Windows.Forms.TabPage();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.comboxOutputFormat = new System.Windows.Forms.ComboBox();
this.comboxOutputCrf = new System.Windows.Forms.ComboBox();
this.comboxOutputEncoder = new System.Windows.Forms.ComboBox();
this.comboxOutputQuality = new System.Windows.Forms.ComboBox();
this.comboxOutputColors = new System.Windows.Forms.ComboBox();
this.aiInfoBtn = new HTAlt.WinForms.HTButton();
this.outSpeedCombox = new System.Windows.Forms.ComboBox();
@ -141,7 +142,6 @@
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.pauseBtn = new System.Windows.Forms.Button();
this.cancelBtn = new System.Windows.Forms.Button();
this.comboxOutputEncoder = new System.Windows.Forms.ComboBox();
this.panel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit();
@ -965,7 +965,7 @@
//
this.flowLayoutPanel1.Controls.Add(this.comboxOutputFormat);
this.flowLayoutPanel1.Controls.Add(this.comboxOutputEncoder);
this.flowLayoutPanel1.Controls.Add(this.comboxOutputCrf);
this.flowLayoutPanel1.Controls.Add(this.comboxOutputQuality);
this.flowLayoutPanel1.Controls.Add(this.comboxOutputColors);
this.flowLayoutPanel1.Location = new System.Drawing.Point(281, 157);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
@ -995,18 +995,41 @@
this.comboxOutputFormat.TabIndex = 47;
this.comboxOutputFormat.SelectedIndexChanged += new System.EventHandler(this.comboxOutputFormat_SelectedIndexChanged);
//
// comboxOutputCrf
// comboxOutputEncoder
//
this.comboxOutputCrf.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.comboxOutputCrf.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.comboxOutputCrf.ForeColor = System.Drawing.Color.White;
this.comboxOutputCrf.FormattingEnabled = true;
this.comboxOutputCrf.Location = new System.Drawing.Point(182, 0);
this.comboxOutputCrf.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.comboxOutputCrf.Name = "comboxOutputCrf";
this.comboxOutputCrf.Size = new System.Drawing.Size(50, 23);
this.comboxOutputCrf.TabIndex = 48;
this.comboxOutputCrf.Text = "24";
this.comboxOutputEncoder.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.comboxOutputEncoder.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboxOutputEncoder.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.comboxOutputEncoder.ForeColor = System.Drawing.Color.White;
this.comboxOutputEncoder.FormattingEnabled = true;
this.comboxOutputEncoder.Items.AddRange(new object[] {
"MP4 Video (h264, h265, AV1)",
"MKV Video (h264, h265, AV1) (Best Audio/Subtitles Support)",
"WEBM Video (Google VP9)",
"MOV Video (Apple ProRes)",
"AVI Video (ffv1, huffyuv, magicyuv, rawvideo)",
"Animated GIF (Only supports up to 50 FPS)",
"Image Sequence (PNG, JPG, WEBP)",
"Real-time Interpolation (Video only)"});
this.comboxOutputEncoder.Location = new System.Drawing.Point(86, 0);
this.comboxOutputEncoder.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.comboxOutputEncoder.Name = "comboxOutputEncoder";
this.comboxOutputEncoder.Size = new System.Drawing.Size(90, 23);
this.comboxOutputEncoder.TabIndex = 50;
this.comboxOutputEncoder.SelectedIndexChanged += new System.EventHandler(this.comboxOutputEncoder_SelectedIndexChanged);
//
// comboxOutputQuality
//
this.comboxOutputQuality.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.comboxOutputQuality.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboxOutputQuality.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.comboxOutputQuality.ForeColor = System.Drawing.Color.White;
this.comboxOutputQuality.FormattingEnabled = true;
this.comboxOutputQuality.Location = new System.Drawing.Point(182, 0);
this.comboxOutputQuality.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.comboxOutputQuality.Name = "comboxOutputQuality";
this.comboxOutputQuality.Size = new System.Drawing.Size(100, 23);
this.comboxOutputQuality.TabIndex = 48;
//
// comboxOutputColors
//
@ -1015,7 +1038,7 @@
this.comboxOutputColors.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.comboxOutputColors.ForeColor = System.Drawing.Color.White;
this.comboxOutputColors.FormattingEnabled = true;
this.comboxOutputColors.Location = new System.Drawing.Point(238, 0);
this.comboxOutputColors.Location = new System.Drawing.Point(288, 0);
this.comboxOutputColors.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.comboxOutputColors.Name = "comboxOutputColors";
this.comboxOutputColors.Size = new System.Drawing.Size(110, 23);
@ -1708,29 +1731,6 @@
this.cancelBtn.UseVisualStyleBackColor = true;
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
//
// comboxOutputEncoder
//
this.comboxOutputEncoder.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.comboxOutputEncoder.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboxOutputEncoder.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.comboxOutputEncoder.ForeColor = System.Drawing.Color.White;
this.comboxOutputEncoder.FormattingEnabled = true;
this.comboxOutputEncoder.Items.AddRange(new object[] {
"MP4 Video (h264, h265, AV1)",
"MKV Video (h264, h265, AV1) (Best Audio/Subtitles Support)",
"WEBM Video (Google VP9)",
"MOV Video (Apple ProRes)",
"AVI Video (ffv1, huffyuv, magicyuv, rawvideo)",
"Animated GIF (Only supports up to 50 FPS)",
"Image Sequence (PNG, JPG, WEBP)",
"Real-time Interpolation (Video only)"});
this.comboxOutputEncoder.Location = new System.Drawing.Point(86, 0);
this.comboxOutputEncoder.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.comboxOutputEncoder.Name = "comboxOutputEncoder";
this.comboxOutputEncoder.Size = new System.Drawing.Size(90, 23);
this.comboxOutputEncoder.TabIndex = 50;
this.comboxOutputEncoder.SelectedIndexChanged += new System.EventHandler(this.comboxOutputEncoder_SelectedIndexChanged);
//
// Form1
//
this.AllowDrop = true;
@ -1917,7 +1917,7 @@
private HTAlt.WinForms.HTButton aiInfoBtn;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
public System.Windows.Forms.ComboBox comboxOutputFormat;
public System.Windows.Forms.ComboBox comboxOutputCrf;
public System.Windows.Forms.ComboBox comboxOutputQuality;
public System.Windows.Forms.ComboBox comboxOutputColors;
public System.Windows.Forms.ComboBox comboxOutputEncoder;
}

View File

@ -105,17 +105,23 @@ namespace Flowframes
var encoder = ParseUtils.GetEnum<Enums.Encoding.Encoder>(comboxOutputEncoder.Text, true, Strings.Encoder);
bool noEncoder = (int)encoder == -1;
comboxOutputCrf.Visible = !noEncoder;
comboxOutputQuality.Visible = !noEncoder;
comboxOutputColors.Visible = !noEncoder;
if (noEncoder)
return;
EncoderInfoVideo info = OutputUtils.GetEncoderInfoVideo(encoder);
comboxOutputCrf.Visible = !info.Lossless;
comboxOutputQuality.Visible = !info.Lossless;
comboxOutputQuality.Items.Clear();
if(info.QualityLevels.Count > 0)
comboxOutputQuality.FillFromEnum(info.QualityLevels, Strings.VideoQuality, info.QualityDefault);
var pixelFormats = info.PixelFormats;
comboxOutputColors.Visible = pixelFormats.Count > 0;
comboxOutputColors.FillFromEnum(pixelFormats, Strings.PixelFormat, 0);
comboxOutputColors.FillFromEnum(pixelFormats, Strings.PixelFormat, info.PixelFormatDefault);
}
async Task Checks()
@ -393,7 +399,7 @@ namespace Flowframes
Enums.Output.Format GetOutputFormat { get { return ParseUtils.GetEnum<Enums.Output.Format>(comboxOutputFormat.Text, true, Strings.OutputFormat); } }
Enums.Encoding.Encoder GetEncoder { get { return ParseUtils.GetEnum<Enums.Encoding.Encoder>(comboxOutputEncoder.Text, true, Strings.Encoder); } }
Enums.Encoding.PixelFormat GetPixelFormat { get { return ParseUtils.GetEnum<Enums.Encoding.PixelFormat>(comboxOutputColors.Text, true, Strings.PixelFormat); } }
ExportSettings GetExportSettings { get { return new ExportSettings() { Encoder = GetEncoder, Format = GetOutputFormat, PixelFormat = GetPixelFormat }; } }
OutputSettings GetExportSettings { get { return new OutputSettings() { Encoder = GetEncoder, Format = GetOutputFormat, PixelFormat = GetPixelFormat, Quality = comboxOutputQuality.Text }; } }
public void SetFormat(Enums.Output.Format format)
{

View File

@ -22,7 +22,7 @@ namespace Flowframes.Main
{
public static async Task ExportFrames(string path, string outFolder, ExportSettings exportSettings, bool stepByStep)
public static async Task ExportFrames(string path, string outFolder, OutputSettings exportSettings, bool stepByStep)
{
if(Config.GetInt(Config.Key.sceneChangeFillMode) == 1)
{
@ -76,7 +76,7 @@ namespace Flowframes.Main
public static async Task<string> GetPipedFfmpegCmd(bool ffplay = false)
{
InterpSettings s = I.currentSettings;
string encArgs = FfmpegUtils.GetEncArgs(s.outSettings.Encoder, s.outSettings.PixelFormat, (s.ScaledResolution.IsEmpty ? s.InputResolution : s.ScaledResolution), s.outFps.GetFloat(), true).FirstOrDefault();
string encArgs = FfmpegUtils.GetEncArgs(s.outSettings, (s.ScaledResolution.IsEmpty ? s.InputResolution : s.ScaledResolution), s.outFps.GetFloat(), true).FirstOrDefault();
string max = Config.Get(Config.Key.maxFps);
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
@ -196,7 +196,7 @@ namespace Flowframes.Main
}
}
static async Task Encode(ExportSettings settings, string framesPath, string outPath, Fraction fps, Fraction resampleFps)
static async Task Encode(OutputSettings settings, string framesPath, string outPath, Fraction fps, Fraction resampleFps)
{
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
@ -209,7 +209,8 @@ namespace Flowframes.Main
if (settings.Format == Enums.Output.Format.Gif)
{
await FfmpegEncode.FramesToGifConcat(framesFile, outPath, fps, true, Config.GetInt(Config.Key.gifColors), resampleFps, I.currentSettings.outItsScale);
int paletteColors = OutputUtils.GetGifColors(ParseUtils.GetEnum<Enums.Encoding.Quality.GifColors>(settings.Quality, true, Strings.VideoQuality));
await FfmpegEncode.FramesToGifConcat(framesFile, outPath, fps, true, paletteColors, resampleFps, I.currentSettings.outItsScale);
}
else
{
@ -289,7 +290,7 @@ namespace Flowframes.Main
await Loop(outPath, await GetLoopTimes());
}
public static async Task EncodeChunk(string outPath, string interpDir, int chunkNo, ExportSettings settings, int firstFrameNum, int framesAmount)
public static async Task EncodeChunk(string outPath, string interpDir, int chunkNo, OutputSettings settings, int firstFrameNum, int framesAmount)
{
string framesFileFull = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
string concatFile = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilenameChunk(firstFrameNum, firstFrameNum + framesAmount));

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
using static Flowframes.AvProcess;
using Utils = Flowframes.Media.FfmpegUtils;
@ -13,14 +14,14 @@ namespace Flowframes.Media
{
partial class FfmpegEncode : FfmpegCommands
{
public static async Task FramesToVideo(string framesFile, string outPath, ExportSettings settings, Fraction fps, Fraction resampleFps, float itsScale, VidExtraData extraData, LogMode logMode = LogMode.OnlyLastLine, bool isChunk = false)
public static async Task FramesToVideo(string framesFile, string outPath, OutputSettings settings, Fraction fps, Fraction resampleFps, float itsScale, VidExtraData extraData, LogMode logMode = LogMode.OnlyLastLine, bool isChunk = false)
{
if (logMode != LogMode.Hidden)
Logger.Log((resampleFps.GetFloat() <= 0) ? "Encoding video..." : $"Encoding video resampled to {resampleFps.GetString()} FPS...");
IoUtils.RenameExistingFile(outPath);
Directory.CreateDirectory(outPath.GetParentDir());
string[] encArgs = Utils.GetEncArgs(settings.Encoder, settings.PixelFormat, (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat());
string[] encArgs = Utils.GetEncArgs(settings, (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat());
string inArg = $"-f concat -i {Path.GetFileName(framesFile)}";
string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix);
@ -50,7 +51,7 @@ namespace Flowframes.Media
return $"-r {fps}";
}
public static string GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, ExportSettings settings, bool isChunk = false)
public static string GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, OutputSettings settings, bool isChunk = false)
{
List<string> filters = new List<string>();
string extraArgs = Config.Get(Config.Key.ffEncArgs);
@ -71,6 +72,14 @@ namespace Flowframes.Media
if (!isChunk && settings.Format == Enums.Output.Format.Mp4)
extraArgs += $" -movflags +faststart";
if(settings.Format == Enums.Output.Format.Gif)
{
string dither = Config.Get(Config.Key.gifDitherType).Split(' ').First();
int colors = OutputUtils.GetGifColors(ParseUtils.GetEnum<Enums.Encoding.Quality.GifColors>(settings.Quality, true, Strings.VideoQuality));
string paletteFilter = $"\"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither={dither}\"";
filters.Add(paletteFilter);
}
filters.Add(GetPadFilter());
return filters.Count > 0 ? $"-vf {string.Join(",", filters)}" : "" + $" {extraArgs}";
}

View File

@ -171,8 +171,10 @@ namespace Flowframes.Media
return false;
}
public static string[] GetEncArgs(Encoder enc, PixelFormat pixFmt, Size res, float fps, bool realtime = false) // Array contains as many entries as there are encoding passes. If "realtime" is true, force single pass.
public static string[] GetEncArgs(OutputSettings settings, Size res, float fps, bool realtime = false) // Array contains as many entries as there are encoding passes. If "realtime" is true, force single pass.
{
Encoder enc = settings.Encoder;
PixelFormat pixFmt = settings.PixelFormat;
int keyint = 10;
var args = new List<string>();
@ -189,25 +191,26 @@ namespace Flowframes.Media
if (enc == Encoder.X264)
{
string preset = Config.Get(Config.Key.ffEncPreset).ToLowerInvariant().Remove(" "); // TODO: Replace this ugly stuff with enums
args.Add($"-crf {Config.GetInt(Config.Key.h264Crf)} -preset {preset}");
int crf = OutputUtils.GetCrf(ParseUtils.GetEnum<Quality.Common>(settings.Quality, true, Strings.VideoQuality), enc);
args.Add($"-crf {crf} -preset {preset}");
}
if (enc == Encoder.X265)
{
string preset = Config.Get(Config.Key.ffEncPreset).ToLowerInvariant().Remove(" "); // TODO: Replace this ugly stuff with enums
int crf = Config.GetInt(Config.Key.h265Crf);
int crf = OutputUtils.GetCrf(ParseUtils.GetEnum<Quality.Common>(settings.Quality, true, Strings.VideoQuality), enc);
args.Add($"{(crf > 0 ? $"-crf {crf}" : "-x265-params lossless=1")} -preset {preset}");
}
if (enc == Encoder.SvtAv1)
{
int cq = Config.GetInt(Config.Key.av1Crf);
args.Add($"-b:v 0 -qp {cq} {GetSvtAv1Speed()} -svtav1-params enable-qm=1:enable-overlays=1:enable-tf=0:scd=0");
int crf = OutputUtils.GetCrf(ParseUtils.GetEnum<Quality.Common>(settings.Quality, true, Strings.VideoQuality), enc);
args.Add($"-crf {crf} {GetSvtAv1Speed()} -svtav1-params enable-qm=1:enable-overlays=1:enable-tf=0:scd=0");
}
if (enc == Encoder.VpxVp9)
{
int crf = Config.GetInt(Config.Key.vp9Crf);
int crf = OutputUtils.GetCrf(ParseUtils.GetEnum<Quality.Common>(settings.Quality, true, Strings.VideoQuality), enc);
string qualityStr = (crf > 0) ? $"-crf {crf}" : "-lossless 1";
string t = GetTilingArgs(res, "-tile-columns ", "-tile-rows ");
@ -226,20 +229,20 @@ namespace Flowframes.Media
if (enc == Encoder.Nvenc264)
{
int cq = (Config.GetInt(Config.Key.h264Crf) * 1.1f).RoundToInt();
args.Add($"-b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")}");
int crf = OutputUtils.GetCrf(ParseUtils.GetEnum<Quality.Common>(settings.Quality, true, Strings.VideoQuality), enc);
args.Add($"-b:v 0 {(crf > 0 ? $"-cq {crf} -preset p7" : "-preset lossless")}");
}
if (enc == Encoder.Nvenc265)
{
int cq = (Config.GetInt(Config.Key.h265Crf) * 1.1f).RoundToInt();
args.Add($"-b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")}");
int crf = OutputUtils.GetCrf(ParseUtils.GetEnum<Quality.Common>(settings.Quality, true, Strings.VideoQuality), enc);
args.Add($"-b:v 0 {(crf > 0 ? $"-cq {crf} -preset p7" : "-preset lossless")}");
}
if (enc == Encoder.NvencAv1)
{
int cq = (Config.GetInt(Config.Key.av1Crf) * 1.1f).RoundToInt();
args.Add($"-b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")}");
int crf = OutputUtils.GetCrf(ParseUtils.GetEnum<Quality.Common>(settings.Quality, true, Strings.VideoQuality), enc);
args.Add($"-b:v 0 -preset p7 {(crf > 0 ? $"-cq {crf}" : "-tune lossless")}"); // Lossless not supported as of Jan 2023!!
}
if (enc == Encoder.ProResKs)
@ -348,7 +351,7 @@ namespace Flowframes.Media
return supported;
}
public static string GetExt(ExportSettings settings, bool dot = true)
public static string GetExt(OutputSettings settings, bool dot = true)
{
string ext = dot ? "." : "";
EncoderInfoVideo info = settings.Encoder.GetInfo();

View File

@ -23,6 +23,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.H264,
Name = "libx264",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P },
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
QualityDefault = (int)Quality.Common.VeryHigh,
};
}
@ -33,6 +35,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.H265,
Name = "libx265",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P10Le },
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
QualityDefault = (int)Quality.Common.VeryHigh,
};
}
@ -43,6 +47,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.H264,
Name = "h264_nvenc",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P },
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
QualityDefault = (int)Quality.Common.VeryHigh,
HwAccelerated = true,
};
}
@ -55,6 +61,8 @@ namespace Flowframes.MiscUtils
Name = "libsvtav1",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv420P10Le },
PixelFormatDefault = PixFmt.Yuv420P10Le,
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
QualityDefault = (int)Quality.Common.VeryHigh,
MaxFramerate = 240,
};
}
@ -66,6 +74,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.VP9,
Name = "libvpx-vp9",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P, PixFmt.Yuv444P10Le },
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
QualityDefault = (int)Quality.Common.VeryHigh,
};
}
@ -76,6 +86,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.H265,
Name = "hevc_nvenc",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le },
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
QualityDefault = (int)Quality.Common.VeryHigh,
HwAccelerated = true,
};
}
@ -87,6 +99,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.AV1,
Name = "av1_nvenc",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le },
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
QualityDefault = (int)Quality.Common.VeryHigh,
PixelFormatDefault = PixFmt.Yuv420P10Le,
HwAccelerated = true,
};
@ -99,6 +113,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.ProRes,
Name = "prores_ks",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv422P10Le, PixFmt.Yuv444P10Le, PixFmt.Yuva444P10Le },
QualityLevels = ParseUtils.GetEnumStrings<Quality.ProResProfile>(),
QualityDefault = (int)Quality.ProResProfile.Hq,
};
}
@ -109,6 +125,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.Gif,
Name = "gif",
PixelFormats = new List<PixFmt>() { PixFmt.Rgb8 },
QualityLevels = ParseUtils.GetEnumStrings<Quality.GifColors>(),
QualityDefault = (int)Quality.GifColors.High128,
OverideExtension = "gif",
MaxFramerate = 50,
};
@ -199,7 +217,7 @@ namespace Flowframes.MiscUtils
public static List<Codec> GetSupportedCodecs(Enums.Output.Format format)
{
switch(format)
switch (format)
{
case Enums.Output.Format.Mp4: return new List<Codec> { Codec.H264, Codec.H265, Codec.AV1 };
case Enums.Output.Format.Mkv: return new List<Codec> { Codec.H264, Codec.H265, Codec.AV1, Codec.VP9 };
@ -216,8 +234,53 @@ namespace Flowframes.MiscUtils
public static List<Encoder> GetAvailableEncoders(Enums.Output.Format format)
{
var allEncoders = Enum.GetValues(typeof(Encoder)).Cast<Encoder>();
var supported = GetSupportedCodecs(format);
return allEncoders.Where(e => supported.Contains(GetEncoderInfoVideo(e).Codec)).ToList();
var supportedCodecs = GetSupportedCodecs(format);
var availableEncoders = supportedCodecs.SelectMany(codec => allEncoders.Where(enc => enc.GetInfo().Codec == codec));
return availableEncoders.ToList();
}
public static int GetCrf (Quality.Common qualityLevel, Encoder encoder)
{
int baseCrf = Crfs[qualityLevel];
float multiplier = 1f;
if (encoder == Encoder.X265)
multiplier = 1.0f;
if (encoder == Encoder.VpxVp9)
multiplier = 1.3f;
if (encoder == Encoder.SvtAv1)
multiplier = 1.3f;
if (encoder == Encoder.Nvenc264)
multiplier = 1.1f;
if (encoder == Encoder.Nvenc265)
multiplier = 1.15f;
if (encoder == Encoder.NvencAv1)
multiplier = 1.3f;
return (baseCrf * multiplier).RoundToInt();
}
public static int GetGifColors (Quality.GifColors qualityLevel)
{
switch (qualityLevel)
{
case Quality.GifColors.Max256: return 256;
case Quality.GifColors.High128: return 128;
case Quality.GifColors.Medium64: return 64;
case Quality.GifColors.Low32: return 32;
case Quality.GifColors.VeryLow16: return 16;
default: return 128;
}
}
public static Dictionary<Quality.Common, int> Crfs = new Dictionary<Quality.Common, int>
{
{ Quality.Common.Lossless, 0 },
{ Quality.Common.VeryHigh, 16 },
{ Quality.Common.High, 20 },
{ Quality.Common.Medium, 26 },
{ Quality.Common.Low, 32 },
{ Quality.Common.VeryLow, 40 },
};
}
}

View File

@ -34,5 +34,11 @@ namespace Flowframes.MiscUtils
return (TEnum)(object)(-1);
}
public static List<string> GetEnumStrings<TEnum>() where TEnum : Enum
{
var entries = Enum.GetValues(typeof(TEnum)).Cast<TEnum>();
return entries.Select(e => e.ToString()).ToList();
}
}
}

View File

@ -1,3 +1,10 @@
Flowframes 1.40.0 Changelog:
- New: Added RIFE 4.6 model
- Improved: Updated ffmpeg, includes SVT-AV1 1.4.1 which speeds up encoding by ~12%
- Fixed: RIFE-NCNN-VS did not work with input resolutions not divisible by 2
- Fixed: Issues with certain system languages (e.g. Turkish)
Flowframes 1.39.0 Changelog:
- Added real-time interpolation output mode (with RIFE-NCNN-VS)
- Added RIFE 4.4 and RIFE 4.3 models