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;
}