diff --git a/CHANGELOG.md b/CHANGELOG.md index ae6a8a765..7ac14c75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Allow HEVC playback in channel preview - This is restricted to compatible browsers - Preview button will be red when preview is disabled due to browser incompatibility -- Add AV1 encoding support with NVIDIA acceleration +- Add AV1 encoding support with NVIDIA and VAAPI acceleration - This also requires `HLS Segmenter (fmp4)` ### Fixed diff --git a/ErsatzTV.FFmpeg/Capabilities/VaapiHardwareCapabilities.cs b/ErsatzTV.FFmpeg/Capabilities/VaapiHardwareCapabilities.cs index 11f0f857b..8ee45feeb 100644 --- a/ErsatzTV.FFmpeg/Capabilities/VaapiHardwareCapabilities.cs +++ b/ErsatzTV.FFmpeg/Capabilities/VaapiHardwareCapabilities.cs @@ -193,6 +193,13 @@ public class VaapiHardwareCapabilities : IHardwareCapabilities VaapiEntrypoint: VaapiEntrypoint.Encode or VaapiEntrypoint.EncodeLowPower }), + VideoFormat.Av1 => + _profileEntrypoints.Any(e => e is + { + VaapiProfile: VaapiProfile.Av1Profile0, + VaapiEntrypoint: VaapiEntrypoint.Encode or VaapiEntrypoint.EncodeLowPower + }), + VideoFormat.Mpeg2Video => _profileEntrypoints.Any(e => e is { diff --git a/ErsatzTV.FFmpeg/Encoder/Vaapi/EncoderAv1Vaapi.cs b/ErsatzTV.FFmpeg/Encoder/Vaapi/EncoderAv1Vaapi.cs new file mode 100644 index 000000000..1d1b7d85d --- /dev/null +++ b/ErsatzTV.FFmpeg/Encoder/Vaapi/EncoderAv1Vaapi.cs @@ -0,0 +1,35 @@ +using ErsatzTV.FFmpeg.Format; + +namespace ErsatzTV.FFmpeg.Encoder.Vaapi; + +public class EncoderAv1Vaapi(RateControlMode rateControlMode) : EncoderBase +{ + public override string Name => "av1_vaapi"; + + public override StreamKind Kind => StreamKind.Video; + + public override string[] OutputOptions + { + get + { + var result = new List(base.OutputOptions); + + if (rateControlMode == RateControlMode.CQP) + { + result.Add("-rc_mode"); + result.Add("1"); + } + + result.Add("-sei"); + result.Add("-a53_cc"); + + return result.ToArray(); + } + } + + public override FrameState NextState(FrameState currentState) => currentState with + { + VideoFormat = VideoFormat.Av1 + // don't change the frame data location + }; +} diff --git a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs index 9e3319ca6..b417e9ce3 100644 --- a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs @@ -312,15 +312,23 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder (ffmpegState.EncoderHardwareAccelerationMode, desiredState.VideoFormat) switch { (HardwareAccelerationMode.Nvenc, VideoFormat.Hevc) => - new EncoderHevcNvenc(_hardwareCapabilities, desiredState.VideoPreset, desiredState.BitDepth, desiredState.AllowBFrames), + new EncoderHevcNvenc( + _hardwareCapabilities, + desiredState.VideoPreset, + desiredState.BitDepth, + desiredState.AllowBFrames), (HardwareAccelerationMode.Nvenc, VideoFormat.H264) => new EncoderH264Nvenc(desiredState.VideoProfile, desiredState.VideoPreset), (HardwareAccelerationMode.Nvenc, VideoFormat.Av1) => new EncoderAv1Nvenc(), - (HardwareAccelerationMode.None, VideoFormat.Av1) => throw new NotSupportedException("AV1 software encoding is not supported"), + (HardwareAccelerationMode.None, VideoFormat.Av1) => throw new NotSupportedException( + "AV1 software encoding is not supported"), // don't pass NVENC profile down to libx264 - (_, _) => GetSoftwareEncoder(ffmpegState, currentState, desiredState with { VideoProfile = Option.None }) + (_, _) => GetSoftwareEncoder( + ffmpegState, + currentState, + desiredState with { VideoProfile = Option.None }) }; foreach (IEncoder encoder in maybeEncoder) diff --git a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs index d9417fe6e..597e11b8f 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs @@ -80,6 +80,11 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder encodeCapability = FFmpegCapability.Software; } + if (desiredState.VideoFormat is VideoFormat.Av1 && ffmpegState.OutputFormat is not OutputFormatKind.HlsMp4) + { + throw new NotSupportedException("AV1 output is only supported with HLS Segmenter (fmp4)"); + } + foreach (string vaapiDevice in ffmpegState.VaapiDevice) { pipelineSteps.Add(new VaapiHardwareAccelerationOption(vaapiDevice, decodeCapability)); @@ -242,6 +247,8 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder Option maybeEncoder = (ffmpegState.EncoderHardwareAccelerationMode, desiredState.VideoFormat) switch { + (HardwareAccelerationMode.Vaapi, VideoFormat.Av1) => + new EncoderAv1Vaapi(rateControlMode), (HardwareAccelerationMode.Vaapi, VideoFormat.Hevc) => new EncoderHevcVaapi(rateControlMode), (HardwareAccelerationMode.Vaapi, VideoFormat.H264) => @@ -249,6 +256,9 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder (HardwareAccelerationMode.Vaapi, VideoFormat.Mpeg2Video) => new EncoderMpeg2Vaapi(rateControlMode), + (HardwareAccelerationMode.None, VideoFormat.Av1) => throw new NotSupportedException( + "AV1 software encoding is not supported"), + (_, _) => GetSoftwareEncoder(ffmpegState, currentState, desiredState) }; diff --git a/ErsatzTV/Pages/FFmpegEditor.razor b/ErsatzTV/Pages/FFmpegEditor.razor index 1dc2aaf5e..ded785a39 100644 --- a/ErsatzTV/Pages/FFmpegEditor.razor +++ b/ErsatzTV/Pages/FFmpegEditor.razor @@ -67,7 +67,7 @@ h264 hevc mpeg-2 - @if (_model.HardwareAcceleration is HardwareAccelerationKind.Nvenc) + @if (_model.HardwareAcceleration is (HardwareAccelerationKind.Nvenc or HardwareAccelerationKind.Vaapi)) { av1 } diff --git a/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs b/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs index 4a6582478..988f4f405 100644 --- a/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs +++ b/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs @@ -26,7 +26,8 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator VideoToolboxFormats = @@ -86,7 +87,7 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator { RuleFor(x => x.VideoFormat).Must(c => VaapiFormats.Contains(c)) - .WithMessage("VAAPI supports formats (h264, hevc, mpeg2video)"); + .WithMessage("VAAPI supports formats (h264, hevc, av1, mpeg2video)"); }); When(