From e2586eed9b04d501cd5805711cb6ad5553c1816b Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 5 Aug 2026 00:33:42 +0800 Subject: Fix concurrent ffmpeg segment racing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a nasty one. The failure mode is: 1. Request A started FFmpeg and waited for a segment. 2. Request B requested an earlier or far away segment. 3. Jellyfin thought FFmpeg should to restart at a different position. 4. Request B killed the existing transcoding job. 5. Killing that job cancelled the same token request A was using. 6. The cancellation produced http 500 to request A. To fix this: we lock transcoding job state changes and segment handling per playlist, and use a thread safe counter to track how many http responses are still using each job’s segments. A job is only stopped or replaced once that counter reaches zero. --- .../MediaEncoding/TranscodingJob.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs index 56990d0b82..5045030b9b 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs @@ -15,6 +15,7 @@ public sealed class TranscodingJob : IDisposable private readonly Lock _processLock = new(); private readonly Lock _timerLock = new(); + private int _activeRequestCount; private Timer? _killTimer; /// @@ -64,7 +65,11 @@ public sealed class TranscodingJob : IDisposable /// /// Gets or sets the active request count. /// - public int ActiveRequestCount { get; set; } + public int ActiveRequestCount + { + get => Volatile.Read(ref _activeRequestCount); + set => Volatile.Write(ref _activeRequestCount, value); + } /// /// Gets or sets device id. @@ -151,6 +156,20 @@ public sealed class TranscodingJob : IDisposable /// public int PingTimeout { get; set; } + /// + /// Increments the active request count. + /// + /// The incremented count. + public int IncrementActiveRequestCount() + => Interlocked.Increment(ref _activeRequestCount); + + /// + /// Decrements the active request count. + /// + /// The decremented count. + public int DecrementActiveRequestCount() + => Interlocked.Decrement(ref _activeRequestCount); + /// /// Stop kill timer. /// -- cgit v1.2.3