adjust nvidia capabilities (#864)
* adjust nvidia capabilities logic * fallback to software encoding for 10-bit h264 * cleanup * more tweaks
This commit is contained in:
@@ -10,6 +10,7 @@ using ErsatzTV.Core.Interfaces.FFmpeg;
|
||||
using ErsatzTV.Core.Interfaces.Images;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Metadata;
|
||||
using ErsatzTV.FFmpeg;
|
||||
using ErsatzTV.FFmpeg.Capabilities;
|
||||
using ErsatzTV.FFmpeg.State;
|
||||
using FluentAssertions;
|
||||
@@ -18,6 +19,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Serilog;
|
||||
using MediaStream = ErsatzTV.Core.Domain.MediaStream;
|
||||
|
||||
namespace ErsatzTV.Core.Tests.FFmpeg;
|
||||
|
||||
@@ -303,9 +305,10 @@ public class TranscodingTests
|
||||
new FFmpegPlaybackSettingsCalculator(),
|
||||
new FakeStreamSelector(),
|
||||
new Mock<ITempFilePool>().Object,
|
||||
new HardwareCapabilitiesFactory(
|
||||
new MemoryCache(new MemoryCacheOptions()),
|
||||
LoggerFactory.CreateLogger<HardwareCapabilitiesFactory>()),
|
||||
new FakeNvidiaCapabilitiesFactory(),
|
||||
// new HardwareCapabilitiesFactory(
|
||||
// new MemoryCache(new MemoryCacheOptions()),
|
||||
// LoggerFactory.CreateLogger<HardwareCapabilitiesFactory>()),
|
||||
LoggerFactory.CreateLogger<FFmpegLibraryProcessService>());
|
||||
|
||||
var v = new MediaVersion
|
||||
@@ -577,6 +580,14 @@ public class TranscodingTests
|
||||
subtitles.HeadOrNone().AsTask();
|
||||
}
|
||||
|
||||
private class FakeNvidiaCapabilitiesFactory : IHardwareCapabilitiesFactory
|
||||
{
|
||||
public Task<IHardwareCapabilities> GetHardwareCapabilities(
|
||||
string ffmpegPath,
|
||||
HardwareAccelerationMode hardwareAccelerationMode) =>
|
||||
Task.FromResult<IHardwareCapabilities>(new NvidiaHardwareCapabilities(61));
|
||||
}
|
||||
|
||||
private static string ExecutableName(string baseName) =>
|
||||
OperatingSystem.IsWindows() ? $"{baseName}.exe" : baseName;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using ErsatzTV.FFmpeg.Format;
|
||||
|
||||
namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
|
||||
public class DefaultHardwareCapabilities : IHardwareCapabilities
|
||||
{
|
||||
public bool CanDecode(string videoFormat) => true;
|
||||
public bool CanEncode(string videoFormat) => true;
|
||||
public bool CanDecode(string videoFormat, Option<IPixelFormat> maybePixelFormat) => true;
|
||||
public bool CanEncode(string videoFormat, Option<IPixelFormat> maybePixelFormat) => true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using ErsatzTV.FFmpeg.Format;
|
||||
|
||||
namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
|
||||
public interface IHardwareCapabilities
|
||||
{
|
||||
public bool CanDecode(string videoFormat);
|
||||
public bool CanEncode(string videoFormat);
|
||||
public bool CanDecode(string videoFormat, Option<IPixelFormat> maybePixelFormat);
|
||||
public bool CanEncode(string videoFormat, Option<IPixelFormat> maybePixelFormat);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using ErsatzTV.FFmpeg.Format;
|
||||
|
||||
namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
|
||||
public class NoHardwareCapabilities : IHardwareCapabilities
|
||||
{
|
||||
public bool CanDecode(string videoFormat) => false;
|
||||
public bool CanEncode(string videoFormat) => false;
|
||||
public bool CanDecode(string videoFormat, Option<IPixelFormat> maybePixelFormat) => false;
|
||||
public bool CanEncode(string videoFormat, Option<IPixelFormat> maybePixelFormat) => false;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Numerics;
|
||||
using ErsatzTV.FFmpeg.Format;
|
||||
|
||||
namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
@@ -8,19 +9,41 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities
|
||||
|
||||
public NvidiaHardwareCapabilities(int architecture) => _architecture = architecture;
|
||||
|
||||
public bool CanDecode(string videoFormat) =>
|
||||
videoFormat switch
|
||||
public bool CanDecode(string videoFormat, Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
// pascal is required to decode hevc/vp9
|
||||
VideoFormat.Hevc or VideoFormat.Vp9 => _architecture >= 60,
|
||||
_ => true
|
||||
};
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
public bool CanEncode(string videoFormat) =>
|
||||
videoFormat switch
|
||||
return videoFormat switch
|
||||
{
|
||||
// pascal is required to encode hevc
|
||||
VideoFormat.Hevc => _architecture >= 60,
|
||||
// second gen maxwell is required to decode hevc
|
||||
VideoFormat.Hevc => _architecture >= 52,
|
||||
|
||||
// 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,
|
||||
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
|
||||
public bool CanEncode(string videoFormat, Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
return videoFormat switch
|
||||
{
|
||||
// pascal is required to encode 10-bit hevc
|
||||
VideoFormat.Hevc when bitDepth == 10 => _architecture >= 60,
|
||||
|
||||
// second gen maxwell is required to encode hevc
|
||||
VideoFormat.Hevc => _architecture >= 52,
|
||||
|
||||
// nvidia cannot encode 10-bit h264
|
||||
VideoFormat.H264 when bitDepth == 10 => false,
|
||||
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ public static class AvailableDecoders
|
||||
currentState.PixelFormat.Match(pf => pf.Name, () => string.Empty)) switch
|
||||
{
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.Hevc, _)
|
||||
when hardwareCapabilities.CanDecode(VideoFormat.Hevc) => new DecoderHevcCuvid(ffmpegState),
|
||||
when hardwareCapabilities.CanDecode(VideoFormat.Hevc, currentState.PixelFormat) =>
|
||||
new DecoderHevcCuvid(ffmpegState),
|
||||
|
||||
// nvenc doesn't support hardware decoding of 10-bit content
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.H264, PixelFormat.YUV420P10LE or PixelFormat.YUV444P10LE)
|
||||
@@ -31,13 +32,15 @@ public static class AvailableDecoders
|
||||
new DecoderMpeg2Video(),
|
||||
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.H264, _)
|
||||
when hardwareCapabilities.CanDecode(VideoFormat.H264) => new DecoderH264Cuvid(ffmpegState),
|
||||
when hardwareCapabilities.CanDecode(VideoFormat.H264, currentState.PixelFormat) =>
|
||||
new DecoderH264Cuvid(ffmpegState),
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.Mpeg2Video, _) => new DecoderMpeg2Cuvid(
|
||||
ffmpegState,
|
||||
desiredState.Deinterlaced),
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.Vc1, _) => new DecoderVc1Cuvid(ffmpegState),
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.Vp9, _)
|
||||
when hardwareCapabilities.CanDecode(VideoFormat.Vp9) => new DecoderVp9Cuvid(ffmpegState),
|
||||
when hardwareCapabilities.CanDecode(VideoFormat.Vp9, currentState.PixelFormat) =>
|
||||
new DecoderVp9Cuvid(ffmpegState),
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.Mpeg4, _) => new DecoderMpeg4Cuvid(ffmpegState),
|
||||
|
||||
// hevc_qsv decoder sometimes causes green lines with 10-bit content
|
||||
|
||||
@@ -21,43 +21,57 @@ public static class AvailableEncoders
|
||||
ILogger logger) =>
|
||||
(ffmpegState.EncoderHardwareAccelerationMode, desiredState.VideoFormat) switch
|
||||
{
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(VideoFormat.Hevc) =>
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.Hevc,
|
||||
desiredState.PixelFormat) =>
|
||||
new EncoderHevcNvenc(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.H264) when hardwareCapabilities.CanEncode(VideoFormat.H264) =>
|
||||
(HardwareAccelerationMode.Nvenc, VideoFormat.H264) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.H264,
|
||||
desiredState.PixelFormat) =>
|
||||
new EncoderH264Nvenc(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
|
||||
(HardwareAccelerationMode.Qsv, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(VideoFormat.Hevc) =>
|
||||
(HardwareAccelerationMode.Qsv, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.Hevc,
|
||||
desiredState.PixelFormat) =>
|
||||
new EncoderHevcQsv(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
(HardwareAccelerationMode.Qsv, VideoFormat.H264) when hardwareCapabilities.CanEncode(VideoFormat.H264) =>
|
||||
(HardwareAccelerationMode.Qsv, VideoFormat.H264) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.H264,
|
||||
desiredState.PixelFormat) =>
|
||||
new EncoderH264Qsv(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
|
||||
(HardwareAccelerationMode.Vaapi, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(VideoFormat.Hevc) =>
|
||||
(HardwareAccelerationMode.Vaapi, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.Hevc,
|
||||
desiredState.PixelFormat) =>
|
||||
new EncoderHevcVaapi(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
(HardwareAccelerationMode.Vaapi, VideoFormat.H264) when hardwareCapabilities.CanEncode(VideoFormat.H264) =>
|
||||
(HardwareAccelerationMode.Vaapi, VideoFormat.H264) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.H264,
|
||||
desiredState.PixelFormat) =>
|
||||
new EncoderH264Vaapi(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
|
||||
(HardwareAccelerationMode.VideoToolbox, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.Hevc) => new EncoderHevcVideoToolbox(),
|
||||
VideoFormat.Hevc,
|
||||
desiredState.PixelFormat) => new EncoderHevcVideoToolbox(),
|
||||
(HardwareAccelerationMode.VideoToolbox, VideoFormat.H264) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.H264) => new EncoderH264VideoToolbox(),
|
||||
VideoFormat.H264,
|
||||
desiredState.PixelFormat) => new EncoderH264VideoToolbox(),
|
||||
|
||||
(_, VideoFormat.Hevc) => new EncoderLibx265(currentState),
|
||||
(_, VideoFormat.H264) => new EncoderLibx264(),
|
||||
|
||||
@@ -4,4 +4,5 @@ public interface IPixelFormat
|
||||
{
|
||||
string Name { get; }
|
||||
string FFmpegName { get; }
|
||||
int BitDepth { get; }
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ public class PixelFormatNv12 : IPixelFormat
|
||||
public string Name { get; }
|
||||
|
||||
public string FFmpegName => "nv12";
|
||||
public int BitDepth => 8;
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ public class PixelFormatUnknown : IPixelFormat
|
||||
{
|
||||
public string Name => "unknown";
|
||||
public string FFmpegName => "unknown";
|
||||
public int BitDepth => 8;
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ public class PixelFormatYuv420P : IPixelFormat
|
||||
{
|
||||
public string Name => PixelFormat.YUV420P;
|
||||
public string FFmpegName => FFmpegFormat.YUV420P;
|
||||
public int BitDepth => 8;
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ public class PixelFormatYuv420P10Le : IPixelFormat
|
||||
{
|
||||
public string Name => PixelFormat.YUV420P10LE;
|
||||
public string FFmpegName => FFmpegFormat.P010LE;
|
||||
public int BitDepth => 10;
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ public class PixelFormatYuv444P : IPixelFormat
|
||||
{
|
||||
public string Name => PixelFormat.YUV444P;
|
||||
public string FFmpegName => FFmpegFormat.YUV444P;
|
||||
public int BitDepth => 8;
|
||||
}
|
||||
|
||||
@@ -6,4 +6,5 @@ public class PixelFormatYuvJ420P : IPixelFormat
|
||||
|
||||
// always convert this to yuv420p in filter chains
|
||||
public string FFmpegName => FFmpegFormat.YUV420P;
|
||||
public int BitDepth => 8;
|
||||
}
|
||||
|
||||
@@ -220,8 +220,10 @@ public class PipelineBuilder
|
||||
|
||||
foreach (IPipelineStep accel in maybeAccel)
|
||||
{
|
||||
bool canDecode = _hardwareCapabilities.CanDecode(currentState.VideoFormat);
|
||||
bool canEncode = _hardwareCapabilities.CanEncode(desiredState.VideoFormat);
|
||||
bool canDecode = _hardwareCapabilities.CanDecode(currentState.VideoFormat, videoStream.PixelFormat);
|
||||
bool canEncode = _hardwareCapabilities.CanEncode(
|
||||
desiredState.VideoFormat,
|
||||
desiredState.PixelFormat);
|
||||
|
||||
// disable hw accel if decoder/encoder isn't supported
|
||||
if (!canDecode || !canEncode)
|
||||
|
||||
Reference in New Issue
Block a user