diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ac14c75e..680b1bd0e 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 and VAAPI acceleration +- Add AV1 encoding support with NVIDIA, VAAPI and QSV acceleration - This also requires `HLS Segmenter (fmp4)` ### Fixed diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs index 6f3c6cb86..f65887adf 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs @@ -57,7 +57,8 @@ public class ? FFmpegProfileBitDepth.EightBit : update.BitDepth; - if (p.HardwareAcceleration is not HardwareAccelerationKind.Nvenc && + if (p.HardwareAcceleration is not (HardwareAccelerationKind.Nvenc or HardwareAccelerationKind.Vaapi + or HardwareAccelerationKind.Qsv) && p.VideoFormat is FFmpegProfileVideoFormat.Av1) { p.VideoFormat = FFmpegProfileVideoFormat.Hevc; diff --git a/ErsatzTV.FFmpeg/Encoder/Qsv/EncoderAv1Qsv.cs b/ErsatzTV.FFmpeg/Encoder/Qsv/EncoderAv1Qsv.cs new file mode 100644 index 000000000..51d929843 --- /dev/null +++ b/ErsatzTV.FFmpeg/Encoder/Qsv/EncoderAv1Qsv.cs @@ -0,0 +1,17 @@ +using ErsatzTV.FFmpeg.Format; + +namespace ErsatzTV.FFmpeg.Encoder.Qsv; + +public class EncoderAv1Qsv : EncoderBase +{ + public override string Name => "av1_qsv"; + public override StreamKind Kind => StreamKind.Video; + + public override string[] OutputOptions => ["-c:v", Name, "-low_power", "0", "-look_ahead", "0"]; + + public override FrameState NextState(FrameState currentState) => currentState with + { + VideoFormat = VideoFormat.Av1, + FrameDataLocation = FrameDataLocation.Hardware + }; +} diff --git a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs index b417e9ce3..5c4d19c86 100644 --- a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs @@ -321,8 +321,6 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder 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"), // don't pass NVENC profile down to libx264 (_, _) => GetSoftwareEncoder( diff --git a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs index b0f93dbc6..67eda2cd5 100644 --- a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs +++ b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs @@ -630,6 +630,8 @@ public abstract class PipelineBuilderBase : IPipelineBuilder VideoFormat.H264 => new EncoderLibx264(desiredState.VideoProfile, desiredState.VideoPreset), VideoFormat.Mpeg2Video => new EncoderMpeg2Video(), + VideoFormat.Av1 => throw new NotSupportedException("AV1 software encoding is not supported"), + VideoFormat.Copy => new EncoderCopyVideo(), VideoFormat.Undetermined => new EncoderImplicitVideo(), diff --git a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs index adf7bd624..8721099de 100644 --- a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs @@ -77,6 +77,11 @@ public class QsvPipelineBuilder : 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)"); + } + bool isHevcOrH264 = videoStream.Codec is /*VideoFormat.Hevc or*/ VideoFormat.H264; bool is10Bit = videoStream.PixelFormat.Map(pf => pf.BitDepth).IfNone(8) == 10; @@ -229,6 +234,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder Option maybeEncoder = (ffmpegState.EncoderHardwareAccelerationMode, desiredState.VideoFormat) switch { + (HardwareAccelerationMode.Qsv, VideoFormat.Av1) => new EncoderAv1Qsv(), (HardwareAccelerationMode.Qsv, VideoFormat.Hevc) => new EncoderHevcQsv(desiredState.VideoPreset), (HardwareAccelerationMode.Qsv, VideoFormat.H264) => new EncoderH264Qsv( desiredState.VideoProfile, diff --git a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs index 597e11b8f..a27997bff 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs @@ -256,9 +256,6 @@ 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 ded785a39..9ae03b51f 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 or HardwareAccelerationKind.Vaapi)) + @if (_model.HardwareAcceleration is (HardwareAccelerationKind.Nvenc or HardwareAccelerationKind.Vaapi or HardwareAccelerationKind.Qsv)) { av1 } diff --git a/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs b/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs index 988f4f405..91db8c668 100644 --- a/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs +++ b/ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs @@ -12,7 +12,8 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator NvencFormats =