From 9f3db05c170f88ef7052b2c1a71742efea65ae4e Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:15:46 +0000 Subject: [PATCH] fix graphics engine on vaapi (#2281) --- .../PrepareTroubleshootingPlaybackHandler.cs | 5 +++++ .../Scheduling/YamlScheduling/YamlPlayoutContext.cs | 4 ++++ .../Vaapi/OverlayGraphicsEngineVaapiFilter.cs | 13 +++++++++++++ ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs | 12 +++++++++--- ErsatzTV/Pages/PlaybackTroubleshooting.razor | 2 ++ 5 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 ErsatzTV.FFmpeg/Filter/Vaapi/OverlayGraphicsEngineVaapiFilter.cs diff --git a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs index 548762c16..fb2fce261 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs @@ -11,6 +11,7 @@ using ErsatzTV.Core.Interfaces.Jellyfin; using ErsatzTV.Core.Interfaces.Locking; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Plex; +using ErsatzTV.Core.Notifications; using ErsatzTV.FFmpeg; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; @@ -27,6 +28,7 @@ public class PrepareTroubleshootingPlaybackHandler( IFFmpegProcessService ffmpegProcessService, ILocalFileSystem localFileSystem, IEntityLocker entityLocker, + IMediator mediator, ILogger logger) : IRequestHandler> { @@ -43,6 +45,7 @@ public class PrepareTroubleshootingPlaybackHandler( catch (Exception ex) { entityLocker.UnlockTroubleshootingPlayback(); + await mediator.Publish(new PlaybackTroubleshootingCompletedNotification(-1), cancellationToken); logger.LogError(ex, "Error while preparing troubleshooting playback"); return BaseError.New(ex.Message); } @@ -116,6 +119,8 @@ public class PrepareTroubleshootingPlaybackHandler( true, new Channel(Guid.Empty) { + Artwork = [], + Name = "ETV", Number = ".troubleshooting", FFmpegProfile = ffmpegProfile, StreamingMode = StreamingMode.HttpLiveStreamingSegmenter, diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs index dedff6435..d57fbb0b7 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs @@ -178,6 +178,10 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio } State state = JsonConvert.DeserializeObject(anchor.Context); + if (state.ChannelWatermarkIds is null) + { + state = state with { ChannelWatermarkIds = [] }; + } foreach (int instructionIndex in Optional(state.InstructionIndex)) { diff --git a/ErsatzTV.FFmpeg/Filter/Vaapi/OverlayGraphicsEngineVaapiFilter.cs b/ErsatzTV.FFmpeg/Filter/Vaapi/OverlayGraphicsEngineVaapiFilter.cs new file mode 100644 index 000000000..cd018db0e --- /dev/null +++ b/ErsatzTV.FFmpeg/Filter/Vaapi/OverlayGraphicsEngineVaapiFilter.cs @@ -0,0 +1,13 @@ +using ErsatzTV.FFmpeg.Format; + +namespace ErsatzTV.FFmpeg.Filter.Vaapi; + +public class OverlayGraphicsEngineVaapiFilter(FrameState currentState, IPixelFormat outputPixelFormat) : BaseFilter +{ + public override string Filter => + currentState.FrameDataLocation is FrameDataLocation.Hardware + ? "overlay_vaapi" + : $"overlay=format={(outputPixelFormat.BitDepth == 10 ? '1' : '0')}"; + + public override FrameState NextState(FrameState currentState) => currentState; +} diff --git a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs index 945336613..525811b8a 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs @@ -230,7 +230,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder currentState, watermarkOverlayFilterSteps); - SetGraphicsEngine(graphicsEngineInput, desiredState, graphicsEngineOverlayFilterSteps); + SetGraphicsEngine(graphicsEngineInput, currentState, desiredState, graphicsEngineOverlayFilterSteps); // after everything else is done, apply the encoder if (pipelineSteps.OfType().All(e => e.Kind != StreamKind.Video)) @@ -538,10 +538,11 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder private static void SetGraphicsEngine( Option graphicsEngineInput, + FrameState currentState, FrameState desiredState, List graphicsEngineOverlayFilterSteps) { - foreach (var _ in graphicsEngineInput) + foreach (var graphicsEngine in graphicsEngineInput) { foreach (IPixelFormat desiredPixelFormat in desiredState.PixelFormat) { @@ -554,7 +555,12 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder } } - graphicsEngineOverlayFilterSteps.Add(new OverlayGraphicsEngineFilter(pf)); + if (currentState.FrameDataLocation is FrameDataLocation.Hardware) + { + graphicsEngine.FilterSteps.Add(new HardwareUploadVaapiFilter(false)); + } + + graphicsEngineOverlayFilterSteps.Add(new OverlayGraphicsEngineVaapiFilter(currentState, pf)); } } } diff --git a/ErsatzTV/Pages/PlaybackTroubleshooting.razor b/ErsatzTV/Pages/PlaybackTroubleshooting.razor index 2ea0a56a5..975583f40 100644 --- a/ErsatzTV/Pages/PlaybackTroubleshooting.razor +++ b/ErsatzTV/Pages/PlaybackTroubleshooting.razor @@ -145,12 +145,14 @@ protected override async Task OnParametersSetAsync() { + _ffmpegProfiles.Clear(); _ffmpegProfiles.AddRange(await Mediator.Send(new GetAllFFmpegProfiles(), _cts.Token)); if (_ffmpegProfiles.Count > 0) { _ffmpegProfileId = _ffmpegProfiles.Map(f => f.Id).Head(); } + _watermarks.Clear(); _watermarks.AddRange(await Mediator.Send(new GetAllWatermarks(), _cts.Token)); if (MediaItemId is not null)