Browse Source

add amf acceleration (#947)

pull/951/head
Jason Dove 4 years ago committed by GitHub
parent
commit
3972e3603b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 9
      ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs
  3. 3
      ErsatzTV.Core/Domain/HardwareAccelerationKind.cs
  4. 2
      ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs
  5. 1
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  6. 15
      ErsatzTV.FFmpeg/Encoder/Amf/EncoderH264Amf.cs
  7. 15
      ErsatzTV.FFmpeg/Encoder/Amf/EncoderHevcAmf.cs
  8. 8
      ErsatzTV.FFmpeg/Encoder/AvailableEncoders.cs
  9. 10
      ErsatzTV.FFmpeg/Filter/ComplexFilter.cs
  10. 3
      ErsatzTV.FFmpeg/Filter/SubtitleHardwareUploadFilter.cs
  11. 3
      ErsatzTV.FFmpeg/Filter/WatermarkHardwareUploadFilter.cs
  12. 3
      ErsatzTV.FFmpeg/HardwareAccelerationMode.cs
  13. 11
      ErsatzTV.FFmpeg/Option/HardwareAcceleration/AmfHardwareAccelerationOption.cs
  14. 1
      ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs
  15. 24
      ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs
  16. 14
      ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs

1
CHANGELOG.md

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- When a title is specified, audio streams that contain that title (case-insensitive search) will be prioritized - When a title is specified, audio streams that contain that title (case-insensitive search) will be prioritized
- This can be helpful for creating channels that use commentary tracks - This can be helpful for creating channels that use commentary tracks
- External tooling exists to easily update title/name metadata if your audio streams don't already have this metadata - External tooling exists to easily update title/name metadata if your audio streams don't already have this metadata
- Add `Amf` hardware acceleration option for AMD GPUs on Windows
## [0.6.6-beta] - 2022-08-17 ## [0.6.6-beta] - 2022-08-17
### Fixed ### Fixed

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

@ -168,6 +168,11 @@ public class TranscodingTests
HardwareAccelerationKind.VideoToolbox HardwareAccelerationKind.VideoToolbox
}; };
public static HardwareAccelerationKind[] AmfAcceleration =
{
HardwareAccelerationKind.Amf
};
public static HardwareAccelerationKind[] QsvAcceleration = public static HardwareAccelerationKind[] QsvAcceleration =
{ {
HardwareAccelerationKind.Qsv HardwareAccelerationKind.Qsv
@ -192,11 +197,11 @@ public class TranscodingTests
[ValueSource(typeof(TestData), nameof(TestData.VideoFormats))] [ValueSource(typeof(TestData), nameof(TestData.VideoFormats))]
FFmpegProfileVideoFormat profileVideoFormat, FFmpegProfileVideoFormat profileVideoFormat,
// [ValueSource(typeof(TestData), nameof(TestData.NoAcceleration))] HardwareAccelerationKind profileAcceleration) // [ValueSource(typeof(TestData), nameof(TestData.NoAcceleration))] HardwareAccelerationKind profileAcceleration)
[ValueSource(typeof(TestData), nameof(TestData.NvidiaAcceleration))] // [ValueSource(typeof(TestData), nameof(TestData.NvidiaAcceleration))] HardwareAccelerationKind profileAcceleration)
HardwareAccelerationKind profileAcceleration)
// [ValueSource(typeof(TestData), nameof(TestData.VaapiAcceleration))] HardwareAccelerationKind profileAcceleration) // [ValueSource(typeof(TestData), nameof(TestData.VaapiAcceleration))] HardwareAccelerationKind profileAcceleration)
// [ValueSource(typeof(TestData), nameof(TestData.QsvAcceleration))] HardwareAccelerationKind profileAcceleration) // [ValueSource(typeof(TestData), nameof(TestData.QsvAcceleration))] HardwareAccelerationKind profileAcceleration)
// [ValueSource(typeof(TestData), nameof(TestData.VideoToolboxAcceleration))] HardwareAccelerationKind profileAcceleration) // [ValueSource(typeof(TestData), nameof(TestData.VideoToolboxAcceleration))] HardwareAccelerationKind profileAcceleration)
[ValueSource(typeof(TestData), nameof(TestData.AmfAcceleration))] HardwareAccelerationKind profileAcceleration)
{ {
if (inputFormat.Encoder is "mpeg1video" or "msmpeg4v2" or "msmpeg4v3") if (inputFormat.Encoder is "mpeg1video" or "msmpeg4v2" or "msmpeg4v3")
{ {

3
ErsatzTV.Core/Domain/HardwareAccelerationKind.cs

@ -6,5 +6,6 @@ public enum HardwareAccelerationKind
Qsv = 1, Qsv = 1,
Nvenc = 2, Nvenc = 2,
Vaapi = 3, Vaapi = 3,
VideoToolbox = 4 VideoToolbox = 4,
Amf = 5
} }

2
ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs

@ -169,6 +169,7 @@ public class FFmpegComplexFilterBuilder
_videoDecoder.Contains("cuvid")), _videoDecoder.Contains("cuvid")),
HardwareAccelerationKind.Qsv => !isSong, HardwareAccelerationKind.Qsv => !isSong,
HardwareAccelerationKind.VideoToolbox => false, HardwareAccelerationKind.VideoToolbox => false,
HardwareAccelerationKind.Amf => false,
_ => false _ => false
}; };
@ -200,6 +201,7 @@ public class FFmpegComplexFilterBuilder
bool usesHardwareFilters = acceleration != HardwareAccelerationKind.None && bool usesHardwareFilters = acceleration != HardwareAccelerationKind.None &&
acceleration != HardwareAccelerationKind.VideoToolbox && acceleration != HardwareAccelerationKind.VideoToolbox &&
acceleration != HardwareAccelerationKind.Amf &&
!isHardwareDecode && !isHardwareDecode &&
(_deinterlace || _scaleToSize.IsSome); (_deinterlace || _scaleToSize.IsSome);

1
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -627,6 +627,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
HardwareAccelerationKind.Qsv => HardwareAccelerationMode.Qsv, HardwareAccelerationKind.Qsv => HardwareAccelerationMode.Qsv,
HardwareAccelerationKind.Vaapi => HardwareAccelerationMode.Vaapi, HardwareAccelerationKind.Vaapi => HardwareAccelerationMode.Vaapi,
HardwareAccelerationKind.VideoToolbox => HardwareAccelerationMode.VideoToolbox, HardwareAccelerationKind.VideoToolbox => HardwareAccelerationMode.VideoToolbox,
HardwareAccelerationKind.Amf => HardwareAccelerationMode.Amf,
_ => HardwareAccelerationMode.None _ => HardwareAccelerationMode.None
}; };
} }

15
ErsatzTV.FFmpeg/Encoder/Amf/EncoderH264Amf.cs

@ -0,0 +1,15 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Encoder.Amf;
public class EncoderH264Amf : EncoderBase
{
public override string Name => "h264_amf";
public override StreamKind Kind => StreamKind.Video;
public override FrameState NextState(FrameState currentState) => currentState with
{
VideoFormat = VideoFormat.H264,
FrameDataLocation = FrameDataLocation.Hardware
};
}

15
ErsatzTV.FFmpeg/Encoder/Amf/EncoderHevcAmf.cs

@ -0,0 +1,15 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Encoder.Amf;
public class EncoderHevcAmf : EncoderBase
{
public override string Name => "hevc_amf";
public override StreamKind Kind => StreamKind.Video;
public override FrameState NextState(FrameState currentState) => currentState with
{
VideoFormat = VideoFormat.Hevc,
FrameDataLocation = FrameDataLocation.Hardware
};
}

8
ErsatzTV.FFmpeg/Encoder/AvailableEncoders.cs

@ -1,4 +1,5 @@
using ErsatzTV.FFmpeg.Capabilities; using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Encoder.Amf;
using ErsatzTV.FFmpeg.Encoder.Nvenc; using ErsatzTV.FFmpeg.Encoder.Nvenc;
using ErsatzTV.FFmpeg.Encoder.Qsv; using ErsatzTV.FFmpeg.Encoder.Qsv;
using ErsatzTV.FFmpeg.Encoder.Vaapi; using ErsatzTV.FFmpeg.Encoder.Vaapi;
@ -72,6 +73,13 @@ public static class AvailableEncoders
(HardwareAccelerationMode.VideoToolbox, VideoFormat.H264) when hardwareCapabilities.CanEncode( (HardwareAccelerationMode.VideoToolbox, VideoFormat.H264) when hardwareCapabilities.CanEncode(
VideoFormat.H264, VideoFormat.H264,
desiredState.PixelFormat) => new EncoderH264VideoToolbox(), desiredState.PixelFormat) => new EncoderH264VideoToolbox(),
(HardwareAccelerationMode.Amf, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(
VideoFormat.Hevc,
desiredState.PixelFormat) => new EncoderHevcAmf(),
(HardwareAccelerationMode.Amf, VideoFormat.H264) when hardwareCapabilities.CanEncode(
VideoFormat.H264,
desiredState.PixelFormat) => new EncoderH264Amf(),
(_, VideoFormat.Hevc) => new EncoderLibx265(currentState), (_, VideoFormat.Hevc) => new EncoderLibx265(currentState),
(_, VideoFormat.H264) => new EncoderLibx264(), (_, VideoFormat.H264) => new EncoderLibx264(),

10
ErsatzTV.FFmpeg/Filter/ComplexFilter.cs

@ -173,7 +173,8 @@ public class ComplexFilter : IPipelineStep
if (_maybeSubtitleInputFile.Map(s => !s.IsImageBased).IfNone(false) && if (_maybeSubtitleInputFile.Map(s => !s.IsImageBased).IfNone(false) &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.Vaapi && _ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.Vaapi &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.VideoToolbox) _ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.VideoToolbox &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.Amf)
{ {
uploadDownloadFilter = new HardwareDownloadFilter(_currentState).Filter; uploadDownloadFilter = new HardwareDownloadFilter(_currentState).Filter;
} }
@ -230,10 +231,9 @@ public class ComplexFilter : IPipelineStep
if (filter != string.Empty) if (filter != string.Empty)
{ {
string tempVideoLabel = string.IsNullOrWhiteSpace(videoFilterComplex) && string tempVideoLabel = videoLabel.StartsWith('[') && videoLabel.EndsWith(']')
string.IsNullOrWhiteSpace(watermarkFilterComplex) ? videoLabel
? $"[{videoLabel}]" : $"[{videoLabel}]";
: videoLabel;
// vaapi uses software overlay and needs to upload // vaapi uses software overlay and needs to upload
// videotoolbox seems to require a hwupload for hevc // videotoolbox seems to require a hwupload for hevc

3
ErsatzTV.FFmpeg/Filter/SubtitleHardwareUploadFilter.cs

@ -25,6 +25,9 @@ public class SubtitleHardwareUploadFilter : BaseFilter
// leave videotoolbox in software since we use a software overlay filter // leave videotoolbox in software since we use a software overlay filter
HardwareAccelerationMode.VideoToolbox => string.Empty, HardwareAccelerationMode.VideoToolbox => string.Empty,
// leave amf in software since we use a software overlay filter
HardwareAccelerationMode.Amf => string.Empty,
_ => "hwupload" _ => "hwupload"
}; };

3
ErsatzTV.FFmpeg/Filter/WatermarkHardwareUploadFilter.cs

@ -24,6 +24,9 @@ public class WatermarkHardwareUploadFilter : BaseFilter
// leave videotoolbox in software since we use a software overlay filter // leave videotoolbox in software since we use a software overlay filter
HardwareAccelerationMode.VideoToolbox => string.Empty, HardwareAccelerationMode.VideoToolbox => string.Empty,
// leave amf in software since we use a software overlay filter
HardwareAccelerationMode.Amf => string.Empty,
_ => "hwupload" _ => "hwupload"
}; };

3
ErsatzTV.FFmpeg/HardwareAccelerationMode.cs

@ -6,5 +6,6 @@ public enum HardwareAccelerationMode
Qsv = 1, Qsv = 1,
Nvenc = 2, Nvenc = 2,
Vaapi = 3, Vaapi = 3,
VideoToolbox = 4 VideoToolbox = 4,
Amf = 5
} }

11
ErsatzTV.FFmpeg/Option/HardwareAcceleration/AmfHardwareAccelerationOption.cs

@ -0,0 +1,11 @@
namespace ErsatzTV.FFmpeg.Option.HardwareAcceleration;
public class AmfHardwareAccelerationOption : GlobalOption
{
public override IList<string> GlobalOptions => new List<string> { "-hwaccel", "dxva2" };
public override FrameState NextState(FrameState currentState) => currentState with
{
FrameDataLocation = FrameDataLocation.Software
};
}

1
ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs

@ -14,6 +14,7 @@ public static class AvailableHardwareAccelerationOptions
HardwareAccelerationMode.Qsv => new QsvHardwareAccelerationOption(), HardwareAccelerationMode.Qsv => new QsvHardwareAccelerationOption(),
HardwareAccelerationMode.Vaapi => GetVaapiAcceleration(vaapiDevice, logger), HardwareAccelerationMode.Vaapi => GetVaapiAcceleration(vaapiDevice, logger),
HardwareAccelerationMode.VideoToolbox => new VideoToolboxHardwareAccelerationOption(), HardwareAccelerationMode.VideoToolbox => new VideoToolboxHardwareAccelerationOption(),
HardwareAccelerationMode.Amf => new AmfHardwareAccelerationOption(),
HardwareAccelerationMode.None => Option<IPipelineStep>.None, HardwareAccelerationMode.None => Option<IPipelineStep>.None,
_ => LogUnknownMode(mode, logger) _ => LogUnknownMode(mode, logger)
}; };

24
ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs

@ -1,8 +1,10 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Health; using ErsatzTV.Core.Health;
using ErsatzTV.Core.Health.Checks; using ErsatzTV.Core.Health.Checks;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Runtime;
using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Data;
using LanguageExt.UnsafeValueAccess; using LanguageExt.UnsafeValueAccess;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -12,14 +14,17 @@ namespace ErsatzTV.Infrastructure.Health.Checks;
public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAccelerationHealthCheck public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAccelerationHealthCheck
{ {
private readonly IConfigElementRepository _configElementRepository; private readonly IConfigElementRepository _configElementRepository;
private readonly IRuntimeInfo _runtimeInfo;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
public HardwareAccelerationHealthCheck( public HardwareAccelerationHealthCheck(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IConfigElementRepository configElementRepository) IConfigElementRepository configElementRepository,
IRuntimeInfo runtimeInfo)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_configElementRepository = configElementRepository; _configElementRepository = configElementRepository;
_runtimeInfo = runtimeInfo;
} }
public override string Title => "Hardware Acceleration"; public override string Title => "Hardware Acceleration";
@ -91,7 +96,7 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler
return None; return None;
} }
private static async Task<List<HardwareAccelerationKind>> GetSupportedAccelerationKinds( private async Task<List<HardwareAccelerationKind>> GetSupportedAccelerationKinds(
string ffmpegPath, string ffmpegPath,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
@ -120,6 +125,21 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler
} }
} }
if (_runtimeInfo.IsOSPlatform(OSPlatform.Windows))
{
string output2 = await GetProcessOutput(
ffmpegPath,
new[] { "-v", "quiet", "-encoders" },
cancellationToken);
foreach (string method in output2.Split("\n").Map(s => s.Trim()))
{
if (method.Contains("_amf "))
{
result.Add(HardwareAccelerationKind.Amf);
}
}
}
return result.ToList(); return result.ToList();
} }
} }

14
ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs

@ -32,6 +32,12 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfi
FFmpegProfileVideoFormat.Hevc FFmpegProfileVideoFormat.Hevc
}; };
private static readonly List<FFmpegProfileVideoFormat> AmfFormats = new()
{
FFmpegProfileVideoFormat.H264,
FFmpegProfileVideoFormat.Hevc
};
public FFmpegProfileEditViewModelValidator() public FFmpegProfileEditViewModelValidator()
{ {
RuleFor(x => x.Name).NotEmpty(); RuleFor(x => x.Name).NotEmpty();
@ -76,5 +82,13 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfi
RuleFor(x => x.VideoFormat).Must(c => VideoToolboxFormats.Contains(c)) RuleFor(x => x.VideoFormat).Must(c => VideoToolboxFormats.Contains(c))
.WithMessage("VideoToolbox supports formats (h264, hevc)"); .WithMessage("VideoToolbox supports formats (h264, hevc)");
}); });
When(
x => x.HardwareAcceleration == HardwareAccelerationKind.Amf,
() =>
{
RuleFor(x => x.VideoFormat).Must(c => AmfFormats.Contains(c))
.WithMessage("Amf supports formats (h264, hevc)");
});
} }
} }

Loading…
Cancel
Save