< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.MediaEncoding.TranscodingJob
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 61
Coverable lines: 61
Total lines: 288
Line coverage: 0%
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

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
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;
 015    private readonly object _processLock = new();
 016    private readonly object _timerLock = new();
 17
 18    private Timer? _killTimer;
 19
 20    /// <summary>
 21    /// Initializes a new instance of the <see cref="TranscodingJob"/> class.
 22    /// </summary>
 23    /// <param name="logger">Instance of the <see cref="ILogger{TranscodingJobDto}"/> interface.</param>
 24    public TranscodingJob(ILogger<TranscodingJob> logger)
 25    {
 026        _logger = logger;
 027    }
 28
 29    /// <summary>
 30    /// Gets or sets the play session identifier.
 31    /// </summary>
 32    public string? PlaySessionId { get; set; }
 33
 34    /// <summary>
 35    /// Gets or sets the live stream identifier.
 36    /// </summary>
 37    public string? LiveStreamId { get; set; }
 38
 39    /// <summary>
 40    /// Gets or sets a value indicating whether is live output.
 41    /// </summary>
 42    public bool IsLiveOutput { get; set; }
 43
 44    /// <summary>
 45    /// Gets or sets the path.
 46    /// </summary>
 47    public MediaSourceInfo? MediaSource { get; set; }
 48
 49    /// <summary>
 50    /// Gets or sets path.
 51    /// </summary>
 52    public string? Path { get; set; }
 53
 54    /// <summary>
 55    /// Gets or sets the type.
 56    /// </summary>
 57    public TranscodingJobType Type { get; set; }
 58
 59    /// <summary>
 60    /// Gets or sets the process.
 61    /// </summary>
 62    public Process? Process { get; set; }
 63
 64    /// <summary>
 65    /// Gets or sets the active request count.
 66    /// </summary>
 67    public int ActiveRequestCount { get; set; }
 68
 69    /// <summary>
 70    /// Gets or sets device id.
 71    /// </summary>
 72    public string? DeviceId { get; set; }
 73
 74    /// <summary>
 75    /// Gets or sets cancellation token source.
 76    /// </summary>
 77    public CancellationTokenSource? CancellationTokenSource { get; set; }
 78
 79    /// <summary>
 80    /// Gets or sets a value indicating whether has exited.
 81    /// </summary>
 82    public bool HasExited { get; set; }
 83
 84    /// <summary>
 85    /// Gets or sets exit code.
 86    /// </summary>
 87    public int ExitCode { get; set; }
 88
 89    /// <summary>
 90    /// Gets or sets a value indicating whether is user paused.
 91    /// </summary>
 92    public bool IsUserPaused { get; set; }
 93
 94    /// <summary>
 95    /// Gets or sets id.
 96    /// </summary>
 97    public string? Id { get; set; }
 98
 99    /// <summary>
 100    /// Gets or sets framerate.
 101    /// </summary>
 102    public float? Framerate { get; set; }
 103
 104    /// <summary>
 105    /// Gets or sets completion percentage.
 106    /// </summary>
 107    public double? CompletionPercentage { get; set; }
 108
 109    /// <summary>
 110    /// Gets or sets bytes downloaded.
 111    /// </summary>
 112    public long BytesDownloaded { get; set; }
 113
 114    /// <summary>
 115    /// Gets or sets bytes transcoded.
 116    /// </summary>
 117    public long? BytesTranscoded { get; set; }
 118
 119    /// <summary>
 120    /// Gets or sets bit rate.
 121    /// </summary>
 122    public int? BitRate { get; set; }
 123
 124    /// <summary>
 125    /// Gets or sets transcoding position ticks.
 126    /// </summary>
 127    public long? TranscodingPositionTicks { get; set; }
 128
 129    /// <summary>
 130    /// Gets or sets download position ticks.
 131    /// </summary>
 132    public long? DownloadPositionTicks { get; set; }
 133
 134    /// <summary>
 135    /// Gets or sets transcoding throttler.
 136    /// </summary>
 137    public TranscodingThrottler? TranscodingThrottler { get; set; }
 138
 139    /// <summary>
 140    /// Gets or sets transcoding segment cleaner.
 141    /// </summary>
 142    public TranscodingSegmentCleaner? TranscodingSegmentCleaner { get; set; }
 143
 144    /// <summary>
 145    /// Gets or sets last ping date.
 146    /// </summary>
 147    public DateTime LastPingDate { get; set; }
 148
 149    /// <summary>
 150    /// Gets or sets ping timeout.
 151    /// </summary>
 152    public int PingTimeout { get; set; }
 153
 154    /// <summary>
 155    /// Stop kill timer.
 156    /// </summary>
 157    public void StopKillTimer()
 158    {
 0159        lock (_timerLock)
 160        {
 0161            _killTimer?.Change(Timeout.Infinite, Timeout.Infinite);
 0162        }
 0163    }
 164
 165    /// <summary>
 166    /// Dispose kill timer.
 167    /// </summary>
 168    public void DisposeKillTimer()
 169    {
 0170        lock (_timerLock)
 171        {
 0172            if (_killTimer is not null)
 173            {
 0174                _killTimer.Dispose();
 0175                _killTimer = null;
 176            }
 0177        }
 0178    }
 179
 180    /// <summary>
 181    /// Start kill timer.
 182    /// </summary>
 183    /// <param name="callback">Callback action.</param>
 184    public void StartKillTimer(Action<object?> callback)
 185    {
 0186        StartKillTimer(callback, PingTimeout);
 0187    }
 188
 189    /// <summary>
 190    /// Start kill timer.
 191    /// </summary>
 192    /// <param name="callback">Callback action.</param>
 193    /// <param name="intervalMs">Callback interval.</param>
 194    public void StartKillTimer(Action<object?> callback, int intervalMs)
 195    {
 0196        if (HasExited)
 197        {
 0198            return;
 199        }
 200
 0201        lock (_timerLock)
 202        {
 0203            if (_killTimer is null)
 204            {
 0205                _logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessio
 0206                _killTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite);
 207            }
 208            else
 209            {
 0210                _logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessio
 0211                _killTimer.Change(intervalMs, Timeout.Infinite);
 212            }
 0213        }
 0214    }
 215
 216    /// <summary>
 217    /// Change kill timer if started.
 218    /// </summary>
 219    public void ChangeKillTimerIfStarted()
 220    {
 0221        if (HasExited)
 222        {
 0223            return;
 224        }
 225
 0226        lock (_timerLock)
 227        {
 0228            if (_killTimer is not null)
 229            {
 0230                var intervalMs = PingTimeout;
 231
 0232                _logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessio
 0233                _killTimer.Change(intervalMs, Timeout.Infinite);
 234            }
 0235        }
 0236    }
 237
 238    /// <summary>
 239    /// Stops the transcoding job.
 240    /// </summary>
 241    public void Stop()
 242    {
 0243        lock (_processLock)
 244        {
 245#pragma warning disable CA1849 // Can't await in lock block
 0246            TranscodingThrottler?.Stop().GetAwaiter().GetResult();
 0247            TranscodingSegmentCleaner?.Stop();
 248
 0249            var process = Process;
 250
 0251            if (!HasExited)
 252            {
 253                try
 254                {
 0255                    _logger.LogInformation("Stopping ffmpeg process with q command for {Path}", Path);
 256
 0257                    process!.StandardInput.WriteLine("q");
 258
 259                    // Need to wait because killing is asynchronous.
 0260                    if (!process.WaitForExit(5000))
 261                    {
 0262                        _logger.LogInformation("Killing FFmpeg process for {Path}", Path);
 0263                        process.Kill();
 264                    }
 0265                }
 0266                catch (InvalidOperationException)
 267                {
 0268                }
 269            }
 270#pragma warning restore CA1849
 0271        }
 0272    }
 273
 274    /// <inheritdoc />
 275    public void Dispose()
 276    {
 0277        Process?.Dispose();
 0278        Process = null;
 0279        _killTimer?.Dispose();
 0280        _killTimer = null;
 0281        CancellationTokenSource?.Dispose();
 0282        CancellationTokenSource = null;
 0283        TranscodingThrottler?.Dispose();
 0284        TranscodingThrottler = null;
 0285        TranscodingSegmentCleaner?.Dispose();
 0286        TranscodingSegmentCleaner = null;
 0287    }
 288}