< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.MediaEncoding.TranscodingJob
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs
Line coverage
12%
Covered lines: 8
Uncovered lines: 55
Coverable lines: 63
Total lines: 307
Line coverage: 12.6%
Branch coverage
0%
Covered branches: 0
Total branches: 30
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/1/2026 - 12:13:05 AM Line coverage: 0% (0/59) Branch coverage: 0% (0/30) Total lines: 2888/9/2026 - 12:16:58 AM Line coverage: 12.6% (8/63) Branch coverage: 0% (0/30) Total lines: 307 5/1/2026 - 12:13:05 AM Line coverage: 0% (0/59) Branch coverage: 0% (0/30) Total lines: 2888/9/2026 - 12:16:58 AM Line coverage: 12.6% (8/63) Branch coverage: 0% (0/30) Total lines: 307

Coverage delta

Coverage delta 13 -13

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_ActiveRequestCount()100%11100%
set_ActiveRequestCount(...)100%11100%
IncrementActiveRequestCount()100%11100%
DecrementActiveRequestCount()100%11100%
StopKillTimer()0%620%
DisposeKillTimer()0%620%
StartKillTimer(...)100%210%
StartKillTimer(...)0%2040%
ChangeKillTimerIfStarted()0%2040%
Stop()0%7280%
Dispose()0%110100%

File(s)

/srv/git/jellyfin/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs

#LineLine coverage
 1using System;
 2using System.Diagnostics;
 3using System.Threading;
 4using MediaBrowser.Model.Dto;
 5using Microsoft.Extensions.Logging;
 6
 7namespace MediaBrowser.Controller.MediaEncoding;
 8
 9/// <summary>
 10/// Class TranscodingJob.
 11/// </summary>
 12public sealed class TranscodingJob : IDisposable
 13{
 14    private readonly ILogger<TranscodingJob> _logger;
 515    private readonly Lock _processLock = new();
 516    private readonly Lock _timerLock = new();
 17
 18    private int _activeRequestCount;
 19    private Timer? _killTimer;
 20
 21    /// <summary>
 22    /// Initializes a new instance of the <see cref="TranscodingJob"/> class.
 23    /// </summary>
 24    /// <param name="logger">Instance of the <see cref="ILogger{TranscodingJobDto}"/> interface.</param>
 25    public TranscodingJob(ILogger<TranscodingJob> logger)
 26    {
 527        _logger = logger;
 528    }
 29
 30    /// <summary>
 31    /// Gets or sets the play session identifier.
 32    /// </summary>
 33    public string? PlaySessionId { get; set; }
 34
 35    /// <summary>
 36    /// Gets or sets the live stream identifier.
 37    /// </summary>
 38    public string? LiveStreamId { get; set; }
 39
 40    /// <summary>
 41    /// Gets or sets a value indicating whether is live output.
 42    /// </summary>
 43    public bool IsLiveOutput { get; set; }
 44
 45    /// <summary>
 46    /// Gets or sets the path.
 47    /// </summary>
 48    public MediaSourceInfo? MediaSource { get; set; }
 49
 50    /// <summary>
 51    /// Gets or sets path.
 52    /// </summary>
 53    public string? Path { get; set; }
 54
 55    /// <summary>
 56    /// Gets or sets the type.
 57    /// </summary>
 58    public TranscodingJobType Type { get; set; }
 59
 60    /// <summary>
 61    /// Gets or sets the process.
 62    /// </summary>
 63    public Process? Process { get; set; }
 64
 65    /// <summary>
 66    /// Gets or sets the active request count.
 67    /// </summary>
 68    public int ActiveRequestCount
 69    {
 870        get => Volatile.Read(ref _activeRequestCount);
 371        set => Volatile.Write(ref _activeRequestCount, value);
 72    }
 73
 74    /// <summary>
 75    /// Gets or sets device id.
 76    /// </summary>
 77    public string? DeviceId { get; set; }
 78
 79    /// <summary>
 80    /// Gets or sets cancellation token source.
 81    /// </summary>
 82    public CancellationTokenSource? CancellationTokenSource { get; set; }
 83
 84    /// <summary>
 85    /// Gets or sets a value indicating whether has exited.
 86    /// </summary>
 87    public bool HasExited { get; set; }
 88
 89    /// <summary>
 90    /// Gets or sets exit code.
 91    /// </summary>
 92    public int ExitCode { get; set; }
 93
 94    /// <summary>
 95    /// Gets or sets a value indicating whether is user paused.
 96    /// </summary>
 97    public bool IsUserPaused { get; set; }
 98
 99    /// <summary>
 100    /// Gets or sets id.
 101    /// </summary>
 102    public string? Id { get; set; }
 103
 104    /// <summary>
 105    /// Gets or sets framerate.
 106    /// </summary>
 107    public float? Framerate { get; set; }
 108
 109    /// <summary>
 110    /// Gets or sets completion percentage.
 111    /// </summary>
 112    public double? CompletionPercentage { get; set; }
 113
 114    /// <summary>
 115    /// Gets or sets bytes downloaded.
 116    /// </summary>
 117    public long BytesDownloaded { get; set; }
 118
 119    /// <summary>
 120    /// Gets or sets bytes transcoded.
 121    /// </summary>
 122    public long? BytesTranscoded { get; set; }
 123
 124    /// <summary>
 125    /// Gets or sets bit rate.
 126    /// </summary>
 127    public int? BitRate { get; set; }
 128
 129    /// <summary>
 130    /// Gets or sets transcoding position ticks.
 131    /// </summary>
 132    public long? TranscodingPositionTicks { get; set; }
 133
 134    /// <summary>
 135    /// Gets or sets download position ticks.
 136    /// </summary>
 137    public long? DownloadPositionTicks { get; set; }
 138
 139    /// <summary>
 140    /// Gets or sets transcoding throttler.
 141    /// </summary>
 142    public TranscodingThrottler? TranscodingThrottler { get; set; }
 143
 144    /// <summary>
 145    /// Gets or sets transcoding segment cleaner.
 146    /// </summary>
 147    public TranscodingSegmentCleaner? TranscodingSegmentCleaner { get; set; }
 148
 149    /// <summary>
 150    /// Gets or sets last ping date.
 151    /// </summary>
 152    public DateTime LastPingDate { get; set; }
 153
 154    /// <summary>
 155    /// Gets or sets ping timeout.
 156    /// </summary>
 157    public int PingTimeout { get; set; }
 158
 159    /// <summary>
 160    /// Increments the active request count.
 161    /// </summary>
 162    /// <returns>The incremented count.</returns>
 163    public int IncrementActiveRequestCount()
 1000164        => Interlocked.Increment(ref _activeRequestCount);
 165
 166    /// <summary>
 167    /// Decrements the active request count.
 168    /// </summary>
 169    /// <returns>The decremented count.</returns>
 170    public int DecrementActiveRequestCount()
 1003171        => Interlocked.Decrement(ref _activeRequestCount);
 172
 173    /// <summary>
 174    /// Stop kill timer.
 175    /// </summary>
 176    public void StopKillTimer()
 0177    {
 178        lock (_timerLock)
 179        {
 0180            _killTimer?.Change(Timeout.Infinite, Timeout.Infinite);
 0181        }
 0182    }
 183
 184    /// <summary>
 185    /// Dispose kill timer.
 186    /// </summary>
 187    public void DisposeKillTimer()
 0188    {
 189        lock (_timerLock)
 190        {
 0191            if (_killTimer is not null)
 192            {
 0193                _killTimer.Dispose();
 0194                _killTimer = null;
 195            }
 0196        }
 0197    }
 198
 199    /// <summary>
 200    /// Start kill timer.
 201    /// </summary>
 202    /// <param name="callback">Callback action.</param>
 203    public void StartKillTimer(Action<object?> callback)
 204    {
 0205        StartKillTimer(callback, PingTimeout);
 0206    }
 207
 208    /// <summary>
 209    /// Start kill timer.
 210    /// </summary>
 211    /// <param name="callback">Callback action.</param>
 212    /// <param name="intervalMs">Callback interval.</param>
 213    public void StartKillTimer(Action<object?> callback, int intervalMs)
 214    {
 0215        if (HasExited)
 216        {
 0217            return;
 218        }
 219
 220        lock (_timerLock)
 221        {
 0222            if (_killTimer is null)
 223            {
 0224                _logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessio
 0225                _killTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite);
 226            }
 227            else
 228            {
 0229                _logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessio
 0230                _killTimer.Change(intervalMs, Timeout.Infinite);
 231            }
 0232        }
 0233    }
 234
 235    /// <summary>
 236    /// Change kill timer if started.
 237    /// </summary>
 238    public void ChangeKillTimerIfStarted()
 239    {
 0240        if (HasExited)
 241        {
 0242            return;
 243        }
 244
 245        lock (_timerLock)
 246        {
 0247            if (_killTimer is not null)
 248            {
 0249                var intervalMs = PingTimeout;
 250
 0251                _logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessio
 0252                _killTimer.Change(intervalMs, Timeout.Infinite);
 253            }
 0254        }
 0255    }
 256
 257    /// <summary>
 258    /// Stops the transcoding job.
 259    /// </summary>
 260    public void Stop()
 0261    {
 262        lock (_processLock)
 263        {
 264#pragma warning disable CA1849 // Can't await in lock block
 0265            TranscodingThrottler?.Stop().GetAwaiter().GetResult();
 0266            TranscodingSegmentCleaner?.Stop();
 267
 0268            var process = Process;
 269
 0270            if (!HasExited)
 271            {
 272                try
 273                {
 0274                    _logger.LogInformation("Stopping ffmpeg process with q command for {Path}", Path);
 275
 0276                    process!.StandardInput.WriteLine("q");
 277
 278                    // Need to wait because killing is asynchronous.
 0279                    if (!process.WaitForExit(5000))
 280                    {
 0281                        _logger.LogInformation("Killing FFmpeg process for {Path}", Path);
 0282                        process.Kill();
 283                    }
 0284                }
 0285                catch (InvalidOperationException)
 286                {
 0287                }
 288            }
 289#pragma warning restore CA1849
 0290        }
 0291    }
 292
 293    /// <inheritdoc />
 294    public void Dispose()
 295    {
 0296        Process?.Dispose();
 0297        Process = null;
 0298        _killTimer?.Dispose();
 0299        _killTimer = null;
 0300        CancellationTokenSource?.Dispose();
 0301        CancellationTokenSource = null;
 0302        TranscodingThrottler?.Dispose();
 0303        TranscodingThrottler = null;
 0304        TranscodingSegmentCleaner?.Dispose();
 0305        TranscodingSegmentCleaner = null;
 0306    }
 307}