From 7850ced6ec52112eb71a9d157b7bc07b20878fd8 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:11:07 -0500 Subject: [PATCH] fix: remove green line workarounds; etv's ffmpeg 8.1.2 fixes (#2953) --- .github/workflows/artifacts.yml | 2 +- CHANGELOG.md | 2 ++ .../AmdCropMetadataWorkaroundFilter.cs | 34 ------------------ .../NvidiaGreenLineWorkaroundFilter.cs | 35 ------------------- .../Pipeline/NvidiaPipelineBuilder.cs | 11 ------ .../Pipeline/VaapiPipelineBuilder.cs | 9 ----- 6 files changed, 3 insertions(+), 90 deletions(-) delete mode 100644 ErsatzTV.FFmpeg/OutputOption/AmdCropMetadataWorkaroundFilter.cs delete mode 100644 ErsatzTV.FFmpeg/OutputOption/NvidiaGreenLineWorkaroundFilter.cs diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index 7c036e8f4..80eb1e332 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -311,7 +311,7 @@ jobs: uses: suisei-cn/actions-download-file@v1.3.0 id: downloadffmpeg with: - url: "https://github.com/ErsatzTV/ErsatzTV-ffmpeg/releases/download/8.1.2/ffmpeg-n8.1.2-win64-gpl-8.1.zip" + url: "https://github.com/ErsatzTV/ErsatzTV-ffmpeg/releases/download/8.1.2/ffmpeg-n8.1.2-etv-g82f576a8-win64-gpl-8.1.zip" target: ffmpeg/ - name: Package artifacts diff --git a/CHANGELOG.md b/CHANGELOG.md index 48e62147d..faa0fbbd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Always randomize start points for all collections the first time they are used - Previously, only collections scheduled during the first day of a playout build had their start points randomized - Allow multiple ETV instances when multiple config folders are used +- Fix green line sometimes seen with NVIDIA and AMD/VAAPI encoding + - Both bugs were in ffmpeg and ETV's patched ffmpeg 8.1.2 is required for the fixes ## [26.6.0] - 2026-07-09 ### Added diff --git a/ErsatzTV.FFmpeg/OutputOption/AmdCropMetadataWorkaroundFilter.cs b/ErsatzTV.FFmpeg/OutputOption/AmdCropMetadataWorkaroundFilter.cs deleted file mode 100644 index 96de6b5f6..000000000 --- a/ErsatzTV.FFmpeg/OutputOption/AmdCropMetadataWorkaroundFilter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using ErsatzTV.FFmpeg.Format; - -namespace ErsatzTV.FFmpeg.OutputOption; - -public class AmdCropMetadataWorkaroundFilter(string videoFormat, FrameSize frameSize) : OutputOption -{ - public override string[] OutputOptions - { - get - { - if (videoFormat is not VideoFormat.Hevc) - { - return []; - } - - int cropPixels = frameSize.Height switch - { - 1080 => 8, - _ => 0 - }; - - if (cropPixels == 0) - { - return []; - } - - return - [ - "-bsf:v", - $"{videoFormat}_metadata=crop_bottom={cropPixels}" - ]; - } - } -} diff --git a/ErsatzTV.FFmpeg/OutputOption/NvidiaGreenLineWorkaroundFilter.cs b/ErsatzTV.FFmpeg/OutputOption/NvidiaGreenLineWorkaroundFilter.cs deleted file mode 100644 index 8cfecb9c7..000000000 --- a/ErsatzTV.FFmpeg/OutputOption/NvidiaGreenLineWorkaroundFilter.cs +++ /dev/null @@ -1,35 +0,0 @@ -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.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 62dfbf7bc..bee7ef619 100644 --- a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs @@ -353,17 +353,6 @@ 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))); - } - // unknown color params can *change* during transcoding, // which can cause ffmpeg to insert an autoscale filter that // cannot accept hardware frames. diff --git a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs index dd3d270f8..81709355f 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs @@ -291,15 +291,6 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder currentState, pipelineSteps); - if (ffmpegState.VaapiDriver == "radeonsi" && - ffmpegState.EncoderHardwareAccelerationMode is HardwareAccelerationMode.Vaapi) - { - pipelineSteps.Add( - new AmdCropMetadataWorkaroundFilter( - desiredState.VideoFormat, - desiredState.CroppedSize.IfNone(desiredState.PaddedSize))); - } - return new FilterChain( videoInputFile.FilterSteps, watermarkInputFile.Map(wm => wm.FilterSteps).IfNone([]),