Fix concurrent ffmpeg segment racing

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.
This commit is contained in:
gnattu
2026-08-05 00:33:42 +08:00
parent 7fbc1ff8c0
commit e2586eed9b
4 changed files with 119 additions and 19 deletions
@@ -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;
/// <summary>
@@ -64,7 +65,11 @@ public sealed class TranscodingJob : IDisposable
/// <summary>
/// Gets or sets the active request count.
/// </summary>
public int ActiveRequestCount { get; set; }
public int ActiveRequestCount
{
get => Volatile.Read(ref _activeRequestCount);
set => Volatile.Write(ref _activeRequestCount, value);
}
/// <summary>
/// Gets or sets device id.
@@ -151,6 +156,20 @@ public sealed class TranscodingJob : IDisposable
/// </summary>
public int PingTimeout { get; set; }
/// <summary>
/// Increments the active request count.
/// </summary>
/// <returns>The incremented count.</returns>
public int IncrementActiveRequestCount()
=> Interlocked.Increment(ref _activeRequestCount);
/// <summary>
/// Decrements the active request count.
/// </summary>
/// <returns>The decremented count.</returns>
public int DecrementActiveRequestCount()
=> Interlocked.Decrement(ref _activeRequestCount);
/// <summary>
/// Stop kill timer.
/// </summary>