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 Fraction outFps;
public float outItsScale; public float outItsScale;
public float interpFactor; public float interpFactor;
public Interpolate.OutMode outMode; public ExportSettings outSettings;
public ModelCollection.ModelInfo model; public ModelCollection.ModelInfo model;
public string tempFolder; public string tempFolder;
@@ -46,7 +46,7 @@ namespace Flowframes
public InterpSettings() { } 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; inPath = inPathArg;
outPath = outPathArg; outPath = outPathArg;
@@ -56,7 +56,7 @@ namespace Flowframes
interpFactor = interpFactorArg; interpFactor = interpFactorArg;
outFps = inFpsArg * (double)interpFactorArg; outFps = inFpsArg * (double)interpFactorArg;
outItsScale = itsScale; outItsScale = itsScale;
outMode = outModeArg; outSettings = outSettingsArg;
model = modelArg; model = modelArg;
alpha = false; alpha = false;
@@ -95,7 +95,7 @@ namespace Flowframes
inFps = new Fraction(); inFps = new Fraction();
interpFactor = 0; interpFactor = 0;
outFps = new Fraction(); outFps = new Fraction();
outMode = Interpolate.OutMode.VidMp4; outSettings = new ExportSettings();
model = null; model = null;
alpha = false; alpha = false;
stepByStep = false; stepByStep = false;
@@ -112,6 +112,7 @@ namespace Flowframes
entries.Add(keyValuePair[0], keyValuePair[1]); entries.Add(keyValuePair[0], keyValuePair[1]);
} }
// TODO: Rework this ugly stuff, JSON?
foreach (KeyValuePair<string, string> entry in entries) foreach (KeyValuePair<string, string> entry in entries)
{ {
switch (entry.Key) switch (entry.Key)
@@ -123,7 +124,7 @@ namespace Flowframes
case "INFPS": inFps = new Fraction(entry.Value); break; case "INFPS": inFps = new Fraction(entry.Value); break;
case "OUTFPS": outFps = new Fraction(entry.Value); break; case "OUTFPS": outFps = new Fraction(entry.Value); break;
case "INTERPFACTOR": interpFactor = float.Parse(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 "MODEL": model = AiModels.GetModelByName(ai, entry.Value); break;
case "INPUTRES": _inputResolution = FormatUtils.ParseSize(entry.Value); break; case "INPUTRES": _inputResolution = FormatUtils.ParseSize(entry.Value); break;
case "ALPHA": alpha = bool.Parse(entry.Value); break; case "ALPHA": alpha = bool.Parse(entry.Value); break;
@@ -173,14 +174,13 @@ namespace Flowframes
try try
{ {
bool alphaModel = model.SupportsAlpha; bool alphaModel = model.SupportsAlpha;
bool png = outMode == Interpolate.OutMode.ImgPng; bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
bool gif = outMode == Interpolate.OutMode.VidGif; bool gifOutput = outSettings.Encoder == Enums.Encoding.Encoder.Gif;
bool proResAlpha = outMode == Interpolate.OutMode.VidProRes && Config.GetInt(Config.Key.proResProfile) > 3; 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 = png || gif || proResAlpha; bool outputSupportsAlpha = pngOutput || gifOutput || proResAlpha;
string ext = inputIsFrames ? Path.GetExtension(IoUtils.GetFilesSorted(inPath).First()).ToLowerInvariant() : Path.GetExtension(inPath).ToLowerInvariant(); 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")); alpha = (alphaModel && outputSupportsAlpha && (ext == ".gif" || ext == ".png" || ext == ".apng" || ext == ".mov"));
Logger.Log($"RefreshAlpha: model.supportsAlpha = {alphaModel} - outputSupportsAlpha = {outputSupportsAlpha} - " + Logger.Log($"RefreshAlpha: model.supportsAlpha = {alphaModel} - outputSupportsAlpha = {outputSupportsAlpha} - input ext: {ext} => alpha = {alpha}", true);
$"input ext: {ext} => alpha = {alpha}", true);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -193,9 +193,9 @@ namespace Flowframes
public void RefreshExtensions(FrameType type = FrameType.Both) public void RefreshExtensions(FrameType type = FrameType.Both)
{ {
bool pngOutput = outMode == Interpolate.OutMode.ImgPng; bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
bool aviHqChroma = outMode == Interpolate.OutMode.VidAvi && Config.Get(Config.Key.aviColors) != "yuv420p"; 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 = outMode == Interpolate.OutMode.VidProRes && Config.GetInt(Config.Key.proResProfile) > 3; 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; bool forceHqChroma = pngOutput || aviHqChroma || proresHqChroma;
@@ -230,7 +230,7 @@ namespace Flowframes
s += $"INFPS|{inFps}\n"; s += $"INFPS|{inFps}\n";
s += $"OUTFPS|{outFps}\n"; s += $"OUTFPS|{outFps}\n";
s += $"INTERPFACTOR|{interpFactor}\n"; s += $"INTERPFACTOR|{interpFactor}\n";
s += $"OUTMODE|{outMode}\n"; s += $"OUTMODE|{outSettings.Format}\n";
s += $"MODEL|{model.Name}\n"; s += $"MODEL|{model.Name}\n";
s += $"INPUTRES|{InputResolution.Width}x{InputResolution.Height}\n"; s += $"INPUTRES|{InputResolution.Width}x{InputResolution.Height}\n";
s += $"OUTPUTRES|{ScaledResolution.Width}x{ScaledResolution.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 Flowframes.Data;
using System.Management.Automation; using System.Management.Automation;
using System.Drawing; using System.Drawing;
using Flowframes.MiscUtils;
namespace Flowframes namespace Flowframes
{ {
@@ -271,5 +272,99 @@ namespace Flowframes
{ {
return $"{(filePath.IsConcatFile() ? filePath.GetConcStr() : "")} -i {filePath.Wrap()}"; 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> <ItemGroup>
<Compile Include="Data\AI.cs" /> <Compile Include="Data\AI.cs" />
<Compile Include="Data\AudioTrack.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\Filetypes.cs" />
<Compile Include="Data\MediaFile.cs" /> <Compile Include="Data\MediaFile.cs" />
<Compile Include="Data\ModelCollection.cs" /> <Compile Include="Data\ModelCollection.cs" />
@@ -344,6 +348,7 @@
<Compile Include="Data\Streams\Stream.cs" /> <Compile Include="Data\Streams\Stream.cs" />
<Compile Include="Data\Streams\SubtitleStream.cs" /> <Compile Include="Data\Streams\SubtitleStream.cs" />
<Compile Include="Data\Streams\VideoStream.cs" /> <Compile Include="Data\Streams\VideoStream.cs" />
<Compile Include="Data\Strings.cs" />
<Compile Include="Data\VideoColorData.cs" /> <Compile Include="Data\VideoColorData.cs" />
<Compile Include="Data\VidExtraData.cs" /> <Compile Include="Data\VidExtraData.cs" />
<Compile Include="Data\Fraction.cs" /> <Compile Include="Data\Fraction.cs" />
@@ -439,6 +444,8 @@
<Compile Include="MiscUtils\FrameRename.cs" /> <Compile Include="MiscUtils\FrameRename.cs" />
<Compile Include="MiscUtils\ModelDownloadFormUtils.cs" /> <Compile Include="MiscUtils\ModelDownloadFormUtils.cs" />
<Compile Include="MiscUtils\NmkdStopwatch.cs" /> <Compile Include="MiscUtils\NmkdStopwatch.cs" />
<Compile Include="MiscUtils\OutputUtils.cs" />
<Compile Include="MiscUtils\ParseUtils.cs" />
<Compile Include="Os\AiProcess.cs" /> <Compile Include="Os\AiProcess.cs" />
<Compile Include="Extensions\ExtensionMethods.cs" /> <Compile Include="Extensions\ExtensionMethods.cs" />
<Compile Include="Form1.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.label15 = new System.Windows.Forms.Label();
this.label11 = new System.Windows.Forms.Label(); this.label11 = new System.Windows.Forms.Label();
this.interpOptsTab = new System.Windows.Forms.TabPage(); 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.aiInfoBtn = new HTAlt.WinForms.HTButton();
this.outSpeedCombox = new System.Windows.Forms.ComboBox(); this.outSpeedCombox = new System.Windows.Forms.ComboBox();
this.completionActionPanel = new System.Windows.Forms.Panel(); this.completionActionPanel = new System.Windows.Forms.Panel();
@@ -137,6 +141,7 @@
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.pauseBtn = new System.Windows.Forms.Button(); this.pauseBtn = new System.Windows.Forms.Button();
this.cancelBtn = new System.Windows.Forms.Button(); this.cancelBtn = new System.Windows.Forms.Button();
this.comboxOutputEncoder = new System.Windows.Forms.ComboBox();
this.panel1.SuspendLayout(); this.panel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit();
@@ -149,6 +154,7 @@
this.panel8.SuspendLayout(); this.panel8.SuspendLayout();
this.panel6.SuspendLayout(); this.panel6.SuspendLayout();
this.interpOptsTab.SuspendLayout(); this.interpOptsTab.SuspendLayout();
this.flowLayoutPanel1.SuspendLayout();
this.completionActionPanel.SuspendLayout(); this.completionActionPanel.SuspendLayout();
this.quickSettingsTab.SuspendLayout(); this.quickSettingsTab.SuspendLayout();
this.mpDedupePanel.SuspendLayout(); this.mpDedupePanel.SuspendLayout();
@@ -228,7 +234,7 @@
"Animated GIF (Only supports up to 50 FPS)", "Animated GIF (Only supports up to 50 FPS)",
"Image Sequence (PNG, JPG, WEBP)", "Image Sequence (PNG, JPG, WEBP)",
"Real-time Interpolation (Video only)"}); "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.Name = "outModeCombox";
this.outModeCombox.Size = new System.Drawing.Size(400, 23); this.outModeCombox.Size = new System.Drawing.Size(400, 23);
this.outModeCombox.TabIndex = 16; this.outModeCombox.TabIndex = 16;
@@ -915,6 +921,7 @@
// //
this.interpOptsTab.AllowDrop = true; this.interpOptsTab.AllowDrop = true;
this.interpOptsTab.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48))))); 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.aiInfoBtn);
this.interpOptsTab.Controls.Add(this.pictureBox5); this.interpOptsTab.Controls.Add(this.pictureBox5);
this.interpOptsTab.Controls.Add(this.outSpeedCombox); this.interpOptsTab.Controls.Add(this.outSpeedCombox);
@@ -954,6 +961,66 @@
this.interpOptsTab.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form1_DragDrop); this.interpOptsTab.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form1_DragDrop);
this.interpOptsTab.DragEnter += new System.Windows.Forms.DragEventHandler(this.Form1_DragEnter); 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 // aiInfoBtn
// //
this.aiInfoBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); 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.FlatAppearance.BorderSize = 0;
this.encodingSettingsBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.encodingSettingsBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.encodingSettingsBtn.ForeColor = System.Drawing.Color.White; 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.Name = "encodingSettingsBtn";
this.encodingSettingsBtn.Size = new System.Drawing.Size(206, 23); this.encodingSettingsBtn.Size = new System.Drawing.Size(206, 23);
this.encodingSettingsBtn.TabIndex = 39; this.encodingSettingsBtn.TabIndex = 39;
@@ -1641,6 +1708,29 @@
this.cancelBtn.UseVisualStyleBackColor = true; this.cancelBtn.UseVisualStyleBackColor = true;
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click); 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 // Form1
// //
this.AllowDrop = true; this.AllowDrop = true;
@@ -1694,6 +1784,7 @@
this.panel6.PerformLayout(); this.panel6.PerformLayout();
this.interpOptsTab.ResumeLayout(false); this.interpOptsTab.ResumeLayout(false);
this.interpOptsTab.PerformLayout(); this.interpOptsTab.PerformLayout();
this.flowLayoutPanel1.ResumeLayout(false);
this.completionActionPanel.ResumeLayout(false); this.completionActionPanel.ResumeLayout(false);
this.completionActionPanel.PerformLayout(); this.completionActionPanel.PerformLayout();
this.quickSettingsTab.ResumeLayout(false); this.quickSettingsTab.ResumeLayout(false);
@@ -1824,6 +1915,11 @@
private System.Windows.Forms.ComboBox outSpeedCombox; private System.Windows.Forms.ComboBox outSpeedCombox;
private System.Windows.Forms.PictureBox pictureBox5; private System.Windows.Forms.PictureBox pictureBox5;
private HTAlt.WinForms.HTButton aiInfoBtn; 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 // Main Tab
UiUtils.InitCombox(interpFactorCombox, 0); UiUtils.InitCombox(interpFactorCombox, 0);
UiUtils.InitCombox(outSpeedCombox, 0); UiUtils.InitCombox(outSpeedCombox, 0);
UiUtils.InitCombox(outModeCombox, 0); //UiUtils.InitCombox(outModeCombox, 0);
InitOutputUi();
UiUtils.InitCombox(aiModel, 2); UiUtils.InitCombox(aiModel, 2);
// Video Utils // Video Utils
UiUtils.InitCombox(trimCombox, 0); UiUtils.InitCombox(trimCombox, 0);
@@ -84,6 +85,39 @@ namespace Flowframes
await Checks(); 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() async Task Checks()
{ {
try try
@@ -164,8 +198,7 @@ namespace Flowframes
public InterpSettings GetCurrentSettings() public InterpSettings GetCurrentSettings()
{ {
SetTab("interpolate"); SetTab("interpolate");
return new InterpSettings(inputTbox.Text.Trim(), outputTbox.Text.Trim(), GetAi(), currInFpsDetected, currInFps, return new InterpSettings(inputTbox.Text.Trim(), outputTbox.Text.Trim(), GetAi(), currInFpsDetected, currInFps, interpFactorCombox.GetFloat(), outSpeedCombox.GetInt().Clamp(1, 64), GetExportSettings, GetModel(GetAi()));
interpFactorCombox.GetFloat(), outSpeedCombox.GetInt().Clamp(1, 64), GetOutMode(), GetModel(GetAi()));
} }
public InterpSettings UpdateCurrentSettings(InterpSettings settings) public InterpSettings UpdateCurrentSettings(InterpSettings settings)
@@ -185,7 +218,7 @@ namespace Flowframes
settings.inFps = currInFps; settings.inFps = currInFps;
settings.interpFactor = interpFactorCombox.GetFloat(); settings.interpFactor = interpFactorCombox.GetFloat();
settings.outFps = settings.inFps * settings.interpFactor; settings.outFps = settings.inFps * settings.interpFactor;
settings.outMode = GetOutMode(); settings.outSettings = GetExportSettings;
settings.model = GetModel(GetAi()); settings.model = GetModel(GetAi());
return settings; return settings;
@@ -197,7 +230,7 @@ namespace Flowframes
MainUiFunctions.SetOutPath(outputTbox, entry.outPath); MainUiFunctions.SetOutPath(outputTbox, entry.outPath);
interpFactorCombox.Text = entry.interpFactor.ToString(); interpFactorCombox.Text = entry.interpFactor.ToString();
aiCombox.SelectedIndex = Implementations.NetworksAvailable.IndexOf(Implementations.NetworksAvailable.Where(x => x.NameInternal == entry.ai.NameInternal).FirstOrDefault()); 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) public void SetStatus(string str)
@@ -287,7 +320,7 @@ namespace Flowframes
ConfigParser.LoadComboxIndex(outModeCombox); ConfigParser.LoadComboxIndex(outModeCombox);
} }
private string GetAiComboboxName (AI ai) private string GetAiComboboxName(AI ai)
{ {
return ai.FriendlyName + " - " + ai.Description; return ai.FriendlyName + " - " + ai.Description;
} }
@@ -334,7 +367,7 @@ namespace Flowframes
AiProcessSuspend.Reset(); AiProcessSuspend.Reset();
if(Interpolate.currentSettings.outMode == Interpolate.OutMode.Realtime) if (Interpolate.currentSettings.outSettings.Format == Enums.Output.Format.Realtime)
{ {
await Interpolate.Realtime(); await Interpolate.Realtime();
SetProgress(0); SetProgress(0);
@@ -345,11 +378,6 @@ namespace Flowframes
} }
} }
private async void RealtimeInterp(object sender, EventArgs e)
{
}
public ModelCollection.ModelInfo GetModel(AI currentAi) public ModelCollection.ModelInfo GetModel(AI currentAi)
{ {
try 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; outModeCombox.Text = Strings.OutputFormat.Get(format.ToString());
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;
} }
public AI GetAi() public AI GetAi()
@@ -768,8 +774,18 @@ namespace Flowframes
{ {
var ai = GetAi(); var ai = GetAi();
if(ai != null) if (ai != null)
UiUtils.ShowMessageBox(ai.GetVerboseInfo(), UiUtils.MessageType.Message); 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++) for (int i = 0; i < Program.batchQueue.Count; i++)
{ {
InterpSettings entry = Program.batchQueue.ElementAt(i); InterpSettings entry = Program.batchQueue.ElementAt(i);
string niceOutMode = entry.outMode.ToString().ToUpper().Remove("VID").Remove("IMG"); string outFormat = Strings.OutputFormat.Get(entry.outSettings.Format.ToString());
string str = $"#{i+1}: {Path.GetFileName(entry.inPath).Trunc(40)} - {entry.inFps.GetFloat()} FPS => " + string str = $"#{i+1}: {Path.GetFileName(entry.inPath).Trunc(40)} - {entry.inFps.GetFloat()} FPS => {entry.interpFactor}x {entry.ai.NameShort} ({entry.model.Name}) => {outFormat}";
$"{entry.interpFactor}x {entry.ai.NameShort} ({entry.model.Name}) => {niceOutMode}";
taskList.Items.Add(str); taskList.Items.Add(str);
} }
} }

View File

@@ -253,18 +253,19 @@ namespace Flowframes.Forms
private void mp4Enc_SelectedIndexChanged(object sender, EventArgs e) private void mp4Enc_SelectedIndexChanged(object sender, EventArgs e)
{ {
string text = mp4Enc.Text.ToUpper().Remove(" "); // string text = mp4Enc.Text.ToUpper().Remove(" ");
//
if (text.Contains(FfmpegUtils.Codec.H264.ToString().ToUpper())) // if (text.Contains(FfmpegUtils.Codec.H264.ToString().ToUpper()))
mp4CrfConfigKey = "h264Crf"; // mp4CrfConfigKey = "h264Crf";
//
if (text.Contains(FfmpegUtils.Codec.H265.ToString().ToUpper())) // if (text.Contains(FfmpegUtils.Codec.H265.ToString().ToUpper()))
mp4CrfConfigKey = "h265Crf"; // mp4CrfConfigKey = "h265Crf";
//
if (text.Contains(FfmpegUtils.Codec.Av1.ToString().ToUpper())) // if (text.Contains(FfmpegUtils.Codec.Av1.ToString().ToUpper()))
mp4CrfConfigKey = "av1Crf"; // mp4CrfConfigKey = "av1Crf";
//
mp4Crf.Value = Config.GetInt(mp4CrfConfigKey); // mp4Crf.Value = Config.GetInt(mp4CrfConfigKey);
throw new NotImplementedException();
} }
private void modelDownloaderBtn_Click(object sender, EventArgs e) 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()); Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
float fps = fpsLimit ? maxFps.GetFloat() : curr.outFps.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); Size outRes = await InterpolateUtils.GetOutputResolution(curr.inPath, true);
string pattern = Config.Get(Config.Key.exportNamePattern); string pattern = Config.Get(Config.Key.exportNamePattern);
string inName = Interpolate.currentSettings.inputIsFrames ? Path.GetFileName(curr.inPath) : Path.GetFileNameWithoutExtension(curr.inPath); 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("[FACTOR]", curr.interpFactor.ToStringDot());
filename = filename.Replace("[AI]", curr.ai.NameShort.ToUpper()); filename = filename.Replace("[AI]", curr.ai.NameShort.ToUpper());
filename = filename.Replace("[MODEL]", curr.model.Name.Remove(" ")); 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("[ROUNDFPS]", fps.RoundToInt().ToString());
filename = filename.Replace("[RES]", $"{outRes.Width}x{outRes.Height}"); filename = filename.Replace("[RES]", $"{outRes.Width}x{outRes.Height}");
filename = filename.Replace("[H]", $"{outRes.Height}p"); filename = filename.Replace("[H]", $"{outRes.Height}p");
@@ -589,7 +586,7 @@ namespace Flowframes.IO
filename += Paths.fpsLimitSuffix; filename += Paths.fpsLimitSuffix;
if (withExt) if (withExt)
filename += FfmpegUtils.GetExt(curr.outMode); filename += FfmpegUtils.GetExt(curr.outSettings);
return filename; return filename;
} }

View File

@@ -54,7 +54,7 @@ namespace Flowframes.Main
{ {
UpdateChunkAndBufferSizes(); UpdateChunkAndBufferSizes();
bool imgSeq = Interpolate.currentSettings.outMode.ToString().ToLowerInvariant().StartsWith("img"); bool imgSeq = Interpolate.currentSettings.outSettings.Encoder.GetInfo().IsImageSequence;
interpFramesFolder = interpFramesPath; interpFramesFolder = interpFramesPath;
videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir); videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir);
@@ -130,12 +130,12 @@ namespace Flowframes.Main
} }
busy = true; 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 firstFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.First()].Trim());
string lastFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()].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"); 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; 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) if(Config.GetInt(Config.Key.sceneChangeFillMode) == 1)
{ {
@@ -30,7 +30,7 @@ namespace Flowframes.Main
await Blend.BlendSceneChanges(frameFile); 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 try
{ {
@@ -61,10 +61,10 @@ namespace Flowframes.Main
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0; bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
if (!dontEncodeFullFpsVid) 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) 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) catch (Exception e)
{ {
@@ -76,7 +76,7 @@ namespace Flowframes.Main
public static async Task<string> GetPipedFfmpegCmd(bool ffplay = false) public static async Task<string> GetPipedFfmpegCmd(bool ffplay = false)
{ {
InterpSettings s = I.currentSettings; 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); string max = Config.Get(Config.Key.maxFps);
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat()); 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); VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(s.inPath);
string extraArgsIn = FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale); 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) 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)); string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
@@ -207,14 +207,14 @@ namespace Flowframes.Main
return; 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); await FfmpegEncode.FramesToGifConcat(framesFile, outPath, fps, true, Config.GetInt(Config.Key.gifColors), resampleFps, I.currentSettings.outItsScale);
} }
else else
{ {
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath); 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 MuxOutputVideo(I.currentSettings.inPath, outPath);
await Loop(outPath, await GetLoopTimes()); 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) 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); I.Cancel("No video chunks found - An error must have occured during chunk encoding!", AiProcess.hasShownError);
return; return;
@@ -289,7 +289,7 @@ namespace Flowframes.Main
await Loop(outPath, await GetLoopTimes()); 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 framesFileFull = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
string concatFile = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilenameChunk(firstFrameNum, firstFrameNum + framesAmount)); 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; 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 desiredFormat = Config.Get(Config.Key.imgSeqFormat);
string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(interpDir)[0]).Remove(".").ToUpper(); string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(interpDir)[0]).Remove(".").ToUpper();
@@ -336,14 +336,14 @@ namespace Flowframes.Main
else else
{ {
if (!dontEncodeFullFpsVid) 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) if (fpsLimit)
{ {
string filename = Path.GetFileName(outPath); string filename = Path.GetFileName(outPath);
string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix; string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix;
outPath = Path.Combine(newParentDir, filename); 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 class Interpolate
{ {
public enum OutMode { VidMp4, VidMkv, VidWebm, VidProRes, VidAvi, VidGif, ImgPng, Realtime }
public static bool currentlyUsingAutoEnc; public static bool currentlyUsingAutoEnc;
public static InterpSettings currentSettings; public static InterpSettings currentSettings;
public static MediaFile currentMediaFile; 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.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 (!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 (!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); Utils.ShowWarnings(currentSettings.interpFactor, currentSettings.ai);
currentSettings.stepByStep = false; currentSettings.stepByStep = false;
Program.mainForm.SetStatus("Starting..."); Program.mainForm.SetStatus("Starting...");
@@ -65,7 +63,7 @@ namespace Flowframes
if (currentSettings.ai.Piped) if (currentSettings.ai.Piped)
await Export.MuxPipedVideo(currentSettings.inPath, currentSettings.FullOutPath); await Export.MuxPipedVideo(currentSettings.inPath, currentSettings.FullOutPath);
else 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) if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder) && IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false) > 0)

View File

@@ -93,7 +93,7 @@ namespace Flowframes.Main
return; 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; if (canceled) return;
Program.mainForm.SetStatus("Running AI..."); 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); string[] outFrames = IoUtils.GetFilesSorted(currentSettings.interpFolder, currentSettings.interpExt);
@@ -130,7 +130,7 @@ namespace Flowframes.Main
return; 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() public static async Task Reset()

View File

@@ -121,18 +121,16 @@ namespace Flowframes.Main
passes = false; 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); string fpsLimitValue = Config.Get(Config.Key.maxFps);
float fpsLimit = (fpsLimitValue.Contains("/") ? new Fraction(Config.Get(Config.Key.maxFps)).GetFloat() : fpsLimitValue.GetFloat()); 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)) if (passes && s.outFps.GetFloat() < 1f || (s.outFps.GetFloat() > maxFps && !(fpsLimit > 0 && fpsLimit <= maxFps)))
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."); {
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) if (!passes)
I.Cancel("Invalid settings detected.", true); I.Cancel("Invalid settings detected.", true);
@@ -222,20 +220,9 @@ namespace Flowframes.Main
return true; 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)); string enc = I.currentSettings.outSettings.Encoder.ToString();
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;
}
if (enc.ToLowerInvariant().Contains("nvenc") && !(await FfmpegCommands.IsEncoderCompatible(enc))) if (enc.ToLowerInvariant().Contains("nvenc") && !(await FfmpegCommands.IsEncoderCompatible(enc)))
{ {
@@ -302,7 +289,7 @@ namespace Flowframes.Main
return false; 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); Logger.Log($"Not Using AutoEnc: Using GIF output", true);
return false; return false;

View File

@@ -29,9 +29,9 @@ namespace Flowframes.Media
string subArgs = "-c:s " + Utils.GetSubCodecForContainer(containerExt); 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; 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) if (!audioCompat && !slowmo)
Logger.Log("Warning: Input audio format(s) not fully supported in output container - Will re-encode.", true, false, "ffmpeg"); 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))) if (!subs || (subs && !Utils.ContainerSupportsSubs(containerExt)))
subArgs = "-sn"; 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 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 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" : ""; string shortestArg = shortest ? "-shortest" : "";

View File

@@ -24,12 +24,19 @@ namespace Flowframes
public static int GetModulo () 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 () public static string GetPadFilter ()
{ {
int mod = GetModulo(); int mod = GetModulo();
if (mod < 2)
return "";
return $"pad=width=ceil(iw/{mod})*{mod}:height=ceil(ih/{mod})*{mod}:color=black@0"; 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 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) if (logMode != LogMode.Hidden)
Logger.Log((resampleFps.GetFloat() <= 0) ? "Encoding video..." : $"Encoding video resampled to {resampleFps.GetString()} FPS..."); Logger.Log((resampleFps.GetFloat() <= 0) ? "Encoding video..." : $"Encoding video resampled to {resampleFps.GetString()} FPS...");
IoUtils.RenameExistingFile(outPath); IoUtils.RenameExistingFile(outPath);
Directory.CreateDirectory(outPath.GetParentDir()); 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 inArg = $"-f concat -i {Path.GetFileName(framesFile)}";
string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix); string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix);
@@ -33,11 +33,11 @@ namespace Flowframes.Media
string args = ""; 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 pre = i == 0 ? "" : $" && ffmpeg {AvProcess.GetFfmpegDefaultArgs()}";
string post = (i == 0 && encArgs.Length > 1) ? $"-f null -" : outPath.Wrap(); 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); await RunFfmpeg(args, framesFile.GetParentDir(), logMode, !isChunk);
@@ -50,7 +50,7 @@ namespace Flowframes.Media
return $"-r {fps}"; 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>(); List<string> filters = new List<string>();
string extraArgs = Config.Get(Config.Key.ffEncArgs); string extraArgs = Config.Get(Config.Key.ffEncArgs);
@@ -68,14 +68,14 @@ namespace Flowframes.Media
if (!string.IsNullOrWhiteSpace(extraData.displayRatio) && !extraData.displayRatio.MatchesWildcard("*N/A*")) if (!string.IsNullOrWhiteSpace(extraData.displayRatio) && !extraData.displayRatio.MatchesWildcard("*N/A*"))
extraArgs += $" -aspect {extraData.displayRatio}"; extraArgs += $" -aspect {extraData.displayRatio}";
if(!isChunk && outMode == Interpolate.OutMode.VidMp4) if (!isChunk && settings.Format == Enums.Output.Format.Mp4)
extraArgs += $" -movflags +faststart"; extraArgs += $" -movflags +faststart";
filters.Add(GetPadFilter()); filters.Add(GetPadFilter());
return filters.Count > 0 ? $"-vf {string.Join(",", filters)}" : "" + $" {extraArgs}"; 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]); return Path.GetExtension(File.ReadAllLines(concatFilePath).FirstOrDefault().Split('\'')[1]);
} }
@@ -110,7 +110,7 @@ namespace Flowframes.Media
if (logMode != LogMode.Hidden) if (logMode != LogMode.Hidden)
Logger.Log((resampleFps.GetFloat() <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.GetFloat().ToString().Replace(",", ".")} FPS..."); Logger.Log((resampleFps.GetFloat() <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.GetFloat().ToString().Replace(",", ".")} FPS...");
string framesFilename = Path.GetFileName(framesFile); string framesFilename = Path.GetFileName(framesFile);
string dither = Config.Get(Config.Key.gifDitherType).Split(' ').First(); 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}\"" : ""; 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.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Flowframes.Data.Enums.Encoding;
using static Flowframes.Media.GetVideoInfo; using static Flowframes.Media.GetVideoInfo;
using Stream = Flowframes.Data.Streams.Stream; using Stream = Flowframes.Data.Streams.Stream;
@@ -170,127 +171,88 @@ namespace Flowframes.Media
return false; return false;
} }
public enum Codec { H264, H265, H264Nvenc, H265Nvenc, Av1, Vp9, ProRes, AviRaw, Gif } 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 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.
{ {
int keyint = 10; 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 preset = Config.Get(Config.Key.ffEncPreset).ToLowerInvariant().Remove(" "); // TODO: Replace this ugly stuff with enums
string g = GetKeyIntArg(fps, keyint); args.Add($"-crf {Config.GetInt(Config.Key.h264Crf)} -preset {preset}");
return new string[] { $"-c:v {GetEnc(codec)} -crf {Config.GetInt(Config.Key.h264Crf)} -preset {preset} {g} -pix_fmt {GetPixFmt()}" };
} }
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); int crf = Config.GetInt(Config.Key.h265Crf);
string g = GetKeyIntArg(fps, keyint); args.Add($"{(crf > 0 ? $"-crf {crf}" : "-x265-params lossless=1")} -preset {preset}");
return new string[] { $"-c:v {GetEnc(codec)} {(crf > 0 ? $"-crf {crf}" : "-x265-params lossless=1")} -preset {preset} {g} -pix_fmt {GetPixFmt()}" };
} }
if (codec == Codec.H264Nvenc) if (enc == Encoder.SvtAv1)
{
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)
{ {
int cq = Config.GetInt(Config.Key.av1Crf); int cq = Config.GetInt(Config.Key.av1Crf);
string g = GetKeyIntArg(fps, keyint); args.Add($"-b:v 0 -qp {cq} {GetSvtAv1Speed()} -svtav1-params enable-qm=1:enable-overlays=1:enable-tf=0:scd=0");
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()}" };
} }
if (codec == Codec.Vp9) if (enc == Encoder.VpxVp9)
{ {
int crf = Config.GetInt(Config.Key.vp9Crf); int crf = Config.GetInt(Config.Key.vp9Crf);
string qualityStr = (crf > 0) ? $"-crf {crf}" : "-lossless 1"; string qualityStr = (crf > 0) ? $"-crf {crf}" : "-lossless 1";
string g = GetKeyIntArg(fps, keyint);
string t = GetTilingArgs(res, "-tile-columns ", "-tile-rows "); string t = GetTilingArgs(res, "-tile-columns ", "-tile-rows ");
if (realtime) // Force 1-pass 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 else
{ {
return new string[] { return new string[] {
$"-c:v {GetEnc(codec)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 {g} -pass 1 -pix_fmt {GetPixFmt()} -an", $"{string.Join(" ", args)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 -pass 1 -an",
$"-c:v {GetEnc(codec)} -b:v 0 {qualityStr} {GetVp9Speed()} {t} -row-mt 1 {g} -pass 2 -pix_fmt {GetPixFmt()}" $"{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) 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); 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 ") 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}"; return $"{arg}{keyInt}";
} }
@@ -348,34 +310,21 @@ namespace Flowframes.Media
return $"-preset {arg}"; return $"-preset {arg}";
} }
static string GetPixFmt() public static bool ContainerSupportsAllAudioFormats(Enums.Output.Format outFormat, List<string> codecs)
{
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)
{ {
if (codecs.Count < 1) if (codecs.Count < 1)
Logger.Log($"Warning: ContainerSupportsAllAudioFormats() was called, but codec list has {codecs.Count} entries.", true, false, "ffmpeg"); Logger.Log($"Warning: ContainerSupportsAllAudioFormats() was called, but codec list has {codecs.Count} entries.", true, false, "ffmpeg");
foreach (string format in codecs) foreach (string format in codecs)
{ {
if (!ContainerSupportsAudioFormat(outMode, format)) if (!ContainerSupportsAudioFormat(outFormat, format))
return false; return false;
} }
return true; return true;
} }
public static bool ContainerSupportsAudioFormat(Interpolate.OutMode outMode, string format) public static bool ContainerSupportsAudioFormat(Enums.Output.Format outFormat, string format)
{ {
bool supported = false; bool supported = false;
string alias = GetAudioExt(format); string alias = GetAudioExt(format);
@@ -383,35 +332,31 @@ namespace Flowframes.Media
string[] formatsMp4 = new string[] { "m4a", "mp3", "ac3", "dts" }; string[] formatsMp4 = new string[] { "m4a", "mp3", "ac3", "dts" };
string[] formatsMkv = new string[] { "m4a", "mp3", "ac3", "dts", "ogg", "mp2", "wav", "wma" }; string[] formatsMkv = new string[] { "m4a", "mp3", "ac3", "dts", "ogg", "mp2", "wav", "wma" };
string[] formatsWebm = new string[] { "ogg" }; 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" }; string[] formatsAvi = new string[] { "m4a", "ac3", "dts" };
switch (outMode) switch (outFormat)
{ {
case Interpolate.OutMode.VidMp4: supported = formatsMp4.Contains(alias); break; case Enums.Output.Format.Mp4: supported = formatsMp4.Contains(alias); break;
case Interpolate.OutMode.VidMkv: supported = formatsMkv.Contains(alias); break; case Enums.Output.Format.Mkv: supported = formatsMkv.Contains(alias); break;
case Interpolate.OutMode.VidWebm: supported = formatsWebm.Contains(alias); break; case Enums.Output.Format.Webm: supported = formatsWebm.Contains(alias); break;
case Interpolate.OutMode.VidProRes: supported = formatsProres.Contains(alias); break; case Enums.Output.Format.Mov: supported = formatsMov.Contains(alias); break;
case Interpolate.OutMode.VidAvi: supported = formatsAvi.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; return supported;
} }
public static string GetExt(Interpolate.OutMode outMode, bool dot = true) public static string GetExt(ExportSettings settings, bool dot = true)
{ {
string ext = dot ? "." : ""; string ext = dot ? "." : "";
EncoderInfoVideo info = settings.Encoder.GetInfo();
switch (outMode) if (string.IsNullOrWhiteSpace(info.OverideExtension))
{ ext += settings.Format.ToString().Lower();
case Interpolate.OutMode.VidMp4: ext += "mp4"; break; else
case Interpolate.OutMode.VidMkv: ext += "mkv"; break; ext += info.OverideExtension;
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;
}
return ext; return ext;
} }
@@ -440,7 +385,7 @@ namespace Flowframes.Media
return "unsupported"; 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); bool opusMp4 = Config.GetBool(Config.Key.allowOpusInMp4);
int opusBr = Config.GetInt(Config.Key.opusBitrate, 128); 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(); int ac = (await GetVideoInfo.GetFfprobeInfoAsync(videoPath, GetVideoInfo.FfprobeMode.ShowStreams, "channels", 0)).GetInt();
string af = GetAudioFilters(itsScale); 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 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 else
return $"-c:a aac -b:a {(ac > 4 ? $"{aacBr * 2}" : $"{aacBr}")}k -aac_coder twoloop -ac {(ac > 0 ? $"{ac}" : "2")} {af}"; 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 (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; hasShownError = true;
UiUtils.ShowMessageBox($"VapourSynth interpolation failed with an unknown error. Check the log for details:\n\n{lastLogLines}", UiUtils.MessageType.Error); UiUtils.ShowMessageBox($"VapourSynth interpolation failed with an unknown error. Check the log for details:\n\n{lastLogLines}", UiUtils.MessageType.Error);