mirror of https://github.com/n00mkrad/flowframes
427 lines
17 KiB
C#
427 lines
17 KiB
C#
using Flowframes.Data;
|
|
using Flowframes.IO;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
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 readonly List<PixFmt> AlphaFormats = new List<PixFmt> { PixFmt.Rgba, PixFmt.Yuva420P, PixFmt.Yuva444P10Le };
|
|
|
|
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 },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.X265)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.H265,
|
|
Name = "libx265",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P10Le },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.SvtAv1)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.AV1,
|
|
Name = "libsvtav1",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv420P10Le },
|
|
PixelFormatDefault = PixFmt.Yuv420P10Le,
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
MaxFramerate = 240,
|
|
Lossless = null,
|
|
};
|
|
}
|
|
|
|
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 },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Nvenc264)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.H264,
|
|
Name = "h264_nvenc",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
HwAccelerated = true,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Nvenc265)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.H265,
|
|
Name = "hevc_nvenc",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
HwAccelerated = true,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.NvencAv1)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.AV1,
|
|
Name = "av1_nvenc",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
PixelFormatDefault = PixFmt.Yuv420P10Le,
|
|
HwAccelerated = true,
|
|
Lossless = null,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Amf264)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.H264,
|
|
Name = "h264_amf",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
HwAccelerated = true,
|
|
Lossless = null,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Amf265)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.H265,
|
|
Name = "hevc_amf",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
HwAccelerated = true,
|
|
Lossless = null,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Qsv264)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.H264,
|
|
Name = "h264_qsv",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
HwAccelerated = true,
|
|
Lossless = null,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Qsv265)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.H265,
|
|
Name = "hevc_qsv",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.Common>(),
|
|
QualityDefault = (int)Quality.Common.VeryHigh,
|
|
HwAccelerated = true,
|
|
Lossless = null,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.ProResKs)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.ProRes,
|
|
Name = "prores_ks",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv422P10Le, PixFmt.Yuv444P10Le, PixFmt.Yuva444P10Le },
|
|
PixelFormatDefault = PixFmt.Yuv422P10Le,
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.ProResProfile>(),
|
|
QualityDefault = (int)Quality.ProResProfile.Standard,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Gif)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Gif,
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Pal8 },
|
|
PixelFormatDefault = PixFmt.Pal8,
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.GifColors>(),
|
|
QualityDefault = (int)Quality.GifColors.High128,
|
|
OverideExtension = "gif",
|
|
MaxFramerate = 50,
|
|
Modulo = 1,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Ffv1)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Ffv1,
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv422P, PixFmt.Yuv422P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P10Le, PixFmt.Yuv444P12Le, PixFmt.Yuv444P16Le, PixFmt.Rgb48Le, PixFmt.Rgba64Le },
|
|
Lossless = true,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Huffyuv)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Huffyuv,
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv422P, PixFmt.Rgb24, PixFmt.Rgba },
|
|
Lossless = true,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Magicyuv)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Magicyuv,
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P },
|
|
Lossless = true,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Rawvideo)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Rawvideo,
|
|
Lossless = true,
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Png)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Png,
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Rgb24, PixFmt.Rgba, PixFmt.Rgb48Be, PixFmt.Rgba64Be },
|
|
PixelFormatDefault = PixFmt.Rgb24,
|
|
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 },
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.JpegWebm>(),
|
|
QualityDefault = (int)Quality.JpegWebm.ImgHigh,
|
|
IsImageSequence = true,
|
|
OverideExtension = "jpg",
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Webp)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Webp,
|
|
Name = "libwebp",
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuva420P, PixFmt.Rgba }, // Actually only supports BGRA not RGBA, but ffmpeg will auto-pick that
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.JpegWebm>(),
|
|
QualityDefault = (int)Quality.JpegWebm.ImgHigh,
|
|
IsImageSequence = true,
|
|
OverideExtension = "webp",
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Tiff)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Tiff,
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P, PixFmt.Rgb24, PixFmt.Rgba, PixFmt.Rgb48Le, PixFmt.Rgba64Le },
|
|
PixelFormatDefault = PixFmt.Rgb24,
|
|
Lossless = true,
|
|
IsImageSequence = true,
|
|
OverideExtension = "tiff",
|
|
};
|
|
}
|
|
|
|
if (encoder == Encoder.Exr)
|
|
{
|
|
return new EncoderInfoVideo
|
|
{
|
|
Codec = Codec.Exr,
|
|
PixelFormats = new List<PixFmt>() { PixFmt.Gbrpf32Le, PixFmt.Gbrapf32Le },
|
|
PixelFormatDefault = PixFmt.Gbrpf32Le,
|
|
QualityLevels = ParseUtils.GetEnumStrings<Quality.ExrPrecision>(),
|
|
QualityDefault = (int)Quality.ExrPrecision.Half,
|
|
Lossless = false,
|
|
IsImageSequence = true,
|
|
OverideExtension = "exr",
|
|
};
|
|
}
|
|
|
|
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, Codec.H264 };
|
|
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, Codec.Tiff, Codec.Exr };
|
|
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 supportedCodecs = GetSupportedCodecs(format);
|
|
var availableEncoders = supportedCodecs.SelectMany(codec => allEncoders.Where(enc => enc.GetInfo().Codec == codec)).ToList();
|
|
RemoveIncompatibleEncoders(ref availableEncoders, new[] {
|
|
Encoder.Nvenc264, Encoder.Nvenc265, Encoder.NvencAv1,
|
|
Encoder.Amf264, Encoder.Amf265,
|
|
Encoder.Qsv264, Encoder.Qsv265,
|
|
});
|
|
return availableEncoders;
|
|
}
|
|
|
|
private static void RemoveIncompatibleEncoders (ref List<Encoder> encoders, IEnumerable<Encoder> encodersToCheck)
|
|
{
|
|
var availHwEncs = Config.Get(Config.Key.SupportedHwEncoders).Split(',');
|
|
|
|
foreach(Encoder enc in encodersToCheck)
|
|
{
|
|
if (encoders.Contains(enc) && !availHwEncs.Contains(enc.GetInfo().Name))
|
|
encoders.Remove(enc);
|
|
}
|
|
}
|
|
|
|
public static int GetCrf (Quality.Common qualityLevel, Encoder encoder)
|
|
{
|
|
var encoderMultipliers = new Dictionary<Encoder, float>
|
|
{
|
|
{ Encoder.X265, 1.0f },
|
|
{ Encoder.VpxVp9, 1.3f },
|
|
{ Encoder.SvtAv1, 1.3f },
|
|
{ Encoder.Nvenc264, 1.1f },
|
|
{ Encoder.Nvenc265, 1.15f },
|
|
{ Encoder.NvencAv1, 1.3f },
|
|
{ Encoder.Qsv265, 0.8f }
|
|
};
|
|
|
|
float multiplier = encoderMultipliers.TryGetValue(encoder, out float value) ? value : 1.0f;
|
|
return (int)Math.Round(Crfs[qualityLevel] * multiplier);
|
|
}
|
|
|
|
public static int GetGifColors (Quality.GifColors qualityLevel)
|
|
{
|
|
switch (qualityLevel)
|
|
{
|
|
case Quality.GifColors.Max256: return 256;
|
|
case Quality.GifColors.High128: return 128;
|
|
case Quality.GifColors.Medium64: return 64;
|
|
case Quality.GifColors.Low32: return 32;
|
|
case Quality.GifColors.VeryLow16: return 16;
|
|
default: return 128;
|
|
}
|
|
}
|
|
|
|
public static Dictionary<Quality.Common, int> Crfs = new Dictionary<Quality.Common, int>
|
|
{
|
|
{ Quality.Common.Lossless, 0 },
|
|
{ Quality.Common.VeryHigh, 16 },
|
|
{ Quality.Common.High, 20 },
|
|
{ Quality.Common.Medium, 26 },
|
|
{ Quality.Common.Low, 32 },
|
|
{ Quality.Common.VeryLow, 40 },
|
|
};
|
|
|
|
public static Dictionary<Quality.ProResProfile, string> ProresProfiles = new Dictionary<Quality.ProResProfile, string>
|
|
{
|
|
{ Quality.ProResProfile.Proxy, "proxy" },
|
|
{ Quality.ProResProfile.Lt, "proxy" },
|
|
{ Quality.ProResProfile.Standard, "standard" },
|
|
{ Quality.ProResProfile.Hq, "hq" },
|
|
{ Quality.ProResProfile.Quad4, "4444" },
|
|
{ Quality.ProResProfile.Quad4Xq, "4444xq" },
|
|
};
|
|
|
|
public static Dictionary<Quality.JpegWebm, int> JpegQuality = new Dictionary<Quality.JpegWebm, int>
|
|
{
|
|
{ Quality.JpegWebm.ImgMax, 1 },
|
|
{ Quality.JpegWebm.ImgHigh, 3 },
|
|
{ Quality.JpegWebm.ImgMed, 5 },
|
|
{ Quality.JpegWebm.ImgLow, 11 },
|
|
{ Quality.JpegWebm.ImgLowest, 31 },
|
|
};
|
|
|
|
public static Dictionary<Quality.JpegWebm, int> WebpQuality = new Dictionary<Quality.JpegWebm, int>
|
|
{
|
|
{ Quality.JpegWebm.ImgMax, 100 },
|
|
{ Quality.JpegWebm.ImgHigh, 90 },
|
|
{ Quality.JpegWebm.ImgMed, 75 },
|
|
{ Quality.JpegWebm.ImgLow, 40 },
|
|
{ Quality.JpegWebm.ImgLowest, 0 },
|
|
};
|
|
|
|
public static int GetImgSeqQ (OutputSettings settings)
|
|
{
|
|
var qualityLevel = ParseUtils.GetEnum<Quality.JpegWebm>(settings.Quality, true, Strings.VideoQuality);
|
|
|
|
if (settings.Encoder == Encoder.Jpeg)
|
|
return JpegQuality[qualityLevel];
|
|
|
|
if (settings.Encoder == Encoder.Webp)
|
|
return WebpQuality[qualityLevel];
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
}
|