From 5b9601a57bf807295a560fa2a633d166614b6f7e Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Tue, 17 Jun 2025 21:59:10 -0500 Subject: [PATCH] maintain cuda pixel format throughout nvidia pipeline (#2042) --- .../Decoder/Cuvid/DecoderImplicitCuda.cs | 27 ++++++++++++++----- ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs | 9 +++++++ ErsatzTV.FFmpeg/Filter/CropFilter.cs | 8 ++++++ .../Filter/Cuda/CudaHardwareDownloadFilter.cs | 15 ++++++++--- .../Filter/HardwareDownloadFilter.cs | 8 ++++++ ErsatzTV.FFmpeg/Filter/PadFilter.cs | 8 ++++++ ErsatzTV.FFmpeg/Format/PixelFormatCuda.cs | 10 +++++++ ErsatzTV.FFmpeg/Format/PixelFormatVaapi.cs | 12 +++------ .../Core/FFmpeg/TranscodingTests.cs | 6 ++--- 9 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 ErsatzTV.FFmpeg/Format/PixelFormatCuda.cs diff --git a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderImplicitCuda.cs b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderImplicitCuda.cs index 17a7a340..dcbba225 100644 --- a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderImplicitCuda.cs +++ b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderImplicitCuda.cs @@ -1,14 +1,27 @@ -namespace ErsatzTV.FFmpeg.Decoder.Cuvid; +using ErsatzTV.FFmpeg.Format; + +namespace ErsatzTV.FFmpeg.Decoder.Cuvid; public class DecoderImplicitCuda : DecoderBase { protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; - public override string Name => string.Empty; + + public override string Name => "implicit_cuda"; public override string[] InputOptions(InputFile inputFile) => - new[] - { - "-hwaccel_output_format", - "cuda" - }; + [ + "-hwaccel_output_format", + "cuda" + ]; + + public override FrameState NextState(FrameState currentState) + { + FrameState nextState = base.NextState(currentState); + + return currentState.PixelFormat.Match( + pixelFormat => pixelFormat.BitDepth == 8 + ? nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) } + : nextState with { PixelFormat = new PixelFormatCuda(pixelFormat.Name, 10) }, + () => nextState); + } } diff --git a/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs b/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs index 76325408..c6aa250a 100644 --- a/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs @@ -42,6 +42,15 @@ public class ColorspaceFilter : BaseFilter } } + // cuda is not a target software format + if (pixelFormat is PixelFormatCuda cuda) + { + foreach (IPixelFormat pf in AvailablePixelFormats.ForPixelFormat(cuda.Name, null)) + { + name = pf.FFmpegName; + } + } + if (!string.IsNullOrWhiteSpace(name)) { hwdownload = $"hwdownload,format={name},"; diff --git a/ErsatzTV.FFmpeg/Filter/CropFilter.cs b/ErsatzTV.FFmpeg/Filter/CropFilter.cs index 953e9a99..c2562e10 100644 --- a/ErsatzTV.FFmpeg/Filter/CropFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/CropFilter.cs @@ -31,6 +31,14 @@ public class CropFilter : BaseFilter } } + if (pixelFormat is PixelFormatCuda) + { + foreach (IPixelFormat pf in AvailablePixelFormats.ForPixelFormat(pixelFormat.Name, null)) + { + return $"hwdownload,format={pf.FFmpegName},{crop}"; + } + } + return $"hwdownload,format={pixelFormat.FFmpegName},{crop}"; } diff --git a/ErsatzTV.FFmpeg/Filter/Cuda/CudaHardwareDownloadFilter.cs b/ErsatzTV.FFmpeg/Filter/Cuda/CudaHardwareDownloadFilter.cs index 03104c64..32173144 100644 --- a/ErsatzTV.FFmpeg/Filter/Cuda/CudaHardwareDownloadFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/Cuda/CudaHardwareDownloadFilter.cs @@ -22,11 +22,20 @@ public class CudaHardwareDownloadFilter : BaseFilter var hwdownload = "hwdownload"; foreach (IPixelFormat pixelFormat in _maybeCurrentPixelFormat) { - if (!string.IsNullOrWhiteSpace(pixelFormat.FFmpegName)) + IPixelFormat currentPixelFormat = pixelFormat; + if (pixelFormat is PixelFormatCuda cuda) { - hwdownload += $",format={pixelFormat.FFmpegName}"; + foreach (IPixelFormat pf in AvailablePixelFormats.ForPixelFormat(cuda.Name, null)) + { + currentPixelFormat = pf; + } + } + + if (!string.IsNullOrWhiteSpace(currentPixelFormat.FFmpegName)) + { + hwdownload += $",format={currentPixelFormat.FFmpegName}"; - if (pixelFormat is PixelFormatNv12 nv12) + if (currentPixelFormat is PixelFormatNv12 nv12) { if (_maybeTargetPixelFormat.IsNone) { diff --git a/ErsatzTV.FFmpeg/Filter/HardwareDownloadFilter.cs b/ErsatzTV.FFmpeg/Filter/HardwareDownloadFilter.cs index 844b4aaf..c7fde484 100644 --- a/ErsatzTV.FFmpeg/Filter/HardwareDownloadFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/HardwareDownloadFilter.cs @@ -26,6 +26,14 @@ public class HardwareDownloadFilter : BaseFilter } } + if (pixelFormat is PixelFormatCuda) + { + foreach (IPixelFormat pf in AvailablePixelFormats.ForPixelFormat(pixelFormat.Name, null)) + { + return $"hwdownload,format={pf.FFmpegName}"; + } + } + if (!string.IsNullOrWhiteSpace(pixelFormat.FFmpegName)) { hwdownload = $"hwdownload,format={pixelFormat.FFmpegName}"; diff --git a/ErsatzTV.FFmpeg/Filter/PadFilter.cs b/ErsatzTV.FFmpeg/Filter/PadFilter.cs index 3804118a..72908952 100644 --- a/ErsatzTV.FFmpeg/Filter/PadFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/PadFilter.cs @@ -31,6 +31,14 @@ public class PadFilter : BaseFilter } } + if (pixelFormat is PixelFormatCuda) + { + foreach (IPixelFormat pf in AvailablePixelFormats.ForPixelFormat(pixelFormat.Name, null)) + { + return $"hwdownload,format={pf.FFmpegName},{pad}"; + } + } + return $"hwdownload,format={pixelFormat.FFmpegName},{pad}"; } diff --git a/ErsatzTV.FFmpeg/Format/PixelFormatCuda.cs b/ErsatzTV.FFmpeg/Format/PixelFormatCuda.cs new file mode 100644 index 00000000..4b6a3bc7 --- /dev/null +++ b/ErsatzTV.FFmpeg/Format/PixelFormatCuda.cs @@ -0,0 +1,10 @@ +namespace ErsatzTV.FFmpeg.Format; + +public class PixelFormatCuda(string name, int bitDepth = 8) : IPixelFormat +{ + public string Name { get; } = name; + + public string FFmpegName => "cuda"; + + public int BitDepth { get; } = bitDepth; +} diff --git a/ErsatzTV.FFmpeg/Format/PixelFormatVaapi.cs b/ErsatzTV.FFmpeg/Format/PixelFormatVaapi.cs index 9da3cd22..03b4bb25 100644 --- a/ErsatzTV.FFmpeg/Format/PixelFormatVaapi.cs +++ b/ErsatzTV.FFmpeg/Format/PixelFormatVaapi.cs @@ -1,16 +1,10 @@ namespace ErsatzTV.FFmpeg.Format; -public class PixelFormatVaapi : IPixelFormat +public class PixelFormatVaapi(string name, int bitDepth = 8) : IPixelFormat { - public PixelFormatVaapi(string name, int bitDepth = 8) - { - Name = name; - BitDepth = bitDepth; - } - - public string Name { get; } + public string Name { get; } = name; public string FFmpegName => "vaapi"; - public int BitDepth { get; } + public int BitDepth { get; } = bitDepth; } diff --git a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs index 5d896b54..050b58fa 100644 --- a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs @@ -158,7 +158,7 @@ public class TranscodingTests // // // // // new("mpeg2video", "yuv420p"), // // - new InputFormat("libx265", "yuv420p"), + //new InputFormat("libx265", "yuv420p"), new InputFormat("libx265", "yuv420p10le") // // new("mpeg4", "yuv420p"), @@ -197,8 +197,8 @@ public class TranscodingTests public static HardwareAccelerationKind[] TestAccelerations = [ - HardwareAccelerationKind.None, - //HardwareAccelerationKind.Nvenc, + //HardwareAccelerationKind.None, + HardwareAccelerationKind.Nvenc, //HardwareAccelerationKind.Vaapi //HardwareAccelerationKind.Qsv, // HardwareAccelerationKind.VideoToolbox,