using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Models.PlaybackDtos { /// <summary> /// Class TranscodingJob. /// </summary> public class TranscodingJobDto : IDisposable { /// <summary> /// The process lock. /// </summary> [SuppressMessage("Microsoft.Performance", "CA1051:NoVisibleInstanceFields", MessageId = "ProcessLock", Justification = "Imported from ServiceStack")] [SuppressMessage("Microsoft.Performance", "SA1401:PrivateField", MessageId = "ProcessLock", Justification = "Imported from ServiceStack")] public readonly object ProcessLock = new object(); /// <summary> /// Timer lock. /// </summary> private readonly object _timerLock = new object(); /// <summary> /// Initializes a new instance of the <see cref="TranscodingJobDto"/> class. /// </summary> /// <param name="logger">Instance of the <see cref="ILogger{TranscodingJobDto}"/> interface.</param> public TranscodingJobDto(ILogger<TranscodingJobDto> logger) { Logger = logger; } /// <summary> /// Gets or sets the play session identifier. /// </summary> /// <value>The play session identifier.</value> public string? PlaySessionId { get; set; } /// <summary> /// Gets or sets the live stream identifier. /// </summary> /// <value>The live stream identifier.</value> public string? LiveStreamId { get; set; } /// <summary> /// Gets or sets a value indicating whether is live output. /// </summary> public bool IsLiveOutput { get; set; } /// <summary> /// Gets or sets the path. /// </summary> /// <value>The path.</value> public MediaSourceInfo? MediaSource { get; set; } /// <summary> /// Gets or sets path. /// </summary> public string? Path { get; set; } /// <summary> /// Gets or sets the type. /// </summary> /// <value>The type.</value> public TranscodingJobType Type { get; set; } /// <summary> /// Gets or sets the process. /// </summary> /// <value>The process.</value> public Process? Process { get; set; } /// <summary> /// Gets logger. /// </summary> public ILogger<TranscodingJobDto> Logger { get; private set; } /// <summary> /// Gets or sets the active request count. /// </summary> /// <value>The active request count.</value> public int ActiveRequestCount { get; set; } /// <summary> /// Gets or sets the kill timer. /// </summary> /// <value>The kill timer.</value> private Timer? KillTimer { get; set; } /// <summary> /// Gets or sets device id. /// </summary> public string? DeviceId { get; set; } /// <summary> /// Gets or sets cancellation token source. /// </summary> public CancellationTokenSource? CancellationTokenSource { get; set; } /// <summary> /// Gets or sets a value indicating whether has exited. /// </summary> public bool HasExited { get; set; } /// <summary> /// Gets or sets exit code. /// </summary> public int ExitCode { get; set; } /// <summary> /// Gets or sets a value indicating whether is user paused. /// </summary> public bool IsUserPaused { get; set; } /// <summary> /// Gets or sets id. /// </summary> public string? Id { get; set; } /// <summary> /// Gets or sets framerate. /// </summary> public float? Framerate { get; set; } /// <summary> /// Gets or sets completion percentage. /// </summary> public double? CompletionPercentage { get; set; } /// <summary> /// Gets or sets bytes downloaded. /// </summary> public long BytesDownloaded { get; set; } /// <summary> /// Gets or sets bytes transcoded. /// </summary> public long? BytesTranscoded { get; set; } /// <summary> /// Gets or sets bit rate. /// </summary> public int? BitRate { get; set; } /// <summary> /// Gets or sets transcoding position ticks. /// </summary> public long? TranscodingPositionTicks { get; set; } /// <summary> /// Gets or sets download position ticks. /// </summary> public long? DownloadPositionTicks { get; set; } /// <summary> /// Gets or sets transcoding throttler. /// </summary> public TranscodingThrottler? TranscodingThrottler { get; set; } /// <summary> /// Gets or sets last ping date. /// </summary> public DateTime LastPingDate { get; set; } /// <summary> /// Gets or sets ping timeout. /// </summary> public int PingTimeout { get; set; } /// <summary> /// Stop kill timer. /// </summary> public void StopKillTimer() { lock (_timerLock) { KillTimer?.Change(Timeout.Infinite, Timeout.Infinite); } } /// <summary> /// Dispose kill timer. /// </summary> public void DisposeKillTimer() { lock (_timerLock) { if (KillTimer != null) { KillTimer.Dispose(); KillTimer = null; } } } /// <summary> /// Start kill timer. /// </summary> /// <param name="callback">Callback action.</param> public void StartKillTimer(Action<object?> callback) { StartKillTimer(callback, PingTimeout); } /// <summary> /// Start kill timer. /// </summary> /// <param name="callback">Callback action.</param> /// <param name="intervalMs">Callback interval.</param> public void StartKillTimer(Action<object?> callback, int intervalMs) { if (HasExited) { return; } lock (_timerLock) { if (KillTimer == null) { Logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite); } else { Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer.Change(intervalMs, Timeout.Infinite); } } } /// <summary> /// Change kill timer if started. /// </summary> public void ChangeKillTimerIfStarted() { if (HasExited) { return; } lock (_timerLock) { if (KillTimer != null) { var intervalMs = PingTimeout; Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer.Change(intervalMs, Timeout.Infinite); } } } /// <inheritdoc /> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Dispose all resources. /// </summary> /// <param name="disposing">Whether to dispose all resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { Process?.Dispose(); Process = null; KillTimer?.Dispose(); KillTimer = null; CancellationTokenSource?.Dispose(); CancellationTokenSource = null; TranscodingThrottler?.Dispose(); TranscodingThrottler = null; } } } }