diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 9399679a4f..7dea5f8ebd 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -6610,6 +6610,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
|| string.Equals("yuv422p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
|| string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
+ var isAv1SupportedSwFormatsVt = is8_10bitSwFormatsVt || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
// The related patches make videotoolbox hardware surface working is only available in jellyfin-ffmpeg 7.0.1 at the moment.
bool useHwSurface = (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface) && IsVideoToolboxFullSupported();
@@ -6643,6 +6644,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return GetHwaccelType(state, options, "hevc", bitDepth, useHwSurface);
}
+
+ if (string.Equals("av1", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
+ && isAv1SupportedSwFormatsVt
+ && _mediaEncoder.IsVideoToolboxAv1DecodeAvailable)
+ {
+ return GetHwaccelType(state, options, "av1", bitDepth, useHwSurface);
+ }
}
return null;
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index c767b4a519..a60f523408 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -75,6 +75,12 @@ namespace MediaBrowser.Controller.MediaEncoding
/// true if the Vaapi device supports vulkan drm interop, false otherwise.
bool IsVaapiDeviceSupportVulkanDrmInterop { get; }
+ ///
+ /// Gets a value indicating whether av1 decoding is available via VideoToolbox.
+ ///
+ /// true if the av1 is available via VideoToolbox, false otherwise.
+ bool IsVideoToolboxAv1DecodeAvailable { get; }
+
///
/// Whether given encoder codec is supported.
///
diff --git a/MediaBrowser.MediaEncoding/Encoder/ApplePlatformHelper.cs b/MediaBrowser.MediaEncoding/Encoder/ApplePlatformHelper.cs
new file mode 100644
index 0000000000..ea2289bd7d
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Encoder/ApplePlatformHelper.cs
@@ -0,0 +1,85 @@
+#pragma warning disable CA1031
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.MediaEncoding.Encoder;
+
+///
+/// Helper class for Apple platform specific operations.
+///
+public static class ApplePlatformHelper
+{
+ private static readonly string[] _av1DecodeBlacklistedCpuClass = ["M1", "M2"];
+
+ private static string GetSysctlValue(string name)
+ {
+ IntPtr length = IntPtr.Zero;
+ // Get length of the value
+ int osStatus = SysctlByName(name, IntPtr.Zero, ref length, IntPtr.Zero, 0);
+
+ if (osStatus != 0)
+ {
+ throw new NotSupportedException($"Failed to get sysctl value for {name} with error {osStatus}");
+ }
+
+ IntPtr buffer = Marshal.AllocHGlobal(length.ToInt32());
+ try
+ {
+ osStatus = SysctlByName(name, buffer, ref length, IntPtr.Zero, 0);
+ if (osStatus != 0)
+ {
+ throw new NotSupportedException($"Failed to get sysctl value for {name} with error {osStatus}");
+ }
+
+ return Marshal.PtrToStringAnsi(buffer) ?? string.Empty;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(buffer);
+ }
+ }
+
+ private static int SysctlByName(string name, IntPtr oldp, ref IntPtr oldlenp, IntPtr newp, uint newlen)
+ {
+ return NativeMethods.SysctlByName(System.Text.Encoding.ASCII.GetBytes(name), oldp, ref oldlenp, newp, newlen);
+ }
+
+ ///
+ /// Check if the current system has hardware acceleration for AV1 decoding.
+ ///
+ /// The logger used for error logging.
+ /// Boolean indicates the hwaccel support.
+ public static bool HasAv1HardwareAccel(ILogger logger)
+ {
+ if (!RuntimeInformation.OSArchitecture.Equals(Architecture.Arm64))
+ {
+ return false;
+ }
+
+ try
+ {
+ string cpuBrandString = GetSysctlValue("machdep.cpu.brand_string");
+ return !_av1DecodeBlacklistedCpuClass.Any(blacklistedCpuClass => cpuBrandString.Contains(blacklistedCpuClass, StringComparison.OrdinalIgnoreCase));
+ }
+ catch (NotSupportedException e)
+ {
+ logger.LogError("Error getting CPU brand string: {Message}", e.Message);
+ }
+ catch (Exception e)
+ {
+ logger.LogError("Unknown error occured: {Exception}", e);
+ }
+
+ return false;
+ }
+
+ private static class NativeMethods
+ {
+ [DllImport("libc", EntryPoint = "sysctlbyname", SetLastError = true)]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
+ internal static extern int SysctlByName(byte[] name, IntPtr oldp, ref IntPtr oldlenp, IntPtr newp, uint newlen);
+ }
+}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 23d9ca7ef5..776b2ab42c 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -437,6 +437,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
+ public bool CheckIsVideoToolboxAv1DecodeAvailable()
+ {
+ return ApplePlatformHelper.HasAv1HardwareAccel(_logger);
+ }
+
private IEnumerable GetHwaccelTypes()
{
string? output = null;
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index a34238cd68..44b38f03bc 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -83,6 +83,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
private bool _isVaapiDeviceSupportVulkanDrmModifier = false;
private bool _isVaapiDeviceSupportVulkanDrmInterop = false;
+ private bool _isVideoToolboxAv1DecodeAvailable = false;
+
private static string[] _vulkanImageDrmFmtModifierExts =
{
"VK_EXT_image_drm_format_modifier",
@@ -153,6 +155,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
///
public bool IsVaapiDeviceSupportVulkanDrmInterop => _isVaapiDeviceSupportVulkanDrmInterop;
+ public bool IsVideoToolboxAv1DecodeAvailable => _isVideoToolboxAv1DecodeAvailable;
+
[GeneratedRegex(@"[^\/\\]+?(\.[^\/\\\n.]+)?$")]
private static partial Regex FfprobePathRegex();
@@ -255,6 +259,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.LogInformation("VAAPI device {RenderNodePath} supports Vulkan DRM interop", options.VaapiDevice);
}
}
+
+ // Check if VideoToolbox supports AV1 decode
+ if (OperatingSystem.IsMacOS() && SupportsHwaccel("videotoolbox"))
+ {
+ _isVideoToolboxAv1DecodeAvailable = validator.CheckIsVideoToolboxAv1DecodeAvailable();
+ }
}
_logger.LogInformation("FFmpeg: {FfmpegPath}", _ffmpegPath ?? string.Empty);