diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bda7eec8..f195c5444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Text that already fits with the configured style will not be adjusted - Block schedules: add **experimental** `Break Content` to decos - Break content is similar to filler from classic schedules - - Break content is currently limited to placement `Break Start` (play before anything else in the block) + - Break content is currently limited to placement `Block Start` (play before anything else in the block) - Future work will add other placement options - Break content is currently limited to playlists (which do *not* pad - they simply play through the playlist one time) - Future work will add other collection options which will pad to the full block duration @@ -64,6 +64,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix deco dead air fallback selection and duration on mirror channels - Fix fallback filler duration on mirror channels - Fix slow startup caused by check for overlapping playout items +- Fix green line in *most* cases when overlaying content using NVIDIA acceleration ### Changed - Filler presets: use separate text fields for `hours`, `minutes` and `seconds` duration diff --git a/ErsatzTV.FFmpeg/OutputOption/NvidiaGreenLineWorkaroundFilter.cs b/ErsatzTV.FFmpeg/OutputOption/NvidiaGreenLineWorkaroundFilter.cs new file mode 100644 index 000000000..9e377b92a --- /dev/null +++ b/ErsatzTV.FFmpeg/OutputOption/NvidiaGreenLineWorkaroundFilter.cs @@ -0,0 +1,35 @@ +using ErsatzTV.FFmpeg.Format; + +namespace ErsatzTV.FFmpeg.OutputOption; + +public class NvidiaGreenLineWorkaroundFilter(string videoFormat, FrameSize frameSize) : OutputOption +{ + public override string[] OutputOptions + { + get + { + if (videoFormat is not (VideoFormat.Hevc or VideoFormat.H264)) + { + return []; + } + + int cropPixels = frameSize.Height switch + { + 1080 => 8, + 720 => 16, + _ => 0 + }; + + if (cropPixels == 0) + { + return []; + } + + return + [ + "-bsf:v", + $"{videoFormat}_metadata=crop_bottom={cropPixels}" + ]; + } + } +} diff --git a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs index 97df70689..684700782 100644 --- a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs @@ -329,6 +329,17 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder context, pipelineSteps); + if (ffmpegState.DecoderHardwareAccelerationMode is HardwareAccelerationMode.Nvenc && + ffmpegState.EncoderHardwareAccelerationMode is HardwareAccelerationMode.Nvenc && + (videoStream.FrameSize == desiredState.ScaledSize || (context is { HasSubtitleOverlay: true, HasGraphicsEngine: true } && desiredState.PaddedSize == desiredState.ScaledSize)) && + (context.HasSubtitleOverlay || context.HasGraphicsEngine || context.HasWatermark)) + { + pipelineSteps.Add( + new NvidiaGreenLineWorkaroundFilter( + desiredState.VideoFormat, + desiredState.CroppedSize.IfNone(desiredState.PaddedSize))); + } + return new FilterChain( videoInputFile.FilterSteps, watermarkInputFile.Map(wm => wm.FilterSteps).IfNone([]),