using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using ADOFAIRenderer.Data; namespace ADOFAIRenderer.Util.FFmpeg; /// /// Family of a video encoder. Selects the encoder-specific configuration in /// (input pixel format, hardware device setup, quality option names, speed preset). /// [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum EncoderKind { /// CPU encoder library linked into FFmpeg (libx264, libx265, libvpx-vp9, libsvtav1, libaom-av1, libvvenc). Software, /// NVIDIA NVENC hardware encoder (*_nvenc). NVENC, /// AMD Advanced Media Framework hardware encoder (*_amf). AMF, /// Intel Quick Sync Video hardware encoder (*_qsv), driven through a QSV hardware device context. QSV, /// Apple VideoToolbox hardware encoder (*_videotoolbox, macOS). VideoToolbox } /// /// Known FFmpeg video encoder: FFmpeg encoder name, output codec, GPU vendor and encoder family. /// /// Instances exist only as the static fields below (the constructor is private); compare them with == or /// , which compare case-insensitively. /// lists every encoder, and its order is the fallback order used by /// GPUUtil.GetPrioritizedEncoders after the encoders of the active GPU vendor. /// /// An entry does not mean that the encoder exists in the loaded FFmpeg build or works on the current machine: /// FFmpegNativeEncoder.Start looks each one up with avcodec_find_encoder_by_name and opens it, and skips the /// encoders that are missing or fail to open. /// Reference: https://ffmpeg.org/ffmpeg-codecs.html (Video Encoders) /// [SuppressMessage("ReSharper", "InconsistentNaming")] internal sealed class VideoEncoder : IEquatable { // ReSharper disable MemberCanBePrivate.Global // Each field: FFmpeg encoder name, output codec, GPU vendor whose hardware runs it // (Unknown for CPU encoders), encoder family. // ── H.264 ── /// NVIDIA NVENC hardware encoder for H.264 (FFmpeg encoder "h264_nvenc"). public static readonly VideoEncoder H264_NVENC = new("h264_nvenc", Codec.H264, GPUVendor.NVIDIA, EncoderKind.NVENC); /// Intel Quick Sync Video hardware encoder for H.264 (FFmpeg encoder "h264_qsv"). public static readonly VideoEncoder H264_QSV = new("h264_qsv", Codec.H264, GPUVendor.Intel, EncoderKind.QSV); /// AMD AMF hardware encoder for H.264 (FFmpeg encoder "h264_amf"). public static readonly VideoEncoder H264_AMF = new("h264_amf", Codec.H264, GPUVendor.AMD, EncoderKind.AMF); /// Apple VideoToolbox hardware encoder for H.264 (FFmpeg encoder "h264_videotoolbox"). public static readonly VideoEncoder H264_VideoToolbox = new("h264_videotoolbox", Codec.H264, GPUVendor.Apple, EncoderKind.VideoToolbox); /// CPU software encoder for H.264 (FFmpeg encoder "libx264"). public static readonly VideoEncoder Libx264 = new("libx264", Codec.H264, GPUVendor.Unknown, EncoderKind.Software); // ── H.265 (HEVC) ── /// NVIDIA NVENC hardware encoder for H.265 (HEVC) (FFmpeg encoder "hevc_nvenc"). public static readonly VideoEncoder HEVC_NVENC = new("hevc_nvenc", Codec.H265, GPUVendor.NVIDIA, EncoderKind.NVENC); /// Intel Quick Sync Video hardware encoder for H.265 (HEVC) (FFmpeg encoder "hevc_qsv"). public static readonly VideoEncoder HEVC_QSV = new("hevc_qsv", Codec.H265, GPUVendor.Intel, EncoderKind.QSV); /// AMD AMF hardware encoder for H.265 (HEVC) (FFmpeg encoder "hevc_amf"). public static readonly VideoEncoder HEVC_AMF = new("hevc_amf", Codec.H265, GPUVendor.AMD, EncoderKind.AMF); /// Apple VideoToolbox hardware encoder for H.265 (HEVC) (FFmpeg encoder "hevc_videotoolbox"). public static readonly VideoEncoder HEVC_VideoToolbox = new("hevc_videotoolbox", Codec.H265, GPUVendor.Apple, EncoderKind.VideoToolbox); /// CPU software encoder for H.265 (HEVC) (FFmpeg encoder "libx265"). public static readonly VideoEncoder Libx265 = new("libx265", Codec.H265, GPUVendor.Unknown, EncoderKind.Software); // ── VP9 ── // libavcodec has no "vp9_nvenc" encoder (NVENC does not encode VP9), so avcodec_find_encoder_by_name returns null // for this entry and FFmpegNativeEncoder.Start skips it with a "not found" warning. /// NVIDIA NVENC entry for VP9 (FFmpeg encoder name "vp9_nvenc"); never found in libavcodec, see above. public static readonly VideoEncoder VP9_NVENC = new("vp9_nvenc", Codec.VP9, GPUVendor.NVIDIA, EncoderKind.NVENC); /// Intel Quick Sync Video hardware encoder for VP9 (FFmpeg encoder "vp9_qsv"). public static readonly VideoEncoder VP9_QSV = new("vp9_qsv", Codec.VP9, GPUVendor.Intel, EncoderKind.QSV); /// CPU software encoder for VP9 (FFmpeg encoder "libvpx-vp9"). public static readonly VideoEncoder LibvpxVP9 = new("libvpx-vp9", Codec.VP9, GPUVendor.Unknown, EncoderKind.Software); // ── AV1 ── /// NVIDIA NVENC hardware encoder for AV1 (FFmpeg encoder "av1_nvenc"). public static readonly VideoEncoder AV1_NVENC = new("av1_nvenc", Codec.AV1, GPUVendor.NVIDIA, EncoderKind.NVENC); /// Intel Quick Sync Video hardware encoder for AV1 (FFmpeg encoder "av1_qsv"). public static readonly VideoEncoder AV1_QSV = new("av1_qsv", Codec.AV1, GPUVendor.Intel, EncoderKind.QSV); /// AMD AMF hardware encoder for AV1 (FFmpeg encoder "av1_amf"). public static readonly VideoEncoder AV1_AMF = new("av1_amf", Codec.AV1, GPUVendor.AMD, EncoderKind.AMF); /// CPU software encoder for AV1 (FFmpeg encoder "libsvtav1"). public static readonly VideoEncoder LibSvtAv1 = new("libsvtav1", Codec.AV1, GPUVendor.Unknown, EncoderKind.Software); /// CPU software encoder for AV1 (FFmpeg encoder "libaom-av1"). public static readonly VideoEncoder LibAomAv1 = new("libaom-av1", Codec.AV1, GPUVendor.Unknown, EncoderKind.Software); // ── VVC ── /// CPU software encoder for H.266 (VVC) (FFmpeg encoder "libvvenc"). public static readonly VideoEncoder LibVvenc = new("libvvenc", Codec.VVC, GPUVendor.Unknown, EncoderKind.Software); // ReSharper restore MemberCanBePrivate.Global /// /// Every known encoder, grouped by codec. Within a codec the order is NVENC → QSV → AMF → VideoToolbox → software /// (for the families the codec has); GPUUtil.GetPrioritizedEncoders keeps this order for the encoders that do not /// belong to the active GPU vendor. /// public static readonly IReadOnlyList All = new[] { H264_NVENC, H264_QSV, H264_AMF, H264_VideoToolbox, Libx264, HEVC_NVENC, HEVC_QSV, HEVC_AMF, HEVC_VideoToolbox, Libx265, VP9_NVENC, VP9_QSV, LibvpxVP9, AV1_NVENC, AV1_QSV, AV1_AMF, LibSvtAv1, LibAomAv1, LibVvenc }; /// Lookup table of by (case-insensitive), used by . private static readonly Dictionary ByName = All.ToDictionary(e => e.Name, StringComparer.OrdinalIgnoreCase); /// FFmpeg encoder name passed to avcodec_find_encoder_by_name (e.g. "h264_nvenc", "libx264"). public string Name { get; } /// Video codec of the bitstream the encoder produces. public Codec Codec { get; } /// GPU vendor whose hardware runs the encoder. for CPU (software) encoders. public GPUVendor Vendor { get; } /// Encoder family (software or hardware API). public EncoderKind Kind { get; } /// True for every family except . public bool IsHardware => Kind != EncoderKind.Software; /// Creates an entry. Used only by the static fields of this class. private VideoEncoder(string name, Codec codec, GPUVendor vendor, EncoderKind kind) { Name = name; Codec = codec; Vendor = vendor; Kind = kind; } /// /// Returns the known encoder with the given FFmpeg name (case-insensitive), /// or null when is null, empty or not in . /// public static VideoEncoder? FromName(string name) { return string.IsNullOrEmpty(name) ? null : ByName.GetValueOrDefault(name); } /// True when is the same entry or has the same (case-insensitive). public bool Equals(VideoEncoder other) { if (ReferenceEquals(null, other)) return false; return ReferenceEquals(this, other) || string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase); } /// True when is a equal to this one (see ). public override bool Equals(object? obj) { return ReferenceEquals(this, obj) || obj is VideoEncoder other && Equals(other); } /// Case-insensitive hash of , consistent with . public override int GetHashCode() { return StringComparer.OrdinalIgnoreCase.GetHashCode(Name); } /// Value equality through ; two null operands are equal. public static bool operator ==(VideoEncoder left, VideoEncoder right) { return Equals(left, right); } /// Negation of the == operator. public static bool operator !=(VideoEncoder left, VideoEncoder right) { return !Equals(left, right); } /// Returns the FFmpeg encoder name (). public override string ToString() => Name; }