diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c1760505..8f0ca7e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Fix subtitle stream selection when subtitle language is different than audio language - Fix bug with unsupported AAC channel layouts +- Fix NVIDIA second-gen maxwell capabilities detection ### Added - Add `640x480` resolution diff --git a/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs b/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs index dd768e1af..691b0bbab 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs @@ -585,7 +585,7 @@ public class TranscodingTests public Task GetHardwareCapabilities( string ffmpegPath, HardwareAccelerationMode hardwareAccelerationMode) => - Task.FromResult(new NvidiaHardwareCapabilities(61)); + Task.FromResult(new NvidiaHardwareCapabilities(61, string.Empty)); } private static string ExecutableName(string baseName) => diff --git a/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs b/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs index 50734d1d0..9107fe491 100644 --- a/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs +++ b/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs @@ -9,7 +9,8 @@ namespace ErsatzTV.FFmpeg.Capabilities; public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory { - private const string CacheKey = "ffmpeg.hardware.nvidia.architecture"; + private const string ArchitectureCacheKey = "ffmpeg.hardware.nvidia.architecture"; + private const string ModelCacheKey = "ffmpeg.hardware.nvidia.model"; private readonly ILogger _logger; private readonly IMemoryCache _memoryCache; @@ -31,9 +32,10 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory private async Task GetNvidiaCapabilities(string ffmpegPath) { - if (_memoryCache.TryGetValue(CacheKey, out int cachedArchitecture)) + if (_memoryCache.TryGetValue(ArchitectureCacheKey, out int cachedArchitecture) + && _memoryCache.TryGetValue(ModelCacheKey, out string cachedModel)) { - return new NvidiaHardwareCapabilities(cachedArchitecture); + return new NvidiaHardwareCapabilities(cachedArchitecture, cachedModel); } string[] arguments = @@ -57,13 +59,20 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory Option maybeLine = Optional(output.Split("\n").FirstOrDefault(x => x.Contains("GPU"))); foreach (string line in maybeLine) { - const string PATTERN = @"SM\s+(\d\.\d)"; - Match match = Regex.Match(line, PATTERN); + const string ARCHITECTURE_PATTERN = @"SM\s+(\d\.\d)"; + Match match = Regex.Match(line, ARCHITECTURE_PATTERN); if (match.Success && int.TryParse(match.Groups[1].Value.Replace(".", string.Empty), out int architecture)) { - _logger.LogInformation("Detected NVIDIA GPU architecture SM {Architecture}", architecture); - _memoryCache.Set(CacheKey, architecture); - return new NvidiaHardwareCapabilities(architecture); + const string MODEL_PATTERN = @"(GTX\s+[0-9a-zA-Z]+[\sTtIi]+)"; + Match modelMatch = Regex.Match(line, MODEL_PATTERN); + string model = modelMatch.Success ? modelMatch.Groups[1].Value.Trim() : "unknown"; + _logger.LogInformation( + "Detected NVIDIA GPU model {Model} architecture SM {Architecture}", + model, + architecture); + _memoryCache.Set(ArchitectureCacheKey, architecture); + _memoryCache.Set(ModelCacheKey, model); + return new NvidiaHardwareCapabilities(architecture, model); } } diff --git a/ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs b/ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs index b24393579..e81b57d46 100644 --- a/ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs +++ b/ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs @@ -5,8 +5,14 @@ namespace ErsatzTV.FFmpeg.Capabilities; public class NvidiaHardwareCapabilities : IHardwareCapabilities { private readonly int _architecture; + private readonly List _maxwellGm206 = new() { "GTX 750", "GTX 950", "GTX 960", "GTX 965M" }; + private readonly string _model; - public NvidiaHardwareCapabilities(int architecture) => _architecture = architecture; + public NvidiaHardwareCapabilities(int architecture, string model) + { + _architecture = architecture; + _model = model; + } public bool CanDecode(string videoFormat, Option maybePixelFormat) { @@ -14,14 +20,14 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities return videoFormat switch { - // second gen maxwell is required to decode hevc - VideoFormat.Hevc => _architecture >= 52, + // some second gen maxwell can decode hevc, otherwise pascal is required + VideoFormat.Hevc => _architecture == 52 && _maxwellGm206.Contains(_model) || _architecture >= 60, // pascal is required to decode vp9 10-bit VideoFormat.Vp9 when bitDepth == 10 => _architecture >= 60, - // second gen maxwell is required to decode vp9 - VideoFormat.Vp9 => _architecture >= 52, + // some second gen maxwell can decode vp9, otherwise pascal is required + VideoFormat.Vp9 => _architecture == 52 && _maxwellGm206.Contains(_model) || _architecture >= 60, _ => true };