Browse Source

fix recent nvidia regression (#2437)

* fix recent nvidia regression

* update transcoding tests for graphics engine
pull/2438/head
Jason Dove 4 months ago committed by GitHub
parent
commit
aa5ba5a78e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 15
      ErsatzTV.FFmpeg/OutputOption/PixelFormatOutputOption.cs
  2. 30
      ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs
  3. 44
      ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs

15
ErsatzTV.FFmpeg/OutputOption/PixelFormatOutputOption.cs

@ -2,14 +2,17 @@
namespace ErsatzTV.FFmpeg.OutputOption; namespace ErsatzTV.FFmpeg.OutputOption;
public class PixelFormatOutputOption : OutputOption public class PixelFormatOutputOption(
IPixelFormat pixelFormat,
HardwareAccelerationMode encoderMode = HardwareAccelerationMode.None)
: OutputOption
{ {
private readonly IPixelFormat _pixelFormat;
public PixelFormatOutputOption(IPixelFormat pixelFormat) => _pixelFormat = pixelFormat; public override string[] OutputOptions =>
[
public override string[] OutputOptions => ["-pix_fmt", _pixelFormat.Name]; "-pix_fmt", encoderMode is HardwareAccelerationMode.Nvenc ? pixelFormat.FFmpegName : pixelFormat.Name
];
public override FrameState NextState(FrameState currentState) => public override FrameState NextState(FrameState currentState) =>
currentState with { PixelFormat = Some(_pixelFormat) }; currentState with { PixelFormat = Some(pixelFormat) };
} }

30
ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs

@ -181,6 +181,19 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
true); true);
currentState = filter.NextState(currentState); currentState = filter.NextState(currentState);
videoInputFile.FilterSteps.Add(filter); videoInputFile.FilterSteps.Add(filter);
if (desiredState.BitDepth == 8)
{
var filter2 = new ScaleCudaFilter(
currentState with { PixelFormat = new PixelFormatYuv420P() },
videoStream.FrameSize,
videoStream.FrameSize,
Option<FrameSize>.None,
false,
false);
currentState = filter2.NextState(currentState);
videoInputFile.FilterSteps.Add(filter2);
}
} }
} }
@ -214,7 +227,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
{ {
Option<IPixelFormat> desiredPixelFormat = Some((IPixelFormat)new PixelFormatYuv420P()); Option<IPixelFormat> desiredPixelFormat = Some((IPixelFormat)new PixelFormatYuv420P());
if (desiredPixelFormat != currentState.PixelFormat) if (desiredPixelFormat.Map(pf => pf.FFmpegName) != currentState.PixelFormat.Map(pf => pf.FFmpegName))
{ {
if (currentState.FrameDataLocation == FrameDataLocation.Software) if (currentState.FrameDataLocation == FrameDataLocation.Software)
{ {
@ -244,8 +257,8 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
} }
// need to upload for any sort of overlay // need to upload for any sort of overlay
if (currentState.FrameDataLocation == FrameDataLocation.Software && if (currentState is { FrameDataLocation: FrameDataLocation.Software, BitDepth: 8 }
currentState.BitDepth == 8 && !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);
@ -263,10 +276,9 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
fontsFolder, fontsFolder,
subtitleOverlayFilterSteps); subtitleOverlayFilterSteps);
// need to use software overlay for watermark with fade points // need to use software overlay with 10 bit primary content and graphics engine
// because `-loop 1` seems to cause a green line at the bottom of the resulting video with overlay_cuda if (currentState.FrameDataLocation is FrameDataLocation.Hardware && context.HasGraphicsEngine &&
if (context.HasWatermark && watermarkInputFile currentState.BitDepth == 10)
.Map(wm => wm.DesiredState.MaybeFadePoints.Map(fp => fp.Count > 0).IfNone(false)).IfNone(false))
{ {
var hardwareDownload = new CudaHardwareDownloadFilter(currentState.PixelFormat, None); var hardwareDownload = new CudaHardwareDownloadFilter(currentState.PixelFormat, None);
currentState = hardwareDownload.NextState(currentState); currentState = hardwareDownload.NextState(currentState);
@ -434,12 +446,12 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
} }
else else
{ {
pipelineSteps.Add(new PixelFormatOutputOption(format)); pipelineSteps.Add(new PixelFormatOutputOption(format, ffmpegState.EncoderHardwareAccelerationMode));
} }
} }
else else
{ {
pipelineSteps.Add(new PixelFormatOutputOption(format)); pipelineSteps.Add(new PixelFormatOutputOption(format, ffmpegState.EncoderHardwareAccelerationMode));
} }
} }

44
ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs

@ -113,16 +113,16 @@ public class TranscodingTests
[ [
Watermark.None, Watermark.None,
Watermark.PermanentOpaqueScaled, Watermark.PermanentOpaqueScaled,
Watermark.PermanentOpaqueActualSize, // Watermark.PermanentOpaqueActualSize,
Watermark.PermanentTransparentScaled, // Watermark.PermanentTransparentScaled,
Watermark.PermanentTransparentActualSize // Watermark.PermanentTransparentActualSize
]; ];
public static Subtitle[] Subtitles = public static Subtitle[] Subtitles =
[ [
Subtitle.None, Subtitle.None,
Subtitle.Picture, Subtitle.Picture,
Subtitle.Text // Subtitle.Text
]; ];
public static Padding[] Paddings = public static Padding[] Paddings =
@ -253,6 +253,13 @@ public class TranscodingTests
Arg.Any<Option<int>>()) Arg.Any<Option<int>>())
.Returns(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "song_album_cover_512.png")); .Returns(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "song_album_cover_512.png"));
var graphicsElementLoader = Substitute.For<IGraphicsElementLoader>();
graphicsElementLoader.LoadAll(
Arg.Any<GraphicsEngineContext>(),
Arg.Any<List<PlayoutItemGraphicsElement>>(),
Arg.Any<CancellationToken>())
.Returns(callInfo => Task.FromResult(callInfo.Arg<GraphicsEngineContext>()));
var oldService = new FFmpegProcessService( var oldService = new FFmpegProcessService(
new FakeStreamSelector(), new FakeStreamSelector(),
tempFilePool, tempFilePool,
@ -272,7 +279,7 @@ public class TranscodingTests
LoggerFactory.CreateLogger<HardwareCapabilitiesFactory>()), LoggerFactory.CreateLogger<HardwareCapabilitiesFactory>()),
LoggerFactory.CreateLogger<PipelineBuilderFactory>()), LoggerFactory.CreateLogger<PipelineBuilderFactory>()),
Substitute.For<IConfigElementRepository>(), Substitute.For<IConfigElementRepository>(),
Substitute.For<IGraphicsElementLoader>(), graphicsElementLoader,
LoggerFactory.CreateLogger<FFmpegLibraryProcessService>()); LoggerFactory.CreateLogger<FFmpegLibraryProcessService>());
var songVideoGenerator = new SongVideoGenerator(tempFilePool, mockImageCache, service); var songVideoGenerator = new SongVideoGenerator(tempFilePool, mockImageCache, service);
@ -362,7 +369,7 @@ public class TranscodingTests
PlayoutItemResult playoutItemResult = await service.ForPlayoutItem( PlayoutItemResult playoutItemResult = await service.ForPlayoutItem(
ExecutableName("ffmpeg"), ExecutableName("ffmpeg"),
ExecutableName("ffprobe"), ExecutableName("ffprobe"),
false, true,
channel, channel,
videoVersion, videoVersion,
new MediaItemAudioVersion(song, songVersion), new MediaItemAudioVersion(song, songVersion),
@ -617,7 +624,11 @@ public class TranscodingTests
hasSubtitleFilters.ShouldBe(subtitle != Subtitle.None); hasSubtitleFilters.ShouldBe(subtitle != Subtitle.None);
bool hasWatermarkFilters = filterChain.WatermarkOverlayFilterSteps.Any(s => bool hasWatermarkFilters = filterChain.WatermarkOverlayFilterSteps.Any(s =>
s is OverlayWatermarkFilter or OverlayWatermarkCudaFilter or OverlayWatermarkQsvFilter); s is OverlayWatermarkFilter or OverlayWatermarkCudaFilter
or OverlayWatermarkQsvFilter) ||
filterChain.GraphicsEngineOverlayFilterSteps.Any(s =>
s is OverlayGraphicsEngineFilter or OverlayGraphicsEngineCudaFilter
or OverlayGraphicsEngineVaapiFilter);
hasWatermarkFilters.ShouldBe(watermark != Watermark.None); hasWatermarkFilters.ShouldBe(watermark != Watermark.None);
} }
@ -668,7 +679,7 @@ public class TranscodingTests
PlayoutItemResult playoutItemResult = await service.ForPlayoutItem( PlayoutItemResult playoutItemResult = await service.ForPlayoutItem(
ExecutableName("ffmpeg"), ExecutableName("ffmpeg"),
ExecutableName("ffprobe"), ExecutableName("ffprobe"),
false, true,
channel, channel,
v, v,
new MediaItemAudioVersion(null, v), new MediaItemAudioVersion(null, v),
@ -725,6 +736,7 @@ public class TranscodingTests
{ {
ImageSource = ChannelWatermarkImageSource.Custom, ImageSource = ChannelWatermarkImageSource.Custom,
Mode = ChannelWatermarkMode.Intermittent, Mode = ChannelWatermarkMode.Intermittent,
Image = "ASDF",
// TODO: how do we make sure this actually appears // TODO: how do we make sure this actually appears
FrequencyMinutes = 1, FrequencyMinutes = 1,
DurationSeconds = 2, DurationSeconds = 2,
@ -735,6 +747,7 @@ public class TranscodingTests
{ {
ImageSource = ChannelWatermarkImageSource.Custom, ImageSource = ChannelWatermarkImageSource.Custom,
Mode = ChannelWatermarkMode.Intermittent, Mode = ChannelWatermarkMode.Intermittent,
Image = "ASDF",
// TODO: how do we make sure this actually appears // TODO: how do we make sure this actually appears
FrequencyMinutes = 1, FrequencyMinutes = 1,
DurationSeconds = 2, DurationSeconds = 2,
@ -745,6 +758,7 @@ public class TranscodingTests
{ {
ImageSource = ChannelWatermarkImageSource.Custom, ImageSource = ChannelWatermarkImageSource.Custom,
Mode = ChannelWatermarkMode.Permanent, Mode = ChannelWatermarkMode.Permanent,
Image = "ASDF",
Opacity = 100, Opacity = 100,
Size = WatermarkSize.Scaled, Size = WatermarkSize.Scaled,
WidthPercent = 15 WidthPercent = 15
@ -754,6 +768,7 @@ public class TranscodingTests
{ {
ImageSource = ChannelWatermarkImageSource.Custom, ImageSource = ChannelWatermarkImageSource.Custom,
Mode = ChannelWatermarkMode.Permanent, Mode = ChannelWatermarkMode.Permanent,
Image = "ASDF",
Opacity = 100, Opacity = 100,
Size = WatermarkSize.ActualSize Size = WatermarkSize.ActualSize
}; };
@ -762,6 +777,7 @@ public class TranscodingTests
{ {
ImageSource = ChannelWatermarkImageSource.Custom, ImageSource = ChannelWatermarkImageSource.Custom,
Mode = ChannelWatermarkMode.Permanent, Mode = ChannelWatermarkMode.Permanent,
Image = "ASDF",
Opacity = 80, Opacity = 80,
Size = WatermarkSize.Scaled, Size = WatermarkSize.Scaled,
WidthPercent = 15 WidthPercent = 15
@ -771,6 +787,7 @@ public class TranscodingTests
{ {
ImageSource = ChannelWatermarkImageSource.Custom, ImageSource = ChannelWatermarkImageSource.Custom,
Mode = ChannelWatermarkMode.Permanent, Mode = ChannelWatermarkMode.Permanent,
Image = "ASDF",
Opacity = 80, Opacity = 80,
Size = WatermarkSize.ActualSize Size = WatermarkSize.ActualSize
}; };
@ -930,6 +947,13 @@ public class TranscodingTests
Arg.Any<Option<int>>()) Arg.Any<Option<int>>())
.Returns(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "song_album_cover_512.png")); .Returns(Path.Combine(TestContext.CurrentContext.TestDirectory, "Resources", "song_album_cover_512.png"));
var graphicsElementLoader = Substitute.For<IGraphicsElementLoader>();
graphicsElementLoader.LoadAll(
Arg.Any<GraphicsEngineContext>(),
Arg.Any<List<PlayoutItemGraphicsElement>>(),
Arg.Any<CancellationToken>())
.Returns(callInfo => Task.FromResult(callInfo.Arg<GraphicsEngineContext>()));
var oldService = new FFmpegProcessService( var oldService = new FFmpegProcessService(
new FakeStreamSelector(), new FakeStreamSelector(),
Substitute.For<ITempFilePool>(), Substitute.For<ITempFilePool>(),
@ -949,7 +973,7 @@ public class TranscodingTests
LoggerFactory.CreateLogger<HardwareCapabilitiesFactory>()), LoggerFactory.CreateLogger<HardwareCapabilitiesFactory>()),
LoggerFactory.CreateLogger<PipelineBuilderFactory>()), LoggerFactory.CreateLogger<PipelineBuilderFactory>()),
Substitute.For<IConfigElementRepository>(), Substitute.For<IConfigElementRepository>(),
Substitute.For<IGraphicsElementLoader>(), graphicsElementLoader,
LoggerFactory.CreateLogger<FFmpegLibraryProcessService>()); LoggerFactory.CreateLogger<FFmpegLibraryProcessService>());
return service; return service;
@ -1125,5 +1149,5 @@ public class TranscodingTests
private static string ExecutableName(string baseName) => private static string ExecutableName(string baseName) =>
OperatingSystem.IsWindows() OperatingSystem.IsWindows()
? $"{baseName}.exe" ? $"{baseName}.exe"
: $"/home/jason/Downloads/ffmpeg/ffmpeg-n7.1.1-56-gc2184b65d2-linux64-gpl-7.1/bin/{baseName}"; : $"{baseName}";
} }

Loading…
Cancel
Save