add amf acceleration (#947)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user