Browse Source

fix case when cuda hw decode falls back to sw (#2718)

* fix case when cuda hw decode falls back to sw

* use a new filter
pull/2720/head
Jason Dove 8 months ago committed by GitHub
parent
commit
09858df654
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 9
      ErsatzTV.FFmpeg/Filter/Cuda/CudaSoftwareFallbackUploadFilter.cs
  3. 13
      ErsatzTV.FFmpeg/Filter/Cuda/HardwareUploadCudaFilter.cs
  4. 17
      ErsatzTV.FFmpeg/Filter/HardwareUploadCudaFilter.cs
  5. 2
      ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/CudaHardwareAccelerationOption.cs
  6. 24
      ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs

1
CHANGELOG.md

@ -53,6 +53,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Automatically kill playback troubleshooting ffmpeg process if it hasn't completed after two minutes - Automatically kill playback troubleshooting ffmpeg process if it hasn't completed after two minutes
- Fix playback of certain BT.2020 content - Fix playback of certain BT.2020 content
- Use playlist item count when using a playlist as filler (instead of a fixed count of 1 for each playlist item) - Use playlist item count when using a playlist as filler (instead of a fixed count of 1 for each playlist item)
- NVIDIA: fix stream failure with certain content that should decode in hardware but falls back to software
### Changed ### Changed
- No longer round framerate to nearest integer when normalizing framerate - No longer round framerate to nearest integer when normalizing framerate

9
ErsatzTV.FFmpeg/Filter/Cuda/CudaSoftwareFallbackUploadFilter.cs

@ -0,0 +1,9 @@
namespace ErsatzTV.FFmpeg.Filter.Cuda;
public class CudaSoftwareFallbackUploadFilter : BaseFilter
{
public override string Filter => "hwupload";
public override FrameState NextState(FrameState currentState) =>
currentState with { FrameDataLocation = FrameDataLocation.Hardware };
}

13
ErsatzTV.FFmpeg/Filter/Cuda/HardwareUploadCudaFilter.cs

@ -0,0 +1,13 @@
namespace ErsatzTV.FFmpeg.Filter.Cuda;
public class HardwareUploadCudaFilter(FrameDataLocation frameDataLocation) : BaseFilter
{
public override string Filter => frameDataLocation switch
{
FrameDataLocation.Hardware => string.Empty,
_ => "hwupload_cuda"
};
public override FrameState NextState(FrameState currentState) =>
currentState with { FrameDataLocation = FrameDataLocation.Hardware };
}

17
ErsatzTV.FFmpeg/Filter/HardwareUploadCudaFilter.cs

@ -1,17 +0,0 @@
namespace ErsatzTV.FFmpeg.Filter;
public class HardwareUploadCudaFilter : BaseFilter
{
private readonly FrameState _currentState;
public HardwareUploadCudaFilter(FrameState currentState) => _currentState = currentState;
public override string Filter => _currentState.FrameDataLocation switch
{
FrameDataLocation.Hardware => string.Empty,
_ => "hwupload_cuda"
};
public override FrameState NextState(FrameState currentState) =>
currentState with { FrameDataLocation = FrameDataLocation.Hardware };
}

2
ErsatzTV.FFmpeg/GlobalOption/HardwareAcceleration/CudaHardwareAccelerationOption.cs

@ -11,7 +11,7 @@ public class CudaHardwareAccelerationOption(bool isVulkanHdr) : GlobalOption
return ["-init_hw_device", "cuda=nv", "-init_hw_device", "vulkan=vk@nv", "-hwaccel", "vulkan"]; return ["-init_hw_device", "cuda=nv", "-init_hw_device", "vulkan=vk@nv", "-hwaccel", "vulkan"];
} }
return ["-hwaccel", "cuda"]; return ["-init_hw_device", "cuda", "-hwaccel", "cuda"];
} }
} }
} }

24
ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs

@ -134,6 +134,15 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
foreach (IDecoder decoder in maybeDecoder) foreach (IDecoder decoder in maybeDecoder)
{ {
videoInputFile.AddOption(decoder); videoInputFile.AddOption(decoder);
// sometimes cuda fails to decode in hardware and falls back to software
// in that case, we need to upload to get the frame in hardware as expected
// this *should* no-op when frames are already in hardware
if (ffmpegState.DecoderHardwareAccelerationMode is HardwareAccelerationMode.Nvenc)
{
videoInputFile.FilterSteps.Add(new CudaSoftwareFallbackUploadFilter());
}
return Some(decoder); return Some(decoder);
} }
@ -266,7 +275,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
&& !context.HasSubtitleText && !context.HasSubtitleText
&& (context.HasSubtitleOverlay || context.HasWatermark || context.HasGraphicsEngine)) && (context.HasSubtitleOverlay || context.HasWatermark || context.HasGraphicsEngine))
{ {
var hardwareUpload = new HardwareUploadCudaFilter(currentState); var hardwareUpload = new HardwareUploadCudaFilter(currentState.FrameDataLocation);
currentState = hardwareUpload.NextState(currentState); currentState = hardwareUpload.NextState(currentState);
videoInputFile.FilterSteps.Add(hardwareUpload); videoInputFile.FilterSteps.Add(hardwareUpload);
} }
@ -562,7 +571,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
else else
{ {
watermark.FilterSteps.Add( watermark.FilterSteps.Add(
new HardwareUploadCudaFilter(currentState with { FrameDataLocation = FrameDataLocation.Software })); new HardwareUploadCudaFilter(FrameDataLocation.Software));
var watermarkFilter = new OverlayWatermarkCudaFilter( var watermarkFilter = new OverlayWatermarkCudaFilter(
watermark.DesiredState, watermark.DesiredState,
@ -612,7 +621,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
if (context.HasWatermark || context.HasGraphicsEngine) if (context.HasWatermark || context.HasGraphicsEngine)
{ {
var subtitleHardwareUpload = new HardwareUploadCudaFilter(currentState); var subtitleHardwareUpload = new HardwareUploadCudaFilter(currentState.FrameDataLocation);
currentState = subtitleHardwareUpload.NextState(currentState); currentState = subtitleHardwareUpload.NextState(currentState);
videoInputFile.FilterSteps.Add(subtitleHardwareUpload); videoInputFile.FilterSteps.Add(subtitleHardwareUpload);
} }
@ -626,8 +635,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
{ {
if (_ffmpegCapabilities.HasFilter(FFmpegKnownFilter.ScaleNpp)) if (_ffmpegCapabilities.HasFilter(FFmpegKnownFilter.ScaleNpp))
{ {
var subtitleHardwareUpload = new HardwareUploadCudaFilter( var subtitleHardwareUpload = new HardwareUploadCudaFilter(FrameDataLocation.Software);
currentState with { FrameDataLocation = FrameDataLocation.Software });
subtitle.FilterSteps.Add(subtitleHardwareUpload); subtitle.FilterSteps.Add(subtitleHardwareUpload);
// only scale if scaling or padding was used for main video stream // only scale if scaling or padding was used for main video stream
@ -648,8 +656,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
subtitle.FilterSteps.Add(scaleFilter); subtitle.FilterSteps.Add(scaleFilter);
} }
var subtitleHardwareUpload = new HardwareUploadCudaFilter( var subtitleHardwareUpload = new HardwareUploadCudaFilter(FrameDataLocation.Software);
currentState with { FrameDataLocation = FrameDataLocation.Software });
subtitle.FilterSteps.Add(subtitleHardwareUpload); subtitle.FilterSteps.Add(subtitleHardwareUpload);
} }
@ -700,8 +707,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
{ {
graphicsEngine.FilterSteps.Add(new PixelFormatFilter(new PixelFormatYuva420P())); graphicsEngine.FilterSteps.Add(new PixelFormatFilter(new PixelFormatYuva420P()));
graphicsEngine.FilterSteps.Add( graphicsEngine.FilterSteps.Add(new HardwareUploadCudaFilter(FrameDataLocation.Software));
new HardwareUploadCudaFilter(currentState with { FrameDataLocation = FrameDataLocation.Software }));
var graphicsEngineFilter = new OverlayGraphicsEngineCudaFilter(); var graphicsEngineFilter = new OverlayGraphicsEngineCudaFilter();
graphicsEngineOverlayFilterSteps.Add(graphicsEngineFilter); graphicsEngineOverlayFilterSteps.Add(graphicsEngineFilter);

Loading…
Cancel
Save