Completely revamped output settings (quality settings WIP)

This commit is contained in:
n00mkrad
2023-01-15 17:23:49 +01:00
parent f5536e07f3
commit 3fa47a70a4
25 changed files with 801 additions and 261 deletions

20
Code/Data/EncoderInfo.cs Normal file
View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Data
{
public class EncoderInfo
{
public string Name { get; set; } = "unknown";
public EncoderInfo() { }
public EncoderInfo(string name)
{
Name = name;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Flowframes.Data.Enums.Encoding;
namespace Flowframes.Data
{
public class EncoderInfoVideo : EncoderInfo
{
public Codec Codec { get; set; } = (Codec)(-1);
public bool Lossless { get; set; } = false;
public bool HwAccelerated { get; set; } = false;
public int Modulo { get; set; } = 2;
public int MaxFramerate { get; set; } = 1000;
public List<PixelFormat> PixelFormats { get; set; } = new List<PixelFormat>();
public PixelFormat PixelFormatDefault { get; set; }
public bool IsImageSequence { get; set; } = false;
public string OverideExtension { get; set; } = "";
}
}

20
Code/Data/Enums.cs Normal file
View File

@@ -0,0 +1,20 @@
namespace Flowframes.Data
{
public class Enums
{
public class Output
{
public enum Format { Mp4, Mkv, Webm, Mov, Avi, Gif, Images, Realtime };
public enum ImageFormat { Png, Jpeg, Webp };
public enum Dithering { None, Bayer, FloydSteinberg };
}
public class Encoding
{
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 }
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Data
{
public class ExportSettings
{
public Enums.Output.Format Format { get; set; }
public Enums.Encoding.Encoder Encoder { get; set; }
public Enums.Encoding.PixelFormat PixelFormat { get; set; }
}
}

View File

@@ -25,7 +25,7 @@ namespace Flowframes
public Fraction outFps;
public float outItsScale;
public float interpFactor;
public Interpolate.OutMode outMode;
public ExportSettings 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, Interpolate.OutMode outModeArg, ModelCollection.ModelInfo modelArg)
public InterpSettings(string inPathArg, string outPathArg, AI aiArg, Fraction inFpsDetectedArg, Fraction inFpsArg, float interpFactorArg, float itsScale, ExportSettings outSettingsArg, ModelCollection.ModelInfo modelArg)
{
inPath = inPathArg;
outPath = outPathArg;
@@ -56,7 +56,7 @@ namespace Flowframes
interpFactor = interpFactorArg;
outFps = inFpsArg * (double)interpFactorArg;
outItsScale = itsScale;
outMode = outModeArg;
outSettings = outSettingsArg;
model = modelArg;
alpha = false;
@@ -95,7 +95,7 @@ namespace Flowframes
inFps = new Fraction();
interpFactor = 0;
outFps = new Fraction();
outMode = Interpolate.OutMode.VidMp4;
outSettings = new ExportSettings();
model = null;
alpha = false;
stepByStep = false;
@@ -112,6 +112,7 @@ namespace Flowframes
entries.Add(keyValuePair[0], keyValuePair[1]);
}
// TODO: Rework this ugly stuff, JSON?
foreach (KeyValuePair<string, string> entry in entries)
{
switch (entry.Key)
@@ -123,7 +124,7 @@ namespace Flowframes
case "INFPS": inFps = new Fraction(entry.Value); break;
case "OUTFPS": outFps = new Fraction(entry.Value); break;
case "INTERPFACTOR": interpFactor = float.Parse(entry.Value); break;
case "OUTMODE": outMode = (Interpolate.OutMode)Enum.Parse(typeof(Interpolate.OutMode), entry.Value); break;
case "OUTMODE": outSettings.Format = (Enums.Output.Format)Enum.Parse(typeof(Enums.Output.Format), entry.Value); break;
case "MODEL": model = AiModels.GetModelByName(ai, entry.Value); break;
case "INPUTRES": _inputResolution = FormatUtils.ParseSize(entry.Value); break;
case "ALPHA": alpha = bool.Parse(entry.Value); break;
@@ -173,14 +174,13 @@ namespace Flowframes
try
{
bool alphaModel = model.SupportsAlpha;
bool png = outMode == Interpolate.OutMode.ImgPng;
bool gif = outMode == Interpolate.OutMode.VidGif;
bool proResAlpha = outMode == Interpolate.OutMode.VidProRes && Config.GetInt(Config.Key.proResProfile) > 3;
bool outputSupportsAlpha = png || gif || proResAlpha;
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
bool gifOutput = outSettings.Encoder == Enums.Encoding.Encoder.Gif;
bool proResAlpha = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && Config.GetInt(Config.Key.proResProfile) > 3; // TODO: CHECK IF WORKS WITH NEW ENCODING SETTINGS CODE
bool outputSupportsAlpha = pngOutput || gifOutput || proResAlpha;
string ext = inputIsFrames ? Path.GetExtension(IoUtils.GetFilesSorted(inPath).First()).ToLowerInvariant() : Path.GetExtension(inPath).ToLowerInvariant();
alpha = (alphaModel && outputSupportsAlpha && (ext == ".gif" || ext == ".png" || ext == ".apng" || ext == ".mov"));
Logger.Log($"RefreshAlpha: model.supportsAlpha = {alphaModel} - outputSupportsAlpha = {outputSupportsAlpha} - " +
$"input ext: {ext} => alpha = {alpha}", true);
Logger.Log($"RefreshAlpha: model.supportsAlpha = {alphaModel} - outputSupportsAlpha = {outputSupportsAlpha} - input ext: {ext} => alpha = {alpha}", true);
}
catch (Exception e)
{
@@ -193,9 +193,9 @@ namespace Flowframes
public void RefreshExtensions(FrameType type = FrameType.Both)
{
bool pngOutput = outMode == Interpolate.OutMode.ImgPng;
bool aviHqChroma = outMode == Interpolate.OutMode.VidAvi && Config.Get(Config.Key.aviColors) != "yuv420p";
bool proresHqChroma = outMode == Interpolate.OutMode.VidProRes && Config.GetInt(Config.Key.proResProfile) > 3;
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
bool aviHqChroma = outSettings.Format == Enums.Output.Format.Avi && outSettings.PixelFormat != Enums.Encoding.PixelFormat.Yuv420P; // TODO: CHECK IF WORKS WITH NEW ENCODING SETTINGS CODE
bool proresHqChroma = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && Config.GetInt(Config.Key.proResProfile) > 3; // TODO: CHECK IF WORKS WITH NEW ENCODING SETTINGS CODE
bool forceHqChroma = pngOutput || aviHqChroma || proresHqChroma;
@@ -230,7 +230,7 @@ namespace Flowframes
s += $"INFPS|{inFps}\n";
s += $"OUTFPS|{outFps}\n";
s += $"INTERPFACTOR|{interpFactor}\n";
s += $"OUTMODE|{outMode}\n";
s += $"OUTMODE|{outSettings.Format}\n";
s += $"MODEL|{model.Name}\n";
s += $"INPUTRES|{InputResolution.Width}x{InputResolution.Height}\n";
s += $"OUTPUTRES|{ScaledResolution.Width}x{ScaledResolution.Height}\n";

54
Code/Data/Strings.cs Normal file
View File

@@ -0,0 +1,54 @@
using System.Collections.Generic;
namespace Flowframes.Data
{
public class Strings
{
public static Dictionary<string, string> OutputFormat = new Dictionary<string, string>
{
{ Enums.Output.Format.Mp4.ToString(), "MP4" },
{ Enums.Output.Format.Mkv.ToString(), "MKV" },
{ Enums.Output.Format.Webm.ToString(), "WEBM" },
{ Enums.Output.Format.Mov.ToString(), "MOV" },
{ Enums.Output.Format.Avi.ToString(), "AVI" },
{ Enums.Output.Format.Gif.ToString(), "GIF" },
{ Enums.Output.Format.Images.ToString(), "Images" },
{ Enums.Output.Format.Realtime.ToString(), "Real-time" },
};
public static Dictionary<string, string> Encoder = new Dictionary<string, string>
{
{ Enums.Encoding.Encoder.X264.ToString(), "h264" },
{ Enums.Encoding.Encoder.X265.ToString(), "h265" },
{ Enums.Encoding.Encoder.SvtAv1.ToString(), "AV1" },
{ Enums.Encoding.Encoder.VpxVp9.ToString(), "VP9" },
{ Enums.Encoding.Encoder.ProResKs.ToString(), "ProRes" },
{ Enums.Encoding.Encoder.Nvenc264.ToString(), "h264 (NVENC)" },
{ Enums.Encoding.Encoder.Nvenc265.ToString(), "h265 (NVENC)" },
{ Enums.Encoding.Encoder.NvencAv1.ToString(), "AV1 (NVENC)" },
{ Enums.Encoding.Encoder.Gif.ToString(), "Animated GIF" },
{ Enums.Encoding.Encoder.Png.ToString(), "PNG" },
{ Enums.Encoding.Encoder.Jpeg.ToString(), "JPEG" },
{ Enums.Encoding.Encoder.Webp.ToString(), "WEBP" },
{ Enums.Encoding.Encoder.Ffv1.ToString(), "FFV1" },
{ Enums.Encoding.Encoder.Huffyuv.ToString(), "HuffYUV" },
{ Enums.Encoding.Encoder.Magicyuv.ToString(), "MagicYUV" },
{ Enums.Encoding.Encoder.Rawvideo.ToString(), "Raw Video" },
};
public static Dictionary<string, string> PixelFormat = new Dictionary<string, string>
{
{ Enums.Encoding.PixelFormat.Yuv420P.ToString(), "YUV 4:2:0 8-bit" },
{ Enums.Encoding.PixelFormat.Yuva420P.ToString(), "YUVA 4:2:0 8-bit" },
{ Enums.Encoding.PixelFormat.Yuv420P10Le.ToString(), "YUV 4:2:0 10-bit" },
{ Enums.Encoding.PixelFormat.Yuv422P.ToString(), "YUV 4:2:2 8-bit" },
{ Enums.Encoding.PixelFormat.Yuv422P10Le.ToString(), "YUV 4:2:2 10-bit" },
{ Enums.Encoding.PixelFormat.Yuv444P.ToString(), "YUV 4:4:4 8-bit" },
{ Enums.Encoding.PixelFormat.Yuv444P10Le.ToString(), "YUV 4:4:4 10-bit" },
{ Enums.Encoding.PixelFormat.Yuva444P10Le.ToString(), "YUVA 4:4:4 10-bit" },
{ Enums.Encoding.PixelFormat.Rgb24.ToString(), "RGB 8-bit" },
{ Enums.Encoding.PixelFormat.Rgb8.ToString(), "RGB 256-color" },
{ Enums.Encoding.PixelFormat.Rgba.ToString(), "RGBA 8-bit" },
};
}
}

View File

@@ -10,6 +10,7 @@ using System.Windows.Forms;
using Flowframes.Data;
using System.Management.Automation;
using System.Drawing;
using Flowframes.MiscUtils;
namespace Flowframes
{
@@ -271,5 +272,99 @@ namespace Flowframes
{
return $"{(filePath.IsConcatFile() ? filePath.GetConcStr() : "")} -i {filePath.Wrap()}";
}
public static string Get(this Dictionary<string, string> dict, string key, bool returnKeyInsteadOfEmptyString = false, bool ignoreCase = false)
{
if (key == null)
key = "";
for (int i = 0; i < dict.Count; i++)
{
if (ignoreCase)
{
if (key.Lower() == dict.ElementAt(i).Key.Lower())
return dict.ElementAt(i).Value;
}
else
{
if (key == dict.ElementAt(i).Key)
return dict.ElementAt(i).Value;
}
}
if (returnKeyInsteadOfEmptyString)
return key;
else
return "";
}
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;
}
public static void FillFromEnum<TEnum>(this ComboBox comboBox, IEnumerable<TEnum> entries, Dictionary<string, string> stringMap = null, int defaultIndex = -1) 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());
if (defaultIndex >= 0 && comboBox.Items.Count > 0)
comboBox.SelectedIndex = defaultIndex;
}
public static void SetIfTextMatches(this ComboBox comboBox, string str, bool ignoreCase = true, Dictionary<string, string> stringMap = null)
{
if (stringMap == null)
stringMap = new Dictionary<string, string>();
str = stringMap.Get(str, true, true);
for (int i = 0; i < comboBox.Items.Count; i++)
{
if (ignoreCase)
{
if (comboBox.Items[i].ToString().Lower() == str.Lower())
{
comboBox.SelectedIndex = i;
return;
}
}
else
{
if (comboBox.Items[i].ToString() == str)
{
comboBox.SelectedIndex = i;
return;
}
}
}
}
public static string Lower(this string s)
{
if (s == null)
return s;
return s.ToLowerInvariant();
}
public static EncoderInfoVideo GetInfo (this Enums.Encoding.Encoder enc)
{
return OutputUtils.GetEncoderInfoVideo(enc);
}
}
}

View File

@@ -334,6 +334,10 @@
<ItemGroup>
<Compile Include="Data\AI.cs" />
<Compile Include="Data\AudioTrack.cs" />
<Compile Include="Data\EncoderInfo.cs" />
<Compile Include="Data\EncoderInfoVideo.cs" />
<Compile Include="Data\Enums.cs" />
<Compile Include="Data\ExportSettings.cs" />
<Compile Include="Data\Filetypes.cs" />
<Compile Include="Data\MediaFile.cs" />
<Compile Include="Data\ModelCollection.cs" />
@@ -344,6 +348,7 @@
<Compile Include="Data\Streams\Stream.cs" />
<Compile Include="Data\Streams\SubtitleStream.cs" />
<Compile Include="Data\Streams\VideoStream.cs" />
<Compile Include="Data\Strings.cs" />
<Compile Include="Data\VideoColorData.cs" />
<Compile Include="Data\VidExtraData.cs" />
<Compile Include="Data\Fraction.cs" />
@@ -439,6 +444,8 @@
<Compile Include="MiscUtils\FrameRename.cs" />
<Compile Include="MiscUtils\ModelDownloadFormUtils.cs" />
<Compile Include="MiscUtils\NmkdStopwatch.cs" />
<Compile Include="MiscUtils\OutputUtils.cs" />
<Compile Include="MiscUtils\ParseUtils.cs" />
<Compile Include="Os\AiProcess.cs" />
<Compile Include="Extensions\ExtensionMethods.cs" />
<Compile Include="Form1.cs">

100
Code/Form1.Designer.cs generated
View File

@@ -90,6 +90,10 @@
this.label15 = new System.Windows.Forms.Label();
this.label11 = new System.Windows.Forms.Label();
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.comboxOutputColors = new System.Windows.Forms.ComboBox();
this.aiInfoBtn = new HTAlt.WinForms.HTButton();
this.outSpeedCombox = new System.Windows.Forms.ComboBox();
this.completionActionPanel = new System.Windows.Forms.Panel();
@@ -137,6 +141,7 @@
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();
@@ -149,6 +154,7 @@
this.panel8.SuspendLayout();
this.panel6.SuspendLayout();
this.interpOptsTab.SuspendLayout();
this.flowLayoutPanel1.SuspendLayout();
this.completionActionPanel.SuspendLayout();
this.quickSettingsTab.SuspendLayout();
this.mpDedupePanel.SuspendLayout();
@@ -228,7 +234,7 @@
"Animated GIF (Only supports up to 50 FPS)",
"Image Sequence (PNG, JPG, WEBP)",
"Real-time Interpolation (Video only)"});
this.outModeCombox.Location = new System.Drawing.Point(281, 157);
this.outModeCombox.Location = new System.Drawing.Point(283, 229);
this.outModeCombox.Name = "outModeCombox";
this.outModeCombox.Size = new System.Drawing.Size(400, 23);
this.outModeCombox.TabIndex = 16;
@@ -915,6 +921,7 @@
//
this.interpOptsTab.AllowDrop = true;
this.interpOptsTab.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.interpOptsTab.Controls.Add(this.flowLayoutPanel1);
this.interpOptsTab.Controls.Add(this.aiInfoBtn);
this.interpOptsTab.Controls.Add(this.pictureBox5);
this.interpOptsTab.Controls.Add(this.outSpeedCombox);
@@ -954,6 +961,66 @@
this.interpOptsTab.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form1_DragDrop);
this.interpOptsTab.DragEnter += new System.Windows.Forms.DragEventHandler(this.Form1_DragEnter);
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.Controls.Add(this.comboxOutputFormat);
this.flowLayoutPanel1.Controls.Add(this.comboxOutputEncoder);
this.flowLayoutPanel1.Controls.Add(this.comboxOutputCrf);
this.flowLayoutPanel1.Controls.Add(this.comboxOutputColors);
this.flowLayoutPanel1.Location = new System.Drawing.Point(281, 157);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(614, 23);
this.flowLayoutPanel1.TabIndex = 46;
//
// comboxOutputFormat
//
this.comboxOutputFormat.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.comboxOutputFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboxOutputFormat.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.comboxOutputFormat.ForeColor = System.Drawing.Color.White;
this.comboxOutputFormat.FormattingEnabled = true;
this.comboxOutputFormat.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.comboxOutputFormat.Location = new System.Drawing.Point(0, 0);
this.comboxOutputFormat.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.comboxOutputFormat.Name = "comboxOutputFormat";
this.comboxOutputFormat.Size = new System.Drawing.Size(80, 23);
this.comboxOutputFormat.TabIndex = 47;
this.comboxOutputFormat.SelectedIndexChanged += new System.EventHandler(this.comboxOutputFormat_SelectedIndexChanged);
//
// comboxOutputCrf
//
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";
//
// comboxOutputColors
//
this.comboxOutputColors.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.comboxOutputColors.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
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.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.comboxOutputColors.Name = "comboxOutputColors";
this.comboxOutputColors.Size = new System.Drawing.Size(110, 23);
this.comboxOutputColors.TabIndex = 49;
//
// aiInfoBtn
//
this.aiInfoBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -1033,7 +1100,7 @@
this.encodingSettingsBtn.FlatAppearance.BorderSize = 0;
this.encodingSettingsBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.encodingSettingsBtn.ForeColor = System.Drawing.Color.White;
this.encodingSettingsBtn.Location = new System.Drawing.Point(689, 157);
this.encodingSettingsBtn.Location = new System.Drawing.Point(689, 192);
this.encodingSettingsBtn.Name = "encodingSettingsBtn";
this.encodingSettingsBtn.Size = new System.Drawing.Size(206, 23);
this.encodingSettingsBtn.TabIndex = 39;
@@ -1641,6 +1708,29 @@
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;
@@ -1694,6 +1784,7 @@
this.panel6.PerformLayout();
this.interpOptsTab.ResumeLayout(false);
this.interpOptsTab.PerformLayout();
this.flowLayoutPanel1.ResumeLayout(false);
this.completionActionPanel.ResumeLayout(false);
this.completionActionPanel.PerformLayout();
this.quickSettingsTab.ResumeLayout(false);
@@ -1824,6 +1915,11 @@
private System.Windows.Forms.ComboBox outSpeedCombox;
private System.Windows.Forms.PictureBox pictureBox5;
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 comboxOutputColors;
public System.Windows.Forms.ComboBox comboxOutputEncoder;
}
}

View File

@@ -55,7 +55,8 @@ namespace Flowframes
// Main Tab
UiUtils.InitCombox(interpFactorCombox, 0);
UiUtils.InitCombox(outSpeedCombox, 0);
UiUtils.InitCombox(outModeCombox, 0);
//UiUtils.InitCombox(outModeCombox, 0);
InitOutputUi();
UiUtils.InitCombox(aiModel, 2);
// Video Utils
UiUtils.InitCombox(trimCombox, 0);
@@ -84,6 +85,39 @@ namespace Flowframes
await Checks();
}
private void InitOutputUi()
{
comboxOutputFormat.FillFromEnum<Enums.Output.Format>(Strings.OutputFormat, 0);
UpdateOutputUi();
}
private void UpdateOutputUi()
{
var outMode = ParseUtils.GetEnum<Enums.Output.Format>(comboxOutputFormat.Text, true, Strings.OutputFormat);
comboxOutputEncoder.FillFromEnum(OutputUtils.GetAvailableEncoders(outMode), Strings.Encoder, 0);
comboxOutputEncoder.Visible = comboxOutputEncoder.Items.Count > 1;
UpdateOutputEncodingUi();
}
private void UpdateOutputEncodingUi()
{
var encoder = ParseUtils.GetEnum<Enums.Encoding.Encoder>(comboxOutputEncoder.Text, true, Strings.Encoder);
bool noEncoder = (int)encoder == -1;
comboxOutputCrf.Visible = !noEncoder;
comboxOutputColors.Visible = !noEncoder;
if (noEncoder)
return;
EncoderInfoVideo info = OutputUtils.GetEncoderInfoVideo(encoder);
comboxOutputCrf.Visible = !info.Lossless;
var pixelFormats = info.PixelFormats;
comboxOutputColors.Visible = pixelFormats.Count > 0;
comboxOutputColors.FillFromEnum(pixelFormats, Strings.PixelFormat, 0);
}
async Task Checks()
{
try
@@ -164,8 +198,7 @@ namespace Flowframes
public InterpSettings GetCurrentSettings()
{
SetTab("interpolate");
return new InterpSettings(inputTbox.Text.Trim(), outputTbox.Text.Trim(), GetAi(), currInFpsDetected, currInFps,
interpFactorCombox.GetFloat(), outSpeedCombox.GetInt().Clamp(1, 64), GetOutMode(), GetModel(GetAi()));
return new InterpSettings(inputTbox.Text.Trim(), outputTbox.Text.Trim(), GetAi(), currInFpsDetected, currInFps, interpFactorCombox.GetFloat(), outSpeedCombox.GetInt().Clamp(1, 64), GetExportSettings, GetModel(GetAi()));
}
public InterpSettings UpdateCurrentSettings(InterpSettings settings)
@@ -185,7 +218,7 @@ namespace Flowframes
settings.inFps = currInFps;
settings.interpFactor = interpFactorCombox.GetFloat();
settings.outFps = settings.inFps * settings.interpFactor;
settings.outMode = GetOutMode();
settings.outSettings = GetExportSettings;
settings.model = GetModel(GetAi());
return settings;
@@ -197,7 +230,7 @@ namespace Flowframes
MainUiFunctions.SetOutPath(outputTbox, entry.outPath);
interpFactorCombox.Text = entry.interpFactor.ToString();
aiCombox.SelectedIndex = Implementations.NetworksAvailable.IndexOf(Implementations.NetworksAvailable.Where(x => x.NameInternal == entry.ai.NameInternal).FirstOrDefault());
SetOutMode(entry.outMode);
SetFormat(entry.outSettings.Format);
}
public void SetStatus(string str)
@@ -287,7 +320,7 @@ namespace Flowframes
ConfigParser.LoadComboxIndex(outModeCombox);
}
private string GetAiComboboxName (AI ai)
private string GetAiComboboxName(AI ai)
{
return ai.FriendlyName + " - " + ai.Description;
}
@@ -334,7 +367,7 @@ namespace Flowframes
AiProcessSuspend.Reset();
if(Interpolate.currentSettings.outMode == Interpolate.OutMode.Realtime)
if (Interpolate.currentSettings.outSettings.Format == Enums.Output.Format.Realtime)
{
await Interpolate.Realtime();
SetProgress(0);
@@ -345,11 +378,6 @@ namespace Flowframes
}
}
private async void RealtimeInterp(object sender, EventArgs e)
{
}
public ModelCollection.ModelInfo GetModel(AI currentAi)
{
try
@@ -362,36 +390,14 @@ namespace Flowframes
}
}
Interpolate.OutMode GetOutMode()
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 }; } }
public void SetFormat(Enums.Output.Format format)
{
Interpolate.OutMode outMode = Interpolate.OutMode.VidMp4;
if (outModeCombox.Text.ToLowerInvariant().Contains("mkv")) outMode = Interpolate.OutMode.VidMkv;
if (outModeCombox.Text.ToLowerInvariant().Contains("webm")) outMode = Interpolate.OutMode.VidWebm;
if (outModeCombox.Text.ToLowerInvariant().Contains("prores")) outMode = Interpolate.OutMode.VidProRes;
if (outModeCombox.Text.ToLowerInvariant().Contains("avi")) outMode = Interpolate.OutMode.VidAvi;
if (outModeCombox.Text.ToLowerInvariant().Contains("gif")) outMode = Interpolate.OutMode.VidGif;
if (outModeCombox.Text.ToLowerInvariant().Contains("image")) outMode = Interpolate.OutMode.ImgPng;
if (outModeCombox.Text.ToLowerInvariant().Contains("real")) outMode = Interpolate.OutMode.Realtime;
return outMode;
}
public void SetOutMode(Interpolate.OutMode mode)
{
int targetIndex = 0;
for (int i = 0; i < outModeCombox.Items.Count; i++)
{
string currentItem = outModeCombox.Items[i].ToString().ToLowerInvariant();
if (mode == Interpolate.OutMode.VidMkv && currentItem.Contains("mkv")) targetIndex = i;
if (mode == Interpolate.OutMode.VidWebm && currentItem.Contains("webm")) targetIndex = i;
if (mode == Interpolate.OutMode.VidProRes && currentItem.Contains("prores")) targetIndex = i;
if (mode == Interpolate.OutMode.VidAvi && currentItem.Contains("avi")) targetIndex = i;
if (mode == Interpolate.OutMode.VidGif && currentItem.Contains("gif")) targetIndex = i;
if (mode == Interpolate.OutMode.ImgPng && currentItem.Contains("image")) targetIndex = i;
if (mode == Interpolate.OutMode.Realtime && currentItem.Contains("real")) targetIndex = i;
}
outModeCombox.SelectedIndex = targetIndex;
outModeCombox.Text = Strings.OutputFormat.Get(format.ToString());
}
public AI GetAi()
@@ -768,8 +774,18 @@ namespace Flowframes
{
var ai = GetAi();
if(ai != null)
if (ai != null)
UiUtils.ShowMessageBox(ai.GetVerboseInfo(), UiUtils.MessageType.Message);
}
private void comboxOutputFormat_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateOutputUi();
}
private void comboxOutputEncoder_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateOutputEncodingUi();
}
}
}

View File

@@ -32,9 +32,8 @@ namespace Flowframes.Forms
for (int i = 0; i < Program.batchQueue.Count; i++)
{
InterpSettings entry = Program.batchQueue.ElementAt(i);
string niceOutMode = entry.outMode.ToString().ToUpper().Remove("VID").Remove("IMG");
string str = $"#{i+1}: {Path.GetFileName(entry.inPath).Trunc(40)} - {entry.inFps.GetFloat()} FPS => " +
$"{entry.interpFactor}x {entry.ai.NameShort} ({entry.model.Name}) => {niceOutMode}";
string outFormat = Strings.OutputFormat.Get(entry.outSettings.Format.ToString());
string str = $"#{i+1}: {Path.GetFileName(entry.inPath).Trunc(40)} - {entry.inFps.GetFloat()} FPS => {entry.interpFactor}x {entry.ai.NameShort} ({entry.model.Name}) => {outFormat}";
taskList.Items.Add(str);
}
}

View File

@@ -253,18 +253,19 @@ namespace Flowframes.Forms
private void mp4Enc_SelectedIndexChanged(object sender, EventArgs e)
{
string text = mp4Enc.Text.ToUpper().Remove(" ");
if (text.Contains(FfmpegUtils.Codec.H264.ToString().ToUpper()))
mp4CrfConfigKey = "h264Crf";
if (text.Contains(FfmpegUtils.Codec.H265.ToString().ToUpper()))
mp4CrfConfigKey = "h265Crf";
if (text.Contains(FfmpegUtils.Codec.Av1.ToString().ToUpper()))
mp4CrfConfigKey = "av1Crf";
mp4Crf.Value = Config.GetInt(mp4CrfConfigKey);
// string text = mp4Enc.Text.ToUpper().Remove(" ");
//
// if (text.Contains(FfmpegUtils.Codec.H264.ToString().ToUpper()))
// mp4CrfConfigKey = "h264Crf";
//
// if (text.Contains(FfmpegUtils.Codec.H265.ToString().ToUpper()))
// mp4CrfConfigKey = "h265Crf";
//
// if (text.Contains(FfmpegUtils.Codec.Av1.ToString().ToUpper()))
// mp4CrfConfigKey = "av1Crf";
//
// mp4Crf.Value = Config.GetInt(mp4CrfConfigKey);
throw new NotImplementedException();
}
private void modelDownloaderBtn_Click(object sender, EventArgs e)

View File

@@ -565,9 +565,6 @@ namespace Flowframes.IO
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
float fps = fpsLimit ? maxFps.GetFloat() : curr.outFps.GetFloat();
if (curr.outMode == Interpolate.OutMode.VidGif && fps > 50f)
fps = 50f;
Size outRes = await InterpolateUtils.GetOutputResolution(curr.inPath, true);
string pattern = Config.Get(Config.Key.exportNamePattern);
string inName = Interpolate.currentSettings.inputIsFrames ? Path.GetFileName(curr.inPath) : Path.GetFileNameWithoutExtension(curr.inPath);
@@ -580,7 +577,7 @@ namespace Flowframes.IO
filename = filename.Replace("[FACTOR]", curr.interpFactor.ToStringDot());
filename = filename.Replace("[AI]", curr.ai.NameShort.ToUpper());
filename = filename.Replace("[MODEL]", curr.model.Name.Remove(" "));
filename = filename.Replace("[FPS]", fps.ToStringDot());
filename = filename.Replace("[FPS]", fps.ToStringDot("0.###"));
filename = filename.Replace("[ROUNDFPS]", fps.RoundToInt().ToString());
filename = filename.Replace("[RES]", $"{outRes.Width}x{outRes.Height}");
filename = filename.Replace("[H]", $"{outRes.Height}p");
@@ -589,7 +586,7 @@ namespace Flowframes.IO
filename += Paths.fpsLimitSuffix;
if (withExt)
filename += FfmpegUtils.GetExt(curr.outMode);
filename += FfmpegUtils.GetExt(curr.outSettings);
return filename;
}

View File

@@ -54,7 +54,7 @@ namespace Flowframes.Main
{
UpdateChunkAndBufferSizes();
bool imgSeq = Interpolate.currentSettings.outMode.ToString().ToLowerInvariant().StartsWith("img");
bool imgSeq = Interpolate.currentSettings.outSettings.Encoder.GetInfo().IsImageSequence;
interpFramesFolder = interpFramesPath;
videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir);
@@ -130,12 +130,12 @@ namespace Flowframes.Main
}
busy = true;
string outpath = Path.Combine(videoChunksFolder, "chunks", $"{chunkNo.ToString().PadLeft(4, '0')}{FfmpegUtils.GetExt(Interpolate.currentSettings.outMode)}");
string outpath = Path.Combine(videoChunksFolder, "chunks", $"{chunkNo.ToString().PadLeft(4, '0')}{FfmpegUtils.GetExt(Interpolate.currentSettings.outSettings)}");
string firstFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.First()].Trim());
string lastFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()].Trim());
Logger.Log($"[AE] Encoding Chunk #{chunkNo} to using line {frameLinesToEncode.First()} ({firstFile}) through {frameLinesToEncode.Last()} ({lastFile}) - {unencodedFrameLines.Count} unencoded frames left in total", true, false, "ffmpeg");
await Export.EncodeChunk(outpath, Interpolate.currentSettings.interpFolder, chunkNo, Interpolate.currentSettings.outMode, frameLinesToEncode.First(), frameLinesToEncode.Count);
await Export.EncodeChunk(outpath, Interpolate.currentSettings.interpFolder, chunkNo, Interpolate.currentSettings.outSettings, frameLinesToEncode.First(), frameLinesToEncode.Count);
if (Interpolate.canceled) return;

View File

@@ -22,7 +22,7 @@ namespace Flowframes.Main
{
public static async Task ExportFrames(string path, string outFolder, I.OutMode mode, bool stepByStep)
public static async Task ExportFrames(string path, string outFolder, ExportSettings exportSettings, bool stepByStep)
{
if(Config.GetInt(Config.Key.sceneChangeFillMode) == 1)
{
@@ -30,7 +30,7 @@ namespace Flowframes.Main
await Blend.BlendSceneChanges(frameFile);
}
if (!mode.ToString().ToLowerInvariant().Contains("vid")) // Copy interp frames out of temp folder and skip video export for image seq export
if (exportSettings.Encoder.GetInfo().IsImageSequence) // Copy interp frames out of temp folder and skip video export for image seq export
{
try
{
@@ -61,10 +61,10 @@ namespace Flowframes.Main
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
if (!dontEncodeFullFpsVid)
await Encode(mode, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(false, true)), I.currentSettings.outFps, new Fraction());
await Encode(exportSettings, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(false, true)), I.currentSettings.outFps, new Fraction());
if (fpsLimit)
await Encode(mode, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(true, true)), I.currentSettings.outFps, maxFps);
await Encode(exportSettings, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(true, true)), I.currentSettings.outFps, maxFps);
}
catch (Exception e)
{
@@ -76,7 +76,7 @@ namespace Flowframes.Main
public static async Task<string> GetPipedFfmpegCmd(bool ffplay = false)
{
InterpSettings s = I.currentSettings;
string encArgs = FfmpegUtils.GetEncArgs(FfmpegUtils.GetCodec(s.outMode), (s.ScaledResolution.IsEmpty ? s.InputResolution : s.ScaledResolution), s.outFps.GetFloat(), true).FirstOrDefault();
string encArgs = FfmpegUtils.GetEncArgs(s.outSettings.Encoder, s.outSettings.PixelFormat, (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());
@@ -84,7 +84,7 @@ namespace Flowframes.Main
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(s.inPath);
string extraArgsIn = FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale);
string extraArgsOut = FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? maxFps : new Fraction(), extraData, s.outMode);
string extraArgsOut = FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? maxFps : new Fraction(), extraData, s.outSettings);
if (ffplay)
{
@@ -196,7 +196,7 @@ namespace Flowframes.Main
}
}
static async Task Encode(I.OutMode mode, string framesPath, string outPath, Fraction fps, Fraction resampleFps)
static async Task Encode(ExportSettings settings, string framesPath, string outPath, Fraction fps, Fraction resampleFps)
{
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
@@ -207,14 +207,14 @@ namespace Flowframes.Main
return;
}
if (mode == I.OutMode.VidGif)
if (settings.Format == Enums.Output.Format.Gif)
{
await FfmpegEncode.FramesToGifConcat(framesFile, outPath, fps, true, Config.GetInt(Config.Key.gifColors), resampleFps, I.currentSettings.outItsScale);
}
else
{
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
await FfmpegEncode.FramesToVideo(framesFile, outPath, mode, fps, resampleFps, I.currentSettings.outItsScale, extraData);
await FfmpegEncode.FramesToVideo(framesFile, outPath, settings, fps, resampleFps, I.currentSettings.outItsScale, extraData);
await MuxOutputVideo(I.currentSettings.inPath, outPath);
await Loop(outPath, await GetLoopTimes());
}
@@ -228,7 +228,7 @@ namespace Flowframes.Main
public static async Task ChunksToVideo(string tempFolder, string chunksFolder, string baseOutPath, bool isBackup = false)
{
if (IoUtils.GetAmountOfFiles(chunksFolder, true, "*" + FfmpegUtils.GetExt(I.currentSettings.outMode)) < 1)
if (IoUtils.GetAmountOfFiles(chunksFolder, true, "*" + FfmpegUtils.GetExt(I.currentSettings.outSettings)) < 1)
{
I.Cancel("No video chunks found - An error must have occured during chunk encoding!", AiProcess.hasShownError);
return;
@@ -289,7 +289,7 @@ namespace Flowframes.Main
await Loop(outPath, await GetLoopTimes());
}
public static async Task EncodeChunk(string outPath, string interpDir, int chunkNo, I.OutMode mode, int firstFrameNum, int framesAmount)
public static async Task EncodeChunk(string outPath, string interpDir, int chunkNo, ExportSettings 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));
@@ -307,7 +307,7 @@ namespace Flowframes.Main
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
if (mode.ToString().ToLowerInvariant().StartsWith("img")) // Image Sequence output mode, not video
if (settings.Encoder.GetInfo().IsImageSequence) // Image Sequence output mode, not video
{
string desiredFormat = Config.Get(Config.Key.imgSeqFormat);
string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(interpDir)[0]).Remove(".").ToUpper();
@@ -336,14 +336,14 @@ namespace Flowframes.Main
else
{
if (!dontEncodeFullFpsVid)
await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.currentSettings.outFps, new Fraction(), I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, new Fraction(), I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode
if (fpsLimit)
{
string filename = Path.GetFileName(outPath);
string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix;
outPath = Path.Combine(newParentDir, filename);
await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.currentSettings.outFps, maxFps, I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, maxFps, I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
}
}

View File

@@ -22,8 +22,6 @@ namespace Flowframes
{
public class Interpolate
{
public enum OutMode { VidMp4, VidMkv, VidWebm, VidProRes, VidAvi, VidGif, ImgPng, Realtime }
public static bool currentlyUsingAutoEnc;
public static InterpSettings currentSettings;
public static MediaFile currentMediaFile;
@@ -40,7 +38,7 @@ namespace Flowframes
if (!Utils.CheckPathValid(currentSettings.inPath)) return; // Check if input path/file is valid
if (!Utils.CheckAiAvailable(currentSettings.ai, currentSettings.model)) return; // Check if selected AI pkg is installed
if (!AutoEncodeResume.resumeNextRun && !Utils.CheckDeleteOldTempFolder()) return; // Try to delete temp folder if an old one exists
if (!(await Utils.CheckEncoderValid(currentSettings.outFps.GetFloat()))) return; // Check encoder compat
if (!(await Utils.CheckEncoderValid())) return; // Check encoder compat
Utils.ShowWarnings(currentSettings.interpFactor, currentSettings.ai);
currentSettings.stepByStep = false;
Program.mainForm.SetStatus("Starting...");
@@ -65,7 +63,7 @@ namespace Flowframes
if (currentSettings.ai.Piped)
await Export.MuxPipedVideo(currentSettings.inPath, currentSettings.FullOutPath);
else
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outMode, false);
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outSettings, false);
}
if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder) && IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false) > 0)

View File

@@ -93,7 +93,7 @@ namespace Flowframes.Main
return;
}
if (Config.GetBool(Config.Key.sbsAllowAutoEnc) && !(await InterpolateUtils.CheckEncoderValid(currentSettings.outFps.GetFloat()))) return;
if (Config.GetBool(Config.Key.sbsAllowAutoEnc) && !(await InterpolateUtils.CheckEncoderValid())) return;
if (canceled) return;
Program.mainForm.SetStatus("Running AI...");
@@ -119,7 +119,7 @@ namespace Flowframes.Main
}
}
if (!(await InterpolateUtils.CheckEncoderValid(currentSettings.outFps.GetFloat()))) return;
if (!(await InterpolateUtils.CheckEncoderValid())) return;
string[] outFrames = IoUtils.GetFilesSorted(currentSettings.interpFolder, currentSettings.interpExt);
@@ -130,7 +130,7 @@ namespace Flowframes.Main
return;
}
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outMode, true);
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outSettings, true);
}
public static async Task Reset()

View File

@@ -121,18 +121,16 @@ namespace Flowframes.Main
passes = false;
}
if (passes && s.outFps.GetFloat() < 1f || s.outFps.GetFloat() > 1000f)
{
string imgSeqNote = isFile ? "" : "\n\nWhen using an image sequence as input, you always have to specify the frame rate manually.";
UiUtils.ShowMessageBox($"Invalid output frame rate ({s.outFps.GetFloat()}).\nMust be 1-1000.{imgSeqNote}");
passes = false;
}
string fpsLimitValue = Config.Get(Config.Key.maxFps);
float fpsLimit = (fpsLimitValue.Contains("/") ? new Fraction(Config.Get(Config.Key.maxFps)).GetFloat() : fpsLimitValue.GetFloat());
int maxFps = s.outSettings.Encoder.GetInfo().MaxFramerate;
if (s.outMode == I.OutMode.VidGif && s.outFps.GetFloat() > 50 && !(fpsLimit > 0 && fpsLimit <= 50))
Logger.Log($"Warning: GIF will be encoded at 50 FPS instead of {s.outFps.GetFloat()} as the format doesn't support frame rates that high.");
if (passes && s.outFps.GetFloat() < 1f || (s.outFps.GetFloat() > maxFps && !(fpsLimit > 0 && fpsLimit <= maxFps)))
{
string imgSeqNote = isFile ? "" : "\n\nWhen using an image sequence as input, you always have to specify the frame rate manually.";
UiUtils.ShowMessageBox($"Invalid output frame rate ({s.outFps.GetFloat()}).\nMust be 1-{maxFps}. Either lower the interpolation factor or use the \"Maximum Output Frame Rate\" option.{imgSeqNote}");
passes = false;
}
if (!passes)
I.Cancel("Invalid settings detected.", true);
@@ -222,20 +220,9 @@ namespace Flowframes.Main
return true;
}
public static async Task<bool> CheckEncoderValid(float interpFps)
public static async Task<bool> CheckEncoderValid()
{
string enc = FfmpegUtils.GetEnc(FfmpegUtils.GetCodec(I.currentSettings.outMode));
float maxAv1Fps = 240; // SVT-AV1 only supports up to 240 FPS as of 2022-08
float maxFps = Config.GetFloat(Config.Key.maxFps);
float encodeFps = maxFps > 0 ? interpFps.Clamp(0, maxFps) : interpFps;
if (enc.ToLowerInvariant().Contains("av1") && encodeFps > maxAv1Fps)
{
UiUtils.ShowMessageBox($"The selected encoder only supports up to {maxAv1Fps} FPS!\nPlease use a different encoder or reduce the interpolation factor.", UiUtils.MessageType.Error);
I.Cancel();
return false;
}
string enc = I.currentSettings.outSettings.Encoder.ToString();
if (enc.ToLowerInvariant().Contains("nvenc") && !(await FfmpegCommands.IsEncoderCompatible(enc)))
{
@@ -302,7 +289,7 @@ namespace Flowframes.Main
return false;
}
if (current.outMode == I.OutMode.VidGif)
if (current.outSettings.Format == Enums.Output.Format.Gif)
{
Logger.Log($"Not Using AutoEnc: Using GIF output", true);
return false;

View File

@@ -29,9 +29,9 @@ namespace Flowframes.Media
string subArgs = "-c:s " + Utils.GetSubCodecForContainer(containerExt);
bool audioCompat = Utils.ContainerSupportsAllAudioFormats(I.currentSettings.outMode, GetAudioCodecs(inputVideo));
bool audioCompat = Utils.ContainerSupportsAllAudioFormats(I.currentSettings.outSettings.Format, GetAudioCodecs(inputVideo));
bool slowmo = I.currentSettings.outItsScale != 0 && I.currentSettings.outItsScale != 1;
string audioArgs = audioCompat && !slowmo ? "" : await Utils.GetAudioFallbackArgs(inputVideo, I.currentSettings.outMode, I.currentSettings.outItsScale);
string audioArgs = audioCompat && !slowmo ? "" : await Utils.GetAudioFallbackArgs(inputVideo, I.currentSettings.outSettings.Format, I.currentSettings.outItsScale);
if (!audioCompat && !slowmo)
Logger.Log("Warning: Input audio format(s) not fully supported in output container - Will re-encode.", true, false, "ffmpeg");
@@ -46,7 +46,7 @@ namespace Flowframes.Media
if (!subs || (subs && !Utils.ContainerSupportsSubs(containerExt)))
subArgs = "-sn";
bool isMkv = I.currentSettings.outMode == I.OutMode.VidMkv;
bool isMkv = I.currentSettings.outSettings.Format == Data.Enums.Output.Format.Mkv;
string mkvFix = isMkv ? "-max_interleave_delta 0" : ""; // https://reddit.com/r/ffmpeg/comments/efddfs/starting_new_cluster_due_to_timestamp/
string metaArg = (isMkv && meta) ? "-map 1:t?" : ""; // https://reddit.com/r/ffmpeg/comments/fw4jnh/how_to_make_ffmpeg_keep_attached_images_in_mkv_as/
string shortestArg = shortest ? "-shortest" : "";

View File

@@ -24,12 +24,19 @@ namespace Flowframes
public static int GetModulo ()
{
return (Interpolate.currentSettings.ai.NameInternal == Implementations.flavrCuda.NameInternal) ? 8 : 2; // FLAVR input needs to be divisible by 8
if (Interpolate.currentSettings.ai.NameInternal == Implementations.flavrCuda.NameInternal)
return 8;
return Interpolate.currentSettings.outSettings.Encoder.GetInfo().Modulo;
}
public static string GetPadFilter ()
{
int mod = GetModulo();
if (mod < 2)
return "";
return $"pad=width=ceil(iw/{mod})*{mod}:height=ceil(ih/{mod})*{mod}:color=black@0";
}

View File

@@ -13,14 +13,14 @@ namespace Flowframes.Media
{
partial class FfmpegEncode : FfmpegCommands
{
public static async Task FramesToVideo(string framesFile, string outPath, Interpolate.OutMode outMode, Fraction fps, Fraction resampleFps, float itsScale, VidExtraData extraData, LogMode logMode = LogMode.OnlyLastLine, bool isChunk = false)
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)
{
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(Utils.GetCodec(outMode), (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat());
string[] encArgs = Utils.GetEncArgs(settings.Encoder, settings.PixelFormat, (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);
@@ -33,11 +33,11 @@ namespace Flowframes.Media
string args = "";
for(int i = 0; i < encArgs.Length; i++)
for (int i = 0; i < encArgs.Length; i++)
{
string pre = i == 0 ? "" : $" && ffmpeg {AvProcess.GetFfmpegDefaultArgs()}";
string post = (i == 0 && encArgs.Length > 1) ? $"-f null -" : outPath.Wrap();
args += $"{pre} {GetFfmpegExportArgsIn(fps, itsScale)} {inArg} {encArgs[i]} {GetFfmpegExportArgsOut(resampleFps, extraData, Interpolate.currentSettings.outMode, isChunk)} {post} ";
args += $"{pre} {GetFfmpegExportArgsIn(fps, itsScale)} {inArg} {encArgs[i]} {GetFfmpegExportArgsOut(resampleFps, extraData, settings, isChunk)} {post} ";
}
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, !isChunk);
@@ -50,7 +50,7 @@ namespace Flowframes.Media
return $"-r {fps}";
}
public static string GetFfmpegExportArgsOut (Fraction resampleFps, VidExtraData extraData, Interpolate.OutMode outMode, bool isChunk = false)
public static string GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, ExportSettings settings, bool isChunk = false)
{
List<string> filters = new List<string>();
string extraArgs = Config.Get(Config.Key.ffEncArgs);
@@ -68,14 +68,14 @@ namespace Flowframes.Media
if (!string.IsNullOrWhiteSpace(extraData.displayRatio) && !extraData.displayRatio.MatchesWildcard("*N/A*"))
extraArgs += $" -aspect {extraData.displayRatio}";
if(!isChunk && outMode == Interpolate.OutMode.VidMp4)
if (!isChunk && settings.Format == Enums.Output.Format.Mp4)
extraArgs += $" -movflags +faststart";
filters.Add(GetPadFilter());
return filters.Count > 0 ? $"-vf {string.Join(",", filters)}" : "" + $" {extraArgs}";
}
public static string GetConcatFileExt (string concatFilePath)
public static string GetConcatFileExt(string concatFilePath)
{
return Path.GetExtension(File.ReadAllLines(concatFilePath).FirstOrDefault().Split('\'')[1]);
}
@@ -110,7 +110,7 @@ namespace Flowframes.Media
if (logMode != LogMode.Hidden)
Logger.Log((resampleFps.GetFloat() <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.GetFloat().ToString().Replace(",", ".")} FPS...");
string framesFilename = Path.GetFileName(framesFile);
string dither = Config.Get(Config.Key.gifDitherType).Split(' ').First();
string paletteFilter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither={dither}\"" : "";

View File

@@ -8,6 +8,7 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static Flowframes.Data.Enums.Encoding;
using static Flowframes.Media.GetVideoInfo;
using Stream = Flowframes.Data.Streams.Stream;
@@ -170,127 +171,88 @@ namespace Flowframes.Media
return false;
}
public enum Codec { H264, H265, H264Nvenc, H265Nvenc, Av1, Vp9, ProRes, AviRaw, Gif }
public static Codec GetCodec(Interpolate.OutMode mode)
{
if (mode == Interpolate.OutMode.VidMp4 || mode == Interpolate.OutMode.VidMkv)
{
int mp4MkvEnc = Config.GetInt(Config.Key.mp4Enc);
if (mp4MkvEnc == 0) return Codec.H264;
if (mp4MkvEnc == 1) return Codec.H265;
if (mp4MkvEnc == 2) return Codec.H264Nvenc;
if (mp4MkvEnc == 3) return Codec.H265Nvenc;
if (mp4MkvEnc == 4) return Codec.Av1;
}
if (mode == Interpolate.OutMode.VidWebm)
return Codec.Vp9;
if (mode == Interpolate.OutMode.VidProRes)
return Codec.ProRes;
if (mode == Interpolate.OutMode.VidAvi)
return Codec.AviRaw;
if (mode == Interpolate.OutMode.VidGif)
return Codec.Gif;
return Codec.H264;
}
public static string GetEnc(Codec codec)
{
switch (codec)
{
case Codec.H264: return "libx264";
case Codec.H265: return "libx265";
case Codec.H264Nvenc: return "h264_nvenc";
case Codec.H265Nvenc: return "hevc_nvenc";
case Codec.Av1: return "libsvtav1";
case Codec.Vp9: return "libvpx-vp9";
case Codec.ProRes: return "prores_ks";
case Codec.AviRaw: return Config.Get(Config.Key.aviCodec);
case Codec.Gif: return "gif";
}
return "libx264";
}
public static string[] GetEncArgs(Codec codec, 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(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.
{
int keyint = 10;
if (codec == Codec.H264)
var args = new List<string>();
EncoderInfoVideo info = OutputUtils.GetEncoderInfoVideo(enc);
args.Add($"-c:v {info.Name}");
if (enc == Encoder.X264 || enc == Encoder.X265 || enc == Encoder.SvtAv1 || enc == Encoder.VpxVp9 || enc == Encoder.Nvenc264 || enc == Encoder.Nvenc265 || enc == Encoder.NvencAv1)
args.Add(GetKeyIntArg(fps, keyint));
if (pixFmt != (PixelFormat)(-1))
args.Add($"-pix_fmt {pixFmt.ToString().Lower()}");
if (enc == Encoder.X264)
{
string preset = Config.Get(Config.Key.ffEncPreset).ToLowerInvariant().Remove(" ");
string g = GetKeyIntArg(fps, keyint);
return new string[] { $"-c:v {GetEnc(codec)} -crf {Config.GetInt(Config.Key.h264Crf)} -preset {preset} {g} -pix_fmt {GetPixFmt()}" };
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}");
}
if (codec == Codec.H265)
if (enc == Encoder.X265)
{
string preset = Config.Get(Config.Key.ffEncPreset).ToLowerInvariant().Remove(" ");
string preset = Config.Get(Config.Key.ffEncPreset).ToLowerInvariant().Remove(" "); // TODO: Replace this ugly stuff with enums
int crf = Config.GetInt(Config.Key.h265Crf);
string g = GetKeyIntArg(fps, keyint);
return new string[] { $"-c:v {GetEnc(codec)} {(crf > 0 ? $"-crf {crf}" : "-x265-params lossless=1")} -preset {preset} {g} -pix_fmt {GetPixFmt()}" };
args.Add($"{(crf > 0 ? $"-crf {crf}" : "-x265-params lossless=1")} -preset {preset}");
}
if (codec == Codec.H264Nvenc)
{
int cq = (Config.GetInt(Config.Key.h264Crf) * 1.1f).RoundToInt();
return new string[] { $"-c:v {GetEnc(codec)} -b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")} -pix_fmt {GetPixFmt()}" };
}
if (codec == Codec.H265Nvenc)
{
int cq = (Config.GetInt(Config.Key.h265Crf) * 1.1f).RoundToInt();
return new string[] { $"-c:v {GetEnc(codec)} -b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")} -pix_fmt {GetPixFmt()}" };
}
if (codec == Codec.Av1)
if (enc == Encoder.SvtAv1)
{
int cq = Config.GetInt(Config.Key.av1Crf);
string g = GetKeyIntArg(fps, keyint);
return new string[] { $"-c:v {GetEnc(codec)} -b:v 0 -qp {cq} {GetSvtAv1Speed()} {g} -svtav1-params enable-qm=1:enable-overlays=1:enable-tf=0:scd=0 -pix_fmt {GetPixFmt()}" };
args.Add($"-b:v 0 -qp {cq} {GetSvtAv1Speed()} -svtav1-params enable-qm=1:enable-overlays=1:enable-tf=0:scd=0");
}
if (codec == Codec.Vp9)
if (enc == Encoder.VpxVp9)
{
int crf = Config.GetInt(Config.Key.vp9Crf);
string qualityStr = (crf > 0) ? $"-crf {crf}" : "-lossless 1";
string g = GetKeyIntArg(fps, keyint);
string t = GetTilingArgs(res, "-tile-columns ", "-tile-rows ");
if (realtime) // Force 1-pass
{
return new string[] { $"-c:v {GetEnc(codec)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 {g} -pix_fmt {GetPixFmt()}" };
args.Add($"-b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1");
}
else
{
return new string[] {
$"-c:v {GetEnc(codec)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 {g} -pass 1 -pix_fmt {GetPixFmt()} -an",
$"-c:v {GetEnc(codec)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 {g} -pass 2 -pix_fmt {GetPixFmt()}"
return new string[] {
$"{string.Join(" ", args)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 -pass 1 -an",
$"{string.Join(" ", args)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 -pass 2"
};
}
}
if (codec == Codec.ProRes)
if (enc == Encoder.Nvenc264)
{
return new string[] { $"-c:v {GetEnc(codec)} -profile:v {Config.GetInt(Config.Key.proResProfile)} -pix_fmt {GetPixFmt()}" };
int cq = (Config.GetInt(Config.Key.h264Crf) * 1.1f).RoundToInt();
args.Add($"-b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")}");
}
if (codec == Codec.AviRaw)
if (enc == Encoder.Nvenc265)
{
return new string[] { $"-c:v {GetEnc(codec)} -pix_fmt {Config.Get(Config.Key.aviColors)}" };
int cq = (Config.GetInt(Config.Key.h265Crf) * 1.1f).RoundToInt();
args.Add($"-b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")}");
}
if (codec == Codec.Gif)
if (enc == Encoder.NvencAv1)
{
return new string[] { $"-c:v {GetEnc(codec)} -gifflags -offsetting" };
int cq = (Config.GetInt(Config.Key.av1Crf) * 1.1f).RoundToInt();
args.Add($"-b:v 0 {(cq > 0 ? $"-cq {cq} -preset p7" : "-preset lossless")}");
}
return new string[0];
if (enc == Encoder.ProResKs)
{
args.Add($"-profile:v {Config.GetInt(Config.Key.proResProfile)}");
}
if (enc == Encoder.Gif)
{
args.Add("-gifflags -offsetting");
}
return new string[] { string.Join(" ", args) };
}
public static string GetTilingArgs(Size resolution, string colArg, string rowArg)
@@ -307,12 +269,12 @@ namespace Flowframes.Media
Logger.Log($"GetTilingArgs: Video resolution is {resolution.Width}x{resolution.Height} - Using 2^{cols} columns, 2^{rows} rows (=> {Math.Pow(2, cols)}x{Math.Pow(2, rows)} = {Math.Pow(2, cols) * Math.Pow(2, rows)} Tiles)", true);
return $"{(cols > 0 ? colArg+cols : "")} {(rows > 0 ? rowArg + rows : "")}";
return $"{(cols > 0 ? colArg + cols : "")} {(rows > 0 ? rowArg + rows : "")}";
}
public static string GetKeyIntArg(float fps, int intervalSeconds, string arg = "-g ")
{
int keyInt = (fps * intervalSeconds).RoundToInt().Clamp(20, 300);
int keyInt = (fps * intervalSeconds).RoundToInt().Clamp(30, 600);
return $"{arg}{keyInt}";
}
@@ -348,34 +310,21 @@ namespace Flowframes.Media
return $"-preset {arg}";
}
static string GetPixFmt()
{
switch (Config.GetInt(Config.Key.pixFmt))
{
case 0: return "yuv420p";
case 1: return "yuv444p";
case 2: return "yuv420p10le";
case 3: return "yuv444p10le";
}
return "yuv420p";
}
public static bool ContainerSupportsAllAudioFormats(Interpolate.OutMode outMode, List<string> codecs)
public static bool ContainerSupportsAllAudioFormats(Enums.Output.Format outFormat, List<string> codecs)
{
if (codecs.Count < 1)
Logger.Log($"Warning: ContainerSupportsAllAudioFormats() was called, but codec list has {codecs.Count} entries.", true, false, "ffmpeg");
foreach (string format in codecs)
{
if (!ContainerSupportsAudioFormat(outMode, format))
if (!ContainerSupportsAudioFormat(outFormat, format))
return false;
}
return true;
}
public static bool ContainerSupportsAudioFormat(Interpolate.OutMode outMode, string format)
public static bool ContainerSupportsAudioFormat(Enums.Output.Format outFormat, string format)
{
bool supported = false;
string alias = GetAudioExt(format);
@@ -383,35 +332,31 @@ namespace Flowframes.Media
string[] formatsMp4 = new string[] { "m4a", "mp3", "ac3", "dts" };
string[] formatsMkv = new string[] { "m4a", "mp3", "ac3", "dts", "ogg", "mp2", "wav", "wma" };
string[] formatsWebm = new string[] { "ogg" };
string[] formatsProres = new string[] { "m4a", "ac3", "dts", "wav" };
string[] formatsMov = new string[] { "m4a", "ac3", "dts", "wav" };
string[] formatsAvi = new string[] { "m4a", "ac3", "dts" };
switch (outMode)
switch (outFormat)
{
case Interpolate.OutMode.VidMp4: supported = formatsMp4.Contains(alias); break;
case Interpolate.OutMode.VidMkv: supported = formatsMkv.Contains(alias); break;
case Interpolate.OutMode.VidWebm: supported = formatsWebm.Contains(alias); break;
case Interpolate.OutMode.VidProRes: supported = formatsProres.Contains(alias); break;
case Interpolate.OutMode.VidAvi: supported = formatsAvi.Contains(alias); break;
case Enums.Output.Format.Mp4: supported = formatsMp4.Contains(alias); break;
case Enums.Output.Format.Mkv: supported = formatsMkv.Contains(alias); break;
case Enums.Output.Format.Webm: supported = formatsWebm.Contains(alias); break;
case Enums.Output.Format.Mov: supported = formatsMov.Contains(alias); break;
case Enums.Output.Format.Avi: supported = formatsAvi.Contains(alias); break;
}
Logger.Log($"Checking if {outMode} supports audio format '{format}' ({alias}): {supported}", true, false, "ffmpeg");
Logger.Log($"Checking if {outFormat} supports audio format '{format}' ({alias}): {supported}", true, false, "ffmpeg");
return supported;
}
public static string GetExt(Interpolate.OutMode outMode, bool dot = true)
public static string GetExt(ExportSettings settings, bool dot = true)
{
string ext = dot ? "." : "";
EncoderInfoVideo info = settings.Encoder.GetInfo();
switch (outMode)
{
case Interpolate.OutMode.VidMp4: ext += "mp4"; break;
case Interpolate.OutMode.VidMkv: ext += "mkv"; break;
case Interpolate.OutMode.VidWebm: ext += "webm"; break;
case Interpolate.OutMode.VidProRes: ext += "mov"; break;
case Interpolate.OutMode.VidAvi: ext += "avi"; break;
case Interpolate.OutMode.VidGif: ext += "gif"; break;
}
if (string.IsNullOrWhiteSpace(info.OverideExtension))
ext += settings.Format.ToString().Lower();
else
ext += info.OverideExtension;
return ext;
}
@@ -440,7 +385,7 @@ namespace Flowframes.Media
return "unsupported";
}
public static async Task<string> GetAudioFallbackArgs(string videoPath, Interpolate.OutMode outMode, float itsScale)
public static async Task<string> GetAudioFallbackArgs(string videoPath, Enums.Output.Format outFormat, float itsScale)
{
bool opusMp4 = Config.GetBool(Config.Key.allowOpusInMp4);
int opusBr = Config.GetInt(Config.Key.opusBitrate, 128);
@@ -448,7 +393,7 @@ namespace Flowframes.Media
int ac = (await GetVideoInfo.GetFfprobeInfoAsync(videoPath, GetVideoInfo.FfprobeMode.ShowStreams, "channels", 0)).GetInt();
string af = GetAudioFilters(itsScale);
if (outMode == Interpolate.OutMode.VidMkv || outMode == Interpolate.OutMode.VidWebm || (outMode == Interpolate.OutMode.VidMp4 && opusMp4))
if (outFormat == Enums.Output.Format.Mkv || outFormat == Enums.Output.Format.Webm || (outFormat == Enums.Output.Format.Mp4 && opusMp4))
return $"-c:a libopus -b:a {(ac > 4 ? $"{opusBr * 2}" : $"{opusBr}")}k -ac {(ac > 0 ? $"{ac}" : "2")} {af}"; // Double bitrate if 5ch or more, ignore ac if <= 0
else
return $"-c:a aac -b:a {(ac > 4 ? $"{aacBr * 2}" : $"{aacBr}")}k -aac_coder twoloop -ac {(ac > 0 ? $"{ac}" : "2")} {af}";

View File

@@ -0,0 +1,223 @@
using Flowframes.Data;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using static Flowframes.Data.Enums.Encoding;
using Encoder = Flowframes.Data.Enums.Encoding.Encoder;
using PixFmt = Flowframes.Data.Enums.Encoding.PixelFormat;
namespace Flowframes.MiscUtils
{
internal class OutputUtils
{
public static EncoderInfoVideo GetEncoderInfoVideo(Encoder encoder)
{
if (encoder == Encoder.X264)
{
return new EncoderInfoVideo
{
Codec = Codec.H264,
Name = "libx264",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P },
};
}
if (encoder == Encoder.X265)
{
return new EncoderInfoVideo
{
Codec = Codec.H265,
Name = "libx265",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P10Le },
};
}
if (encoder == Encoder.Nvenc264)
{
return new EncoderInfoVideo
{
Codec = Codec.H264,
Name = "h264_nvenc",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P },
HwAccelerated = true,
};
}
if (encoder == Encoder.SvtAv1)
{
return new EncoderInfoVideo
{
Codec = Codec.AV1,
Name = "libsvtav1",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv420P10Le },
PixelFormatDefault = PixFmt.Yuv420P10Le,
MaxFramerate = 240,
};
}
if (encoder == Encoder.VpxVp9)
{
return new EncoderInfoVideo
{
Codec = Codec.VP9,
Name = "libvpx-vp9",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P, PixFmt.Yuv444P10Le },
};
}
if (encoder == Encoder.Nvenc265)
{
return new EncoderInfoVideo
{
Codec = Codec.H265,
Name = "hevc_nvenc",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le },
HwAccelerated = true,
};
}
if (encoder == Encoder.NvencAv1)
{
return new EncoderInfoVideo
{
Codec = Codec.AV1,
Name = "av1_nvenc",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le },
PixelFormatDefault = PixFmt.Yuv420P10Le,
HwAccelerated = true,
};
}
if (encoder == Encoder.ProResKs)
{
return new EncoderInfoVideo
{
Codec = Codec.ProRes,
Name = "prores_ks",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv422P10Le, PixFmt.Yuv444P10Le, PixFmt.Yuva444P10Le },
};
}
if (encoder == Encoder.Gif)
{
return new EncoderInfoVideo
{
Codec = Codec.Gif,
Name = "gif",
PixelFormats = new List<PixFmt>() { PixFmt.Rgb8 },
OverideExtension = "gif",
MaxFramerate = 50,
};
}
if (encoder == Encoder.Ffv1)
{
return new EncoderInfoVideo
{
Codec = Codec.Ffv1,
Name = "ffv1",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv422P, PixFmt.Yuv422P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P10Le },
Lossless = true,
};
}
if (encoder == Encoder.Huffyuv)
{
return new EncoderInfoVideo
{
Codec = Codec.Huffyuv,
Name = "huffyuv",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv422P, PixFmt.Rgb24 },
Lossless = true,
};
}
if (encoder == Encoder.Magicyuv)
{
return new EncoderInfoVideo
{
Codec = Codec.Magicyuv,
Name = "magicyuv",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P },
Lossless = true,
};
}
if (encoder == Encoder.Rawvideo)
{
return new EncoderInfoVideo
{
Codec = Codec.Rawvideo,
Name = "rawvideo",
Lossless = true,
};
}
if (encoder == Encoder.Png)
{
return new EncoderInfoVideo
{
Codec = Codec.Png,
Name = "png",
PixelFormats = new List<PixFmt>() { PixFmt.Rgb24, PixFmt.Rgba },
Lossless = true,
IsImageSequence = true,
OverideExtension = "png",
};
}
if (encoder == Encoder.Jpeg)
{
return new EncoderInfoVideo
{
Codec = Codec.Jpeg,
Name = "mjpeg",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P },
IsImageSequence = true,
OverideExtension = "jpg",
};
}
if (encoder == Encoder.Webp)
{
return new EncoderInfoVideo
{
Codec = Codec.Webp,
Name = "libwebp",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuva420P },
IsImageSequence = true,
OverideExtension = "webp",
};
}
return new EncoderInfoVideo();
}
public static List<Codec> GetSupportedCodecs(Enums.Output.Format 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 };
case Enums.Output.Format.Webm: return new List<Codec> { Codec.VP9, Codec.AV1 };
case Enums.Output.Format.Mov: return new List<Codec> { Codec.ProRes };
case Enums.Output.Format.Avi: return new List<Codec> { Codec.Ffv1, Codec.Huffyuv, Codec.Magicyuv, Codec.Rawvideo };
case Enums.Output.Format.Gif: return new List<Codec> { Codec.Gif };
case Enums.Output.Format.Images: return new List<Codec> { Codec.Png, Codec.Jpeg, Codec.Webp };
case Enums.Output.Format.Realtime: return new List<Codec> { };
default: return new List<Codec> { };
}
}
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();
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.MiscUtils
{
internal class ParseUtils
{
public static TEnum GetEnum<TEnum>(string str, bool ignoreCase = true, Dictionary<string, string> stringMap = null) where TEnum : Enum
{
if (stringMap == null)
stringMap = new Dictionary<string, string>();
str = stringMap.Get(str, true, true);
var values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>();
foreach (var entry in values)
{
string entryString = stringMap.Get(entry.ToString(), true);
if (ignoreCase)
{
if (entryString.Lower() == str.Lower())
return entry;
}
else
{
if (entryString == str)
return entry;
}
}
return (TEnum)(object)(-1);
}
}
}

View File

@@ -678,7 +678,7 @@ namespace Flowframes.Os
if (ai.Piped) // VS specific
{
if (!hasShownError && Interpolate.currentSettings.outMode != Interpolate.OutMode.Realtime && line.ToLowerInvariant().Contains("fwrite() call failed"))
if (!hasShownError && Interpolate.currentSettings.outSettings.Format != Enums.Output.Format.Realtime && line.ToLowerInvariant().Contains("fwrite() call failed"))
{
hasShownError = true;
UiUtils.ShowMessageBox($"VapourSynth interpolation failed with an unknown error. Check the log for details:\n\n{lastLogLines}", UiUtils.MessageType.Error);