diff --git a/CHANGELOG.md b/CHANGELOG.md index 108b8c467..01eba14f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix detection of Plex Other Video libraries using `Plex Personal Media` agent - If the library is already detected as a Movies library in ETV, synchronization must be disabled for the library to change it to an Other Videos library - A warning will be logged when this scenario is detected +- VAAPI: work around buggy ffmpeg behavior where hevc_vaapi encoder with RadeonSI driver incorrectly outputs height of 1088 instead of 1080 ## [25.9.0] - 2025-11-29 ### Added diff --git a/ErsatzTV.FFmpeg/OutputOption/AmdCropMetadataWorkaroundFilter.cs b/ErsatzTV.FFmpeg/OutputOption/AmdCropMetadataWorkaroundFilter.cs new file mode 100644 index 000000000..96de6b5f6 --- /dev/null +++ b/ErsatzTV.FFmpeg/OutputOption/AmdCropMetadataWorkaroundFilter.cs @@ -0,0 +1,34 @@ +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/Pipeline/VaapiPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs index 562240754..2ad3352a3 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs @@ -283,6 +283,15 @@ 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([]),