From 3a4dff8cc475e53691466dde2db9828b4d1b66aa Mon Sep 17 00:00:00 2001 From: WizardOfYendor1 Date: Sat, 28 Feb 2026 11:21:53 -0500 Subject: Fix live stream consumer leak when PositionTicks is negative When a stalled HLS client sends ReportPlaybackStopped with a negative PositionTicks (e.g. HLS.js reporting position on buffer stall), the ArgumentOutOfRangeException was thrown before GetSession was called, causing CloseLiveStreamIfNeededAsync to never run. This left the ILiveStream.ConsumerCount permanently incremented, permanently holding a tuner open with no active viewer. Fix by acquiring the session and performing live stream cleanup before throwing the validation exception, so tuner resources are always freed even when playback reporting fails with invalid position data. Co-Authored-By: Claude Opus 4.6 --- Emby.Server.Implementations/Session/SessionManager.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 8e14f5bdf4..7e13e17eb6 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1025,15 +1025,22 @@ namespace Emby.Server.Implementations.Session ArgumentNullException.ThrowIfNull(info); + var session = GetSession(info.SessionId); + + session.StopAutomaticProgress(); + if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0) { + // Ensure live stream is cleaned up before throwing, to prevent tuner + // resource leaks when stalled clients report a negative PositionTicks. + if (!string.IsNullOrEmpty(info.LiveStreamId)) + { + await CloseLiveStreamIfNeededAsync(info.LiveStreamId, session.Id).ConfigureAwait(false); + } + throw new ArgumentOutOfRangeException(nameof(info), "The PlaybackStopInfo's PositionTicks was negative."); } - var session = GetSession(info.SessionId); - - session.StopAutomaticProgress(); - var libraryItem = info.ItemId.IsEmpty() ? null : GetNowPlayingItem(session, info.ItemId); -- cgit v1.2.3