diff --git a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj index 430312c3b..46d72c573 100644 --- a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj +++ b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj @@ -16,7 +16,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs b/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs index 000e3ccad..aa44bea8b 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs @@ -35,7 +35,7 @@ namespace ErsatzTV.Core.Tests.FFmpeg; public class TranscodingTests { private static readonly ILoggerFactory LoggerFactory; - private static readonly MemoryCache _memoryCache; + private static readonly MemoryCache MemoryCache; static TranscodingTests() { @@ -46,7 +46,7 @@ public class TranscodingTests LoggerFactory = new LoggerFactory().AddSerilog(Log.Logger); - _memoryCache = new MemoryCache(new MemoryCacheOptions()); + MemoryCache = new MemoryCache(new MemoryCacheOptions()); } [Test] @@ -61,7 +61,13 @@ public class TranscodingTests Assert.Pass(); } - public record InputFormat(string Encoder, string PixelFormat); + public record InputFormat( + string Encoder, + string PixelFormat, + string ColorRange = "tv", + string ColorSpace = "bt709", + string ColorTransfer = "bt709", + string ColorPrimaries = "bt709"); public enum Padding { @@ -93,45 +99,47 @@ public class TranscodingTests { public static Watermark[] Watermarks = { - Watermark.None, + // Watermark.None, Watermark.PermanentOpaqueScaled, - Watermark.PermanentOpaqueActualSize, - Watermark.PermanentTransparentScaled, - Watermark.PermanentTransparentActualSize + // Watermark.PermanentOpaqueActualSize, + // Watermark.PermanentTransparentScaled, + // Watermark.PermanentTransparentActualSize }; public static Subtitle[] Subtitles = { - Subtitle.None, - Subtitle.Picture, + // Subtitle.None, + // Subtitle.Picture, Subtitle.Text }; public static Padding[] Paddings = { - Padding.NoPadding, + // Padding.NoPadding, Padding.WithPadding }; public static VideoScanKind[] VideoScanKinds = { VideoScanKind.Progressive, - VideoScanKind.Interlaced + // VideoScanKind.Interlaced }; public static InputFormat[] InputFormats = { - new("libx264", "yuv420p"), + // example format that requires colorspace filter + new("libx264", "yuv420p", "tv", "smpte170m", "bt709", "smpte170m"), + // new("libx264", "yuvj420p"), - new("libx264", "yuv420p10le"), + // new("libx264", "yuv420p10le"), // new("libx264", "yuv444p10le"), // new("mpeg1video", "yuv420p"), // // new("mpeg2video", "yuv420p"), - new("libx265", "yuv420p"), - new("libx265", "yuv420p10le"), + // new("libx265", "yuv420p"), + // new("libx265", "yuv420p10le"), // new("mpeg4", "yuv420p"), // @@ -148,14 +156,14 @@ public class TranscodingTests public static Resolution[] Resolutions = { - new() { Width = 1920, Height = 1080 }, + // new() { Width = 1920, Height = 1080 }, new() { Width = 1280, Height = 720 } }; public static FFmpegProfileBitDepth[] BitDepths = { FFmpegProfileBitDepth.EightBit, - FFmpegProfileBitDepth.TenBit + // FFmpegProfileBitDepth.TenBit }; public static HardwareAccelerationKind[] NoAcceleration = @@ -166,7 +174,7 @@ public class TranscodingTests public static FFmpegProfileVideoFormat[] VideoFormats = { FFmpegProfileVideoFormat.H264, - FFmpegProfileVideoFormat.Hevc + // FFmpegProfileVideoFormat.Hevc }; public static HardwareAccelerationKind[] NvidiaAcceleration = @@ -215,8 +223,8 @@ public class TranscodingTests [ValueSource(typeof(TestData), nameof(TestData.VideoFormats))] FFmpegProfileVideoFormat profileVideoFormat, // [ValueSource(typeof(TestData), nameof(TestData.NoAcceleration))] HardwareAccelerationKind profileAcceleration) - // [ValueSource(typeof(TestData), nameof(TestData.NvidiaAcceleration))] HardwareAccelerationKind profileAcceleration) - [ValueSource(typeof(TestData), nameof(TestData.VaapiAcceleration))] HardwareAccelerationKind profileAcceleration) + [ValueSource(typeof(TestData), nameof(TestData.NvidiaAcceleration))] HardwareAccelerationKind profileAcceleration) + // [ValueSource(typeof(TestData), nameof(TestData.VaapiAcceleration))] HardwareAccelerationKind profileAcceleration) // [ValueSource(typeof(TestData), nameof(TestData.QsvAcceleration))] HardwareAccelerationKind profileAcceleration) // [ValueSource(typeof(TestData), nameof(TestData.VideoToolboxAcceleration))] HardwareAccelerationKind profileAcceleration) // [ValueSource(typeof(TestData), nameof(TestData.AmfAcceleration))] HardwareAccelerationKind profileAcceleration) @@ -230,7 +238,7 @@ public class TranscodingTests } } - string name = GetStringSha256Hash($"{inputFormat.Encoder}_{inputFormat.PixelFormat}_{videoScanKind}_{padding}_{subtitle}"); + string name = GetStringSha256Hash($"{inputFormat}_{videoScanKind}_{padding}_{subtitle}"); string file = Path.Combine(TestContext.CurrentContext.TestDirectory, $"{name}.mkv"); if (!File.Exists(file)) @@ -242,8 +250,24 @@ public class TranscodingTests : string.Empty; string flags = videoScanKind == VideoScanKind.Interlaced ? "-field_order tt -flags +ildct+ilme" : string.Empty; + string colorRange = !string.IsNullOrWhiteSpace(inputFormat.ColorRange) + ? $" -color_range {inputFormat.ColorRange}" + : string.Empty; + + string colorSpace = !string.IsNullOrWhiteSpace(inputFormat.ColorSpace) + ? $" -colorspace {inputFormat.ColorSpace}" + : string.Empty; + + string colorTransfer = !string.IsNullOrWhiteSpace(inputFormat.ColorTransfer) + ? $" -color_trc {inputFormat.ColorTransfer}" + : string.Empty; + + string colorPrimaries = !string.IsNullOrWhiteSpace(inputFormat.ColorPrimaries) + ? $" -color_primaries {inputFormat.ColorPrimaries}" + : string.Empty; + string args = - $"-y -f lavfi -i anoisesrc=color=brown -f lavfi -i testsrc=duration=1:size={resolution}:rate=30 {videoFilter} -c:a aac -c:v {inputFormat.Encoder} -shortest -pix_fmt {inputFormat.PixelFormat} -strict -2 {flags} {file}"; + $"-y -f lavfi -i anoisesrc=color=brown -f lavfi -i testsrc=duration=1:size={resolution}:rate=30 {videoFilter} -c:a aac -c:v {inputFormat.Encoder}{colorRange}{colorSpace}{colorTransfer}{colorPrimaries} -shortest -pix_fmt {inputFormat.PixelFormat} -strict -2 {flags} {file}"; var p1 = new Process { StartInfo = new ProcessStartInfo @@ -321,7 +345,7 @@ public class TranscodingTests imageCache.Object, new Mock().Object, new Mock().Object, - _memoryCache, + MemoryCache, LoggerFactory.CreateLogger()); var service = new FFmpegLibraryProcessService( @@ -333,7 +357,7 @@ public class TranscodingTests new RuntimeInfo(), //new FakeNvidiaCapabilitiesFactory(), new HardwareCapabilitiesFactory( - _memoryCache, + MemoryCache, LoggerFactory.CreateLogger()), LoggerFactory.CreateLogger()), LoggerFactory.CreateLogger()); diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj b/ErsatzTV.Core/ErsatzTV.Core.csproj index 0f7a6ce1b..cbf78ae95 100644 --- a/ErsatzTV.Core/ErsatzTV.Core.csproj +++ b/ErsatzTV.Core/ErsatzTV.Core.csproj @@ -9,7 +9,7 @@ - + diff --git a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj index e83c35dd6..4d350c09c 100644 --- a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj +++ b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs b/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs index 7973cf0de..0e6c855d7 100644 --- a/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs @@ -4,17 +4,20 @@ namespace ErsatzTV.FFmpeg.Filter; public class ColorspaceFilter : BaseFilter { + private readonly FrameState _currentState; private readonly VideoStream _videoStream; private readonly IPixelFormat _desiredPixelFormat; private readonly bool _forceInputOverrides; private readonly FrameDataLocation _nextDataLocation; public ColorspaceFilter( + FrameState currentState, VideoStream videoStream, IPixelFormat desiredPixelFormat, bool forceInputOverrides = false, FrameDataLocation nextDataLocation = FrameDataLocation.Software) { + _currentState = currentState; _videoStream = videoStream; _desiredPixelFormat = desiredPixelFormat; _forceInputOverrides = forceInputOverrides; @@ -37,21 +40,33 @@ public class ColorspaceFilter : BaseFilter { get { + string hwdownload = string.Empty; + if (_currentState.FrameDataLocation == FrameDataLocation.Hardware) + { + hwdownload = "hwdownload"; + } + string inputOverrides = string.Empty; ColorParams cp = _videoStream.ColorParams; if (cp.IsMixed || _forceInputOverrides) { string range = string.IsNullOrWhiteSpace(cp.ColorRange) ? "tv" : cp.ColorRange; + string transfer = string.IsNullOrWhiteSpace(cp.ColorTransfer) + ? "bt709" + : cp.ColorTransfer; + string primaries = string.IsNullOrWhiteSpace(cp.ColorPrimaries) + ? "bt709" + : cp.ColorPrimaries; inputOverrides = - $"irange={range}:ispace={cp.ColorSpace}:itrc={cp.ColorTransfer}:iprimaries={cp.ColorPrimaries}:"; + $"irange={range}:ispace={cp.ColorSpace}:itrc={transfer}:iprimaries={primaries}:"; } string colorspace = _desiredPixelFormat.BitDepth switch { _ when cp.IsUnknown => "setparams=range=tv:colorspace=bt709:color_trc=bt709:color_primaries=bt709", 10 or 8 when !cp.IsUnknown => - $"colorspace={inputOverrides}all=bt709:format={_desiredPixelFormat.FFmpegName}", + $"{hwdownload},colorspace={inputOverrides}all=bt709:format={_desiredPixelFormat.FFmpegName}", _ => string.Empty }; diff --git a/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs b/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs index cd607ff34..519279d67 100644 --- a/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs @@ -317,7 +317,7 @@ public class ComplexFilter : IPipelineStep if (!videoStream.ColorParams.IsBt709) { _logger.LogDebug("Adding colorspace filter"); - var colorspace = new ColorspaceFilter(videoStream, pixelFormat); + var colorspace = new ColorspaceFilter(_currentState, videoStream, pixelFormat); _pipelineSteps.Add(colorspace); filter = colorspace.Filter; } diff --git a/ErsatzTV.FFmpeg/Pipeline/AmfPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/AmfPipelineBuilder.cs index 2503ef563..bed01b179 100644 --- a/ErsatzTV.FFmpeg/Pipeline/AmfPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/AmfPipelineBuilder.cs @@ -93,7 +93,7 @@ public class AmfPipelineBuilder : SoftwarePipelineBuilder if (!videoStream.ColorParams.IsBt709) { _logger.LogDebug("Adding colorspace filter"); - var colorspace = new ColorspaceFilter(videoStream, pixelFormat); + var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat); currentState = colorspace.NextState(currentState); result.Add(colorspace); } diff --git a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs index 3c85d18da..8c5718d84 100644 --- a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs @@ -181,7 +181,8 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder // need to upload for any sort of overlay if (currentState.FrameDataLocation == FrameDataLocation.Software && - currentState.BitDepth == 8 && (context.HasSubtitleOverlay || context.HasWatermark)) + currentState.BitDepth == 8 && context.HasSubtitleText == false + && (context.HasSubtitleOverlay || context.HasWatermark)) { var hardwareUpload = new HardwareUploadCudaFilter(currentState); currentState = hardwareUpload.NextState(currentState); @@ -268,7 +269,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder if (!videoStream.ColorParams.IsBt709) { _logger.LogDebug("Adding colorspace filter"); - var colorspace = new ColorspaceFilter(videoStream, format, false, currentState.FrameDataLocation); + var colorspace = new ColorspaceFilter(currentState, videoStream, format, false, currentState.FrameDataLocation); currentState = colorspace.NextState(currentState); result.Add(colorspace); diff --git a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs index 92e74972f..44fe515e4 100644 --- a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs @@ -246,6 +246,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder bool forceInputOverrides = videoInputFile.FilterSteps.Any(f => f is QsvFormatFilter or ScaleQsvFilter); var colorspace = new ColorspaceFilter( + currentState, videoStream, format, forceInputOverrides, diff --git a/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs index 9db0f84f1..e98d48fc9 100644 --- a/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs @@ -140,7 +140,7 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase if (!videoStream.ColorParams.IsBt709) { _logger.LogDebug("Adding colorspace filter"); - var colorspace = new ColorspaceFilter(videoStream, pixelFormat); + var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat); currentState = colorspace.NextState(currentState); result.Add(colorspace); } diff --git a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs index 3ac365c0c..8f61decae 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs @@ -246,7 +246,12 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder if (!videoStream.ColorParams.IsBt709) { _logger.LogDebug("Adding colorspace filter"); - var colorspace = new ColorspaceFilter(videoStream, format, false, currentState.FrameDataLocation); + var colorspace = new ColorspaceFilter( + currentState, + videoStream, + format, + false, + currentState.FrameDataLocation); currentState = colorspace.NextState(currentState); result.Add(colorspace); } diff --git a/ErsatzTV.FFmpeg/Pipeline/VideoToolboxPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VideoToolboxPipelineBuilder.cs index fc1de86d1..d51889cd2 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VideoToolboxPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VideoToolboxPipelineBuilder.cs @@ -114,7 +114,7 @@ public class VideoToolboxPipelineBuilder : SoftwarePipelineBuilder if (!videoStream.ColorParams.IsBt709) { _logger.LogDebug("Adding colorspace filter"); - var colorspace = new ColorspaceFilter(videoStream, pixelFormat); + var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat); currentState = colorspace.NextState(currentState); result.Add(colorspace); } diff --git a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj index b6bac8ad8..0c147cdca 100644 --- a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj +++ b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 3ed5475b1..52af8ea5d 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -70,7 +70,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - +