From 74d6b328283a8dee185e0ccaa37e8634c338418e Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Mon, 21 Jul 2025 17:23:30 +0000 Subject: [PATCH] change how qsv is initialized on windows (#2183) --- .../Environment/CudaVisibleDevicesVariable.cs | 11 +++++++ .../QsvHardwareAccelerationOption.cs | 4 +-- .../InputOption/StreamSeekInputOption.cs | 2 +- .../Pipeline/QsvPipelineBuilder.cs | 32 +++++++++++-------- 4 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs diff --git a/ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs b/ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs new file mode 100644 index 00000000..07c13e6e --- /dev/null +++ b/ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs @@ -0,0 +1,11 @@ +namespace ErsatzTV.FFmpeg.Environment; + +public class CudaVisibleDevicesVariable(string visibleDevices) : IPipelineStep +{ + public EnvironmentVariable[] EnvironmentVariables => [ new("CUDA_VISIBLE_DEVICES", visibleDevices) ]; + public string[] GlobalOptions => []; + public string[] InputOptions(InputFile inputFile) => []; + public string[] FilterOptions => []; + public string[] OutputOptions => []; + public FrameState NextState(FrameState currentState) => currentState; +} diff --git a/ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOption.cs b/ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOption.cs index 984f2895..9754b9cd 100644 --- a/ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOption.cs +++ b/ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/QsvHardwareAccelerationOption.cs @@ -16,9 +16,7 @@ public class QsvHardwareAccelerationOption(Option device, FFmpegCapabili { get { - string[] initDevices = OperatingSystem.IsWindows() - ? new[] { "-init_hw_device", "d3d11va=hw:,vendor=0x8086", "-filter_hw_device", "hw" } - : new[] { "-init_hw_device", "qsv=hw", "-filter_hw_device", "hw" }; + string[] initDevices = ["-init_hw_device", "qsv=hw", "-filter_hw_device", "hw"]; var result = new List { diff --git a/ErsatzTV.FFmpeg/InputOption/StreamSeekInputOption.cs b/ErsatzTV.FFmpeg/InputOption/StreamSeekInputOption.cs index 8ff33319..2d7d20e2 100644 --- a/ErsatzTV.FFmpeg/InputOption/StreamSeekInputOption.cs +++ b/ErsatzTV.FFmpeg/InputOption/StreamSeekInputOption.cs @@ -15,7 +15,7 @@ public class StreamSeekInputOption : IInputOption public string[] OutputOptions => Array.Empty(); public FrameState NextState(FrameState currentState) => currentState; - public bool AppliesTo(AudioInputFile audioInputFile) => true; + public bool AppliesTo(AudioInputFile audioInputFile) => audioInputFile is not NullAudioInputFile; // don't seek into a still image public bool AppliesTo(VideoInputFile videoInputFile) => videoInputFile.VideoStreams.All(s => !s.StillImage); diff --git a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs index 93a5671b..5141bc8c 100644 --- a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs @@ -3,6 +3,7 @@ using ErsatzTV.FFmpeg.Decoder; using ErsatzTV.FFmpeg.Decoder.Qsv; using ErsatzTV.FFmpeg.Encoder; using ErsatzTV.FFmpeg.Encoder.Qsv; +using ErsatzTV.FFmpeg.Environment; using ErsatzTV.FFmpeg.Filter; using ErsatzTV.FFmpeg.Filter.Qsv; using ErsatzTV.FFmpeg.Format; @@ -75,7 +76,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder } bool isHevcOrH264 = videoStream.Codec is /*VideoFormat.Hevc or*/ VideoFormat.H264; - bool is10Bit = videoStream.BitDepth == 10; + bool is10Bit = videoStream.PixelFormat.Map(pf => pf.BitDepth).IfNone(8) == 10; // 10-bit hevc/h264 qsv decoders have issues, so use software if (decodeCapability == FFmpegCapability.Hardware && isHevcOrH264 && is10Bit) @@ -89,6 +90,9 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder decodeCapability = FFmpegCapability.Software; } + // give a bogus value so no cuda devices are visible to ffmpeg + pipelineSteps.Add(new CudaVisibleDevicesVariable("999")); + pipelineSteps.Add(new QsvHardwareAccelerationOption(ffmpegState.VaapiDevice, decodeCapability)); // disable hw accel if decoder/encoder isn't supported @@ -279,8 +283,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder IPixelFormat formatForDownload = pixelFormat; bool usesVppQsv = - videoInputFile.FilterSteps.Any(f => - f is QsvFormatFilter or ScaleQsvFilter or DeinterlaceQsvFilter or TonemapQsvFilter); + videoInputFile.FilterSteps.Any(f => f is QsvFormatFilter or ScaleQsvFilter or DeinterlaceQsvFilter or TonemapQsvFilter); // if we have no filters, check whether we need to convert pixel format // since qsv doesn't seem to like doing that at the encoder @@ -336,15 +339,18 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder // force p010/nv12 if we're still in hardware if (currentState.FrameDataLocation == FrameDataLocation.Hardware) { - if (currentState.BitDepth is 10 && formatForDownload is not PixelFormatYuv420P10Le) - { - formatForDownload = new PixelFormatYuv420P10Le(); - currentState = currentState with { PixelFormat = Some(formatForDownload) }; - } - else if (currentState.BitDepth is 8 && formatForDownload is not PixelFormatNv12) + foreach (int bitDepth in currentState.PixelFormat.Map(pf => pf.BitDepth)) { - formatForDownload = new PixelFormatNv12(formatForDownload.Name); - currentState = currentState with { PixelFormat = Some(formatForDownload) }; + if (bitDepth is 10 && formatForDownload is not PixelFormatYuv420P10Le) + { + formatForDownload = new PixelFormatYuv420P10Le(); + currentState = currentState with { PixelFormat = Some(formatForDownload) }; + } + else if (bitDepth is 8 && formatForDownload is not PixelFormatNv12) + { + formatForDownload = new PixelFormatNv12(formatForDownload.Name); + currentState = currentState with { PixelFormat = Some(formatForDownload) }; + } } } @@ -586,8 +592,8 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder // auto_scale filter seems to muck up 10-bit software decode => hardware scale, so use software scale in that case useSoftwareFilter = useSoftwareFilter || - ffmpegState is { DecoderHardwareAccelerationMode: HardwareAccelerationMode.None } && - OperatingSystem.IsWindows() && currentState.BitDepth == 10; + (ffmpegState is { DecoderHardwareAccelerationMode: HardwareAccelerationMode.None } && + OperatingSystem.IsWindows() && currentState.BitDepth == 10); if (currentState.ScaledSize != desiredState.ScaledSize && useSoftwareFilter) {