Browse Source

adjust nvidia capabilities (#864)

* adjust nvidia capabilities logic

* fallback to software encoding for 10-bit h264

* cleanup

* more tweaks
pull/865/head
Jason Dove 3 years ago committed by GitHub
parent
commit
3204da8e43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs
  2. 6
      ErsatzTV.FFmpeg/Capabilities/DefaultHardwareCapabilities.cs
  3. 6
      ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilities.cs
  4. 6
      ErsatzTV.FFmpeg/Capabilities/NoHardwareCapabilities.cs
  5. 39
      ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs
  6. 9
      ErsatzTV.FFmpeg/Decoder/AvailableDecoders.cs
  7. 30
      ErsatzTV.FFmpeg/Encoder/AvailableEncoders.cs
  8. 1
      ErsatzTV.FFmpeg/Format/IPixelFormat.cs
  9. 1
      ErsatzTV.FFmpeg/Format/PixelFormatNv12.cs
  10. 1
      ErsatzTV.FFmpeg/Format/PixelFormatUnknown.cs
  11. 1
      ErsatzTV.FFmpeg/Format/PixelFormatYuv420P.cs
  12. 1
      ErsatzTV.FFmpeg/Format/PixelFormatYuv420P10Le.cs
  13. 1
      ErsatzTV.FFmpeg/Format/PixelFormatYuv444P.cs
  14. 1
      ErsatzTV.FFmpeg/Format/PixelFormatYuvJ420P.cs
  15. 6
      ErsatzTV.FFmpeg/PipelineBuilder.cs

17
ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs

@ -10,6 +10,7 @@ using ErsatzTV.Core.Interfaces.FFmpeg; @@ -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; @@ -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 @@ -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 @@ -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;
}

6
ErsatzTV.FFmpeg/Capabilities/DefaultHardwareCapabilities.cs

@ -1,7 +1,9 @@ @@ -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;
}

6
ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilities.cs

@ -1,7 +1,9 @@ @@ -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);
}

6
ErsatzTV.FFmpeg/Capabilities/NoHardwareCapabilities.cs

@ -1,7 +1,9 @@ @@ -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;
}

39
ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
using System.Numerics;
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Capabilities;
@ -8,19 +9,41 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities @@ -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)
{
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
return videoFormat switch
{
// pascal is required to decode hevc/vp9
VideoFormat.Hevc or VideoFormat.Vp9 => _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) =>
videoFormat switch
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 hevc
VideoFormat.Hevc => _architecture >= 60,
// 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
};
}
}

9
ErsatzTV.FFmpeg/Decoder/AvailableDecoders.cs

@ -20,7 +20,8 @@ public static class AvailableDecoders @@ -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 @@ -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

30
ErsatzTV.FFmpeg/Encoder/AvailableEncoders.cs

@ -21,43 +21,57 @@ public static class AvailableEncoders @@ -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(),

1
ErsatzTV.FFmpeg/Format/IPixelFormat.cs

@ -4,4 +4,5 @@ public interface IPixelFormat @@ -4,4 +4,5 @@ public interface IPixelFormat
{
string Name { get; }
string FFmpegName { get; }
int BitDepth { get; }
}

1
ErsatzTV.FFmpeg/Format/PixelFormatNv12.cs

@ -7,4 +7,5 @@ public class PixelFormatNv12 : IPixelFormat @@ -7,4 +7,5 @@ public class PixelFormatNv12 : IPixelFormat
public string Name { get; }
public string FFmpegName => "nv12";
public int BitDepth => 8;
}

1
ErsatzTV.FFmpeg/Format/PixelFormatUnknown.cs

@ -4,4 +4,5 @@ public class PixelFormatUnknown : IPixelFormat @@ -4,4 +4,5 @@ public class PixelFormatUnknown : IPixelFormat
{
public string Name => "unknown";
public string FFmpegName => "unknown";
public int BitDepth => 8;
}

1
ErsatzTV.FFmpeg/Format/PixelFormatYuv420P.cs

@ -4,4 +4,5 @@ public class PixelFormatYuv420P : IPixelFormat @@ -4,4 +4,5 @@ public class PixelFormatYuv420P : IPixelFormat
{
public string Name => PixelFormat.YUV420P;
public string FFmpegName => FFmpegFormat.YUV420P;
public int BitDepth => 8;
}

1
ErsatzTV.FFmpeg/Format/PixelFormatYuv420P10Le.cs

@ -4,4 +4,5 @@ public class PixelFormatYuv420P10Le : IPixelFormat @@ -4,4 +4,5 @@ public class PixelFormatYuv420P10Le : IPixelFormat
{
public string Name => PixelFormat.YUV420P10LE;
public string FFmpegName => FFmpegFormat.P010LE;
public int BitDepth => 10;
}

1
ErsatzTV.FFmpeg/Format/PixelFormatYuv444P.cs

@ -4,4 +4,5 @@ public class PixelFormatYuv444P : IPixelFormat @@ -4,4 +4,5 @@ public class PixelFormatYuv444P : IPixelFormat
{
public string Name => PixelFormat.YUV444P;
public string FFmpegName => FFmpegFormat.YUV444P;
public int BitDepth => 8;
}

1
ErsatzTV.FFmpeg/Format/PixelFormatYuvJ420P.cs

@ -6,4 +6,5 @@ public class PixelFormatYuvJ420P : IPixelFormat @@ -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;
}

6
ErsatzTV.FFmpeg/PipelineBuilder.cs

@ -220,8 +220,10 @@ public class PipelineBuilder @@ -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)

Loading…
Cancel
Save