using System.Collections.Concurrent; using System.Runtime.InteropServices; using ErsatzTV.FFmpeg.Capabilities.VideoToolbox; using ErsatzTV.FFmpeg.Format; using Microsoft.Extensions.Logging; namespace ErsatzTV.FFmpeg.Capabilities; public class VideoToolboxHardwareCapabilities : IHardwareCapabilities { private static readonly ConcurrentDictionary Encoders = new (); private readonly IFFmpegCapabilities _ffmpegCapabilities; private readonly ILogger _logger; public VideoToolboxHardwareCapabilities(IFFmpegCapabilities ffmpegCapabilities, ILogger logger) { _ffmpegCapabilities = ffmpegCapabilities; _logger = logger; } public FFmpegCapability CanDecode(string videoFormat, Option videoProfile, Option maybePixelFormat, bool isHdr) { int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8); return (videoFormat, bitDepth) switch { // 10-bit h264 decoding is likely not support by any hardware (VideoFormat.H264, 10) => FFmpegCapability.Software, _ => FFmpegCapability.Hardware }; } public FFmpegCapability CanEncode(string videoFormat, Option videoProfile, Option maybePixelFormat) { if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && Encoders.IsEmpty) { var encoderList = VideoToolboxUtil.GetAvailableEncoders(); _logger.LogDebug("VideoToolbox reports {Count} encoders", encoderList.Count); // we only really care about h264 and hevc hardware encoders foreach (var encoder in encoderList) { if (encoder.Contains("HEVC (HW)", StringComparison.OrdinalIgnoreCase)) { Encoders.AddOrUpdate(VideoFormat.Hevc, true, (_, _) => true); } if (encoder.Contains("H.264 (HW)", StringComparison.OrdinalIgnoreCase)) { Encoders.AddOrUpdate(VideoFormat.H264, true, (_, _) => true); } } } int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8); return (videoFormat, bitDepth) switch { // 10-bit h264 encoding is not support by any hardware (VideoFormat.H264, 10) => FFmpegCapability.Software, (VideoFormat.H264, 8) => _ffmpegCapabilities.HasEncoder(FFmpegKnownEncoder.H264VideoToolbox) && Encoders.ContainsKey(videoFormat) ? FFmpegCapability.Hardware : FFmpegCapability.Software, (VideoFormat.Hevc, _) => _ffmpegCapabilities.HasEncoder(FFmpegKnownEncoder.HevcVideoToolbox) && Encoders.ContainsKey(videoFormat) ? FFmpegCapability.Hardware : FFmpegCapability.Software, _ => FFmpegCapability.Software }; } public Option GetRateControlMode(string videoFormat, Option maybePixelFormat) => Option.None; }