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 AlphaFormats = new List { 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.Yuv420P, PixFmt.Yuv444P }, QualityLevels = ParseUtils.GetEnumStrings(), QualityDefault = (int)Quality.Common.VeryHigh, }; } if (encoder == Encoder.X265) { return new EncoderInfoVideo { Codec = Codec.H265, Name = "libx265", PixelFormats = new List() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P10Le }, QualityLevels = ParseUtils.GetEnumStrings(), QualityDefault = (int)Quality.Common.VeryHigh, }; } if (encoder == Encoder.SvtAv1) { return new EncoderInfoVideo { Codec = Codec.AV1, Name = "libsvtav1", PixelFormats = new List() { PixFmt.Yuv420P, PixFmt.Yuv420P10Le }, PixelFormatDefault = PixFmt.Yuv420P10Le, QualityLevels = ParseUtils.GetEnumStrings(), 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.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le, PixFmt.Yuv444P, PixFmt.Yuv444P10Le }, QualityLevels = ParseUtils.GetEnumStrings(), QualityDefault = (int)Quality.Common.VeryHigh, }; } if (encoder == Encoder.Nvenc264) { return new EncoderInfoVideo { Codec = Codec.H264, Name = "h264_nvenc", PixelFormats = new List() { PixFmt.Yuv420P, PixFmt.Yuv444P }, QualityLevels = ParseUtils.GetEnumStrings(), QualityDefault = (int)Quality.Common.VeryHigh, HwAccelerated = true, }; } if (encoder == Encoder.Nvenc265) { return new EncoderInfoVideo { Codec = Codec.H265, Name = "hevc_nvenc", PixelFormats = new List() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le }, QualityLevels = ParseUtils.GetEnumStrings(), QualityDefault = (int)Quality.Common.VeryHigh, HwAccelerated = true, }; } if (encoder == Encoder.NvencAv1) { return new EncoderInfoVideo { Codec = Codec.AV1, Name = "av1_nvenc", PixelFormats = new List() { PixFmt.Yuv420P, PixFmt.Yuv444P, PixFmt.Yuv420P10Le }, QualityLevels = ParseUtils.GetEnumStrings(), 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.Yuv420P }, QualityLevels = ParseUtils.GetEnumStrings(), 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.Yuv420P }, QualityLevels = ParseUtils.GetEnumStrings(), 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.Yuv420P }, QualityLevels = ParseUtils.GetEnumStrings(), 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.Yuv420P }, QualityLevels = ParseUtils.GetEnumStrings(), 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.Yuv422P10Le, PixFmt.Yuv444P10Le, PixFmt.Yuva444P10Le }, PixelFormatDefault = PixFmt.Yuv422P10Le, QualityLevels = ParseUtils.GetEnumStrings(), QualityDefault = (int)Quality.ProResProfile.Standard, }; } if (encoder == Encoder.Gif) { return new EncoderInfoVideo { Codec = Codec.Gif, PixelFormats = new List() { PixFmt.Pal8 }, PixelFormatDefault = PixFmt.Pal8, QualityLevels = ParseUtils.GetEnumStrings(), 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.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.Yuv422P, PixFmt.Rgb24, PixFmt.Rgba }, Lossless = true, }; } if (encoder == Encoder.Magicyuv) { return new EncoderInfoVideo { Codec = Codec.Magicyuv, PixelFormats = new List() { 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.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.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P }, QualityLevels = ParseUtils.GetEnumStrings(), 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.Yuv420P, PixFmt.Yuva420P, PixFmt.Rgba }, // Actually only supports BGRA not RGBA, but ffmpeg will auto-pick that QualityLevels = ParseUtils.GetEnumStrings(), QualityDefault = (int)Quality.JpegWebm.ImgHigh, IsImageSequence = true, OverideExtension = "webp", }; } if (encoder == Encoder.Tiff) { return new EncoderInfoVideo { Codec = Codec.Tiff, PixelFormats = new List() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P, PixFmt.Yuv444P16Le, PixFmt.Rgb24, PixFmt.Rgba, PixFmt.Rgb48Le, PixFmt.Rgba64Le }, PixelFormatDefault = PixFmt.Rgb24, Lossless = true, IsImageSequence = true, OverideExtension = "tiff", }; } return new EncoderInfoVideo(); } public static List GetSupportedCodecs(Enums.Output.Format format) { switch (format) { case Enums.Output.Format.Mp4: return new List { Codec.H264, Codec.H265, Codec.AV1 }; case Enums.Output.Format.Mkv: return new List { Codec.H264, Codec.H265, Codec.AV1, Codec.VP9 }; case Enums.Output.Format.Webm: return new List { Codec.VP9, Codec.AV1 }; case Enums.Output.Format.Mov: return new List { Codec.ProRes }; case Enums.Output.Format.Avi: return new List { Codec.Ffv1, Codec.Huffyuv, Codec.Magicyuv, Codec.Rawvideo }; case Enums.Output.Format.Gif: return new List { Codec.Gif }; case Enums.Output.Format.Images: return new List { Codec.Png, Codec.Jpeg, Codec.Webp, Codec.Tiff }; case Enums.Output.Format.Realtime: return new List { }; default: return new List { }; } } public static List GetAvailableEncoders(Enums.Output.Format format) { var allEncoders = Enum.GetValues(typeof(Encoder)).Cast(); 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 encoders, IEnumerable 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.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 Crfs = new Dictionary { { 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 ProresProfiles = new Dictionary { { 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 JpegQuality = new Dictionary { { Quality.JpegWebm.ImgMax, 1 }, { Quality.JpegWebm.ImgHigh, 3 }, { Quality.JpegWebm.ImgMed, 5 }, { Quality.JpegWebm.ImgLow, 11 }, { Quality.JpegWebm.ImgLowest, 31 }, }; public static Dictionary WebpQuality = new Dictionary { { 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(settings.Quality, true, Strings.VideoQuality); if (settings.Encoder == Encoder.Jpeg) return JpegQuality[qualityLevel]; if (settings.Encoder == Encoder.Webp) return WebpQuality[qualityLevel]; return -1; } } }