From 6ba9404752d0262a0b81120b409fcef618d3c0fd Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sun, 10 Oct 2021 22:40:43 -0500 Subject: [PATCH] nvidia transcoding improvements (#412) * nvidia transcoding fixes * use yadif_cuda to deinterlace --- CHANGELOG.md | 6 +++++ .../FFmpeg/FFmpegComplexFilterBuilderTests.cs | 27 +++---------------- .../FFmpeg/TranscodingTests.cs | 7 ++--- .../FFmpeg/FFmpegComplexFilterBuilder.cs | 8 +++--- ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs | 11 ++++++-- ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs | 2 +- docker/nvidia/ffmpeg.Dockerfile | 2 +- 7 files changed, 29 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c9eb797d..9ff1797ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added +- Include more cuda (nvidia) filters in docker image +- Enable deinterlacing with nvidia using new `yadif_cuda` filter + +### Fixed +- Fix some transcoding edge cases with nvidia and pixel format `yuv420p10le` ## [0.1.1-alpha] - 2021-10-10 ### Added diff --git a/ErsatzTV.Core.Tests/FFmpeg/FFmpegComplexFilterBuilderTests.cs b/ErsatzTV.Core.Tests/FFmpeg/FFmpegComplexFilterBuilderTests.cs index f94392097..eb8c7c9db 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/FFmpegComplexFilterBuilderTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/FFmpegComplexFilterBuilderTests.cs @@ -341,43 +341,24 @@ namespace ErsatzTV.Core.Tests.FFmpeg } [Test] - // TODO: get yadif_cuda working in docker - // [TestCase(true, false, false, "[0:V]yadif_cuda[v]", "[v]")] - // [TestCase( - // true, - // true, - // false, - // "[0:V]yadif_cuda,scale_npp=1920:1000:format=yuv420p,hwdownload,setsar=1,hwupload[v]", - // "[v]")] - // [TestCase( - // true, - // false, - // true, - // "[0:V]yadif_cuda,hwdownload,setsar=1,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,hwupload[v]", - // "[v]")] - // [TestCase( - // true, - // true, - // true, - // "[0:V]yadif_cuda,scale_npp=1920:1000:format=yuv420p,hwdownload,setsar=1,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,hwupload[v]", - // "[v]")] + [TestCase(true, false, false, "[0:0]yadif_cuda[v]", "[v]")] [TestCase( true, true, false, - "[0:0]scale_npp=1920:1000,hwdownload,format=nv12,setsar=1,hwupload[v]", + "[0:0]yadif_cuda,scale_npp=1920:1000,hwdownload,format=nv12,setsar=1,hwupload[v]", "[v]")] [TestCase( true, false, true, - "[0:0]hwdownload,format=nv12,setsar=1,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,hwupload[v]", + "[0:0]yadif_cuda,hwdownload,format=nv12,setsar=1,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,hwupload[v]", "[v]")] [TestCase( true, true, true, - "[0:0]scale_npp=1920:1000,hwdownload,format=nv12,setsar=1,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,hwupload[v]", + "[0:0]yadif_cuda,scale_npp=1920:1000,hwdownload,format=nv12,setsar=1,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,hwupload[v]", "[v]")] [TestCase( false, diff --git a/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs b/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs index d938a3d40..36ec7a490 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs @@ -205,9 +205,9 @@ namespace ErsatzTV.Core.Tests.FFmpeg if (profileAcceleration != HardwareAccelerationKind.None && unsupportedMessages.Any(error.Contains)) { - IEnumerable quotedArgs = process.StartInfo.ArgumentList.Map(a => $"\'{a}\'"); + var quotedArgs = process.StartInfo.ArgumentList.Map(a => $"\'{a}\'").ToList(); process.ExitCode.Should().Be(1, $"Error message with successful exit code? {string.Join(" ", quotedArgs)}"); - Assert.Warn("Unsupported on this hardware"); + Assert.Warn($"Unsupported on this hardware: ffmpeg {string.Join(" ", quotedArgs)}"); } else if (error.Contains("Impossible to convert between")) { @@ -216,7 +216,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg } else { - process.ExitCode.Should().Be(0, error); + IEnumerable quotedArgs = process.StartInfo.ArgumentList.Map(a => $"\'{a}\'"); + process.ExitCode.Should().Be(0, error + Environment.NewLine + string.Join(" ", quotedArgs)); } } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs b/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs index a3af18272..57552e715 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs @@ -125,7 +125,7 @@ namespace ErsatzTV.Core.FFmpeg string filter = acceleration switch { HardwareAccelerationKind.Qsv => "deinterlace_qsv", - HardwareAccelerationKind.Nvenc => "", // TODO: yadif_cuda support in docker + HardwareAccelerationKind.Nvenc => "yadif_cuda", HardwareAccelerationKind.Vaapi => "deinterlace_vaapi", _ => "yadif=1" }; @@ -156,8 +156,8 @@ namespace ErsatzTV.Core.FFmpeg string filter = acceleration switch { HardwareAccelerationKind.Qsv => $"scale_qsv=w={size.Width}:h={size.Height}", - HardwareAccelerationKind.Nvenc when _pixelFormat == "yuv420p10le" => - $"hwdownload,format=p010le,format=nv12,hwupload,scale_npp={size.Width}:{size.Height}", + HardwareAccelerationKind.Nvenc when _pixelFormat is "yuv420p10le" => + $"hwupload_cuda,scale_cuda={size.Width}:{size.Height}", HardwareAccelerationKind.Nvenc => $"scale_npp={size.Width}:{size.Height}", HardwareAccelerationKind.Vaapi => $"scale_vaapi=format=nv12:w={size.Width}:h={size.Height}", _ => $"scale={size.Width}:{size.Height}:flags=fast_bilinear" @@ -180,7 +180,7 @@ namespace ErsatzTV.Core.FFmpeg string format = acceleration switch { HardwareAccelerationKind.Vaapi => "format=nv12|vaapi", - HardwareAccelerationKind.Nvenc when _scaleToSize.IsNone && _pixelFormat == "yuv420p10le" => + HardwareAccelerationKind.Nvenc when _pixelFormat == "yuv420p10le" => "format=p010le,format=nv12", _ => "format=nv12" }; diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs index eb027a9c5..b5b6c86cf 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs @@ -72,7 +72,7 @@ namespace ErsatzTV.Core.FFmpeg return this; } - public FFmpegProcessBuilder WithHardwareAcceleration(HardwareAccelerationKind hwAccel) + public FFmpegProcessBuilder WithHardwareAcceleration(HardwareAccelerationKind hwAccel, string pixelFormat) { _hwAccel = hwAccel; @@ -85,10 +85,17 @@ namespace ErsatzTV.Core.FFmpeg _arguments.Add("qsv=qsv:MFX_IMPL_hw_any"); break; case HardwareAccelerationKind.Nvenc: + string outputFormat = pixelFormat switch + { + "yuv420p10le" => "p010le", + // "yuv444p10le" => "p016le", + _ => "cuda" + }; + _arguments.Add("-hwaccel"); _arguments.Add("cuda"); _arguments.Add("-hwaccel_output_format"); - _arguments.Add("cuda"); + _arguments.Add(outputFormat); break; case HardwareAccelerationKind.Vaapi: _arguments.Add("-hwaccel"); diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs index ad136a77e..e382a0fd8 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs @@ -64,7 +64,7 @@ namespace ErsatzTV.Core.FFmpeg FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, saveReports, _logger) .WithThreads(playbackSettings.ThreadCount) - .WithHardwareAcceleration(playbackSettings.HardwareAcceleration) + .WithHardwareAcceleration(playbackSettings.HardwareAcceleration, videoStream.PixelFormat) .WithVaapiDriver(maybeVaapiDriver) .WithQuiet() .WithFormatFlags(playbackSettings.FormatFlags) diff --git a/docker/nvidia/ffmpeg.Dockerfile b/docker/nvidia/ffmpeg.Dockerfile index 81258424d..cfc130340 100644 --- a/docker/nvidia/ffmpeg.Dockerfile +++ b/docker/nvidia/ffmpeg.Dockerfile @@ -1,6 +1,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:5.0-focal-amd64 AS dotnet-runtime -FROM jrottenberg/ffmpeg:4.3-nvidia2004 AS runtime-base +FROM jasongdove/ffmpeg-base:4.3-nvidia2004 AS runtime-base COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet RUN apt-get update \ && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu-dev tzdata \