diff options
Diffstat (limited to 'MediaBrowser.Api/ApiEntryPoint.cs')
| -rw-r--r-- | MediaBrowser.Api/ApiEntryPoint.cs | 244 |
1 files changed, 182 insertions, 62 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index ed5fa5bfd..55aa778e2 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Configuration; @@ -40,6 +41,7 @@ namespace MediaBrowser.Api private readonly ISessionManager _sessionManager; private readonly IFileSystem _fileSystem; + private readonly IMediaSourceManager _mediaSourceManager; public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1); @@ -49,12 +51,15 @@ namespace MediaBrowser.Api /// <param name="logger">The logger.</param> /// <param name="sessionManager">The session manager.</param> /// <param name="config">The configuration.</param> - public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem) + /// <param name="fileSystem">The file system.</param> + /// <param name="mediaSourceManager">The media source manager.</param> + public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager) { Logger = logger; _sessionManager = sessionManager; _config = config; _fileSystem = fileSystem; + _mediaSourceManager = mediaSourceManager; Instance = this; } @@ -114,7 +119,7 @@ namespace MediaBrowser.Api { var jobCount = _activeTranscodingJobs.Count; - Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, path => true)); + Parallel.ForEach(_activeTranscodingJobs.ToList(), j => KillTranscodingJob(j, false, path => true)); // Try to allow for some time to kill the ffmpeg processes and delete the partial stream files if (jobCount > 0) @@ -133,6 +138,7 @@ namespace MediaBrowser.Api /// </summary> /// <param name="path">The path.</param> /// <param name="playSessionId">The play session identifier.</param> + /// <param name="liveStreamId">The live stream identifier.</param> /// <param name="transcodingJobId">The transcoding job identifier.</param> /// <param name="type">The type.</param> /// <param name="process">The process.</param> @@ -142,6 +148,7 @@ namespace MediaBrowser.Api /// <returns>TranscodingJob.</returns> public TranscodingJob OnTranscodeBeginning(string path, string playSessionId, + string liveStreamId, string transcodingJobId, TranscodingJobType type, Process process, @@ -151,7 +158,7 @@ namespace MediaBrowser.Api { lock (_activeTranscodingJobs) { - var job = new TranscodingJob + var job = new TranscodingJob(Logger) { Type = type, Path = path, @@ -160,7 +167,8 @@ namespace MediaBrowser.Api DeviceId = deviceId, CancellationTokenSource = cancellationTokenSource, Id = transcodingJobId, - PlaySessionId = playSessionId + PlaySessionId = playSessionId, + LiveStreamId = liveStreamId }; _activeTranscodingJobs.Add(job); @@ -284,66 +292,86 @@ namespace MediaBrowser.Api { job.ActiveRequestCount++; - job.DisposeKillTimer(); + if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive) + { + job.StopKillTimer(); + } } public void OnTranscodeEndRequest(TranscodingJob job) { job.ActiveRequestCount--; - - if (job.ActiveRequestCount == 0) + Logger.Debug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount); + if (job.ActiveRequestCount <= 0) { - PingTimer(job, true); + PingTimer(job, false); } } - internal void PingTranscodingJob(string deviceId, string playSessionId) + internal void PingTranscodingJob(string playSessionId) { - if (string.IsNullOrEmpty(deviceId)) + if (string.IsNullOrEmpty(playSessionId)) { - throw new ArgumentNullException("deviceId"); + throw new ArgumentNullException("playSessionId"); } + Logger.Debug("PingTranscodingJob PlaySessionId={0}", playSessionId); + var jobs = new List<TranscodingJob>(); lock (_activeTranscodingJobs) { // This is really only needed for HLS. // Progressive streams can stop on their own reliably - jobs = jobs.Where(j => - { - if (string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)) - { - return string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase); - } - - return false; - - }).ToList(); + jobs = jobs.Where(j => string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase)).ToList(); } foreach (var job in jobs) { - PingTimer(job, false); + PingTimer(job, true); } } - private void PingTimer(TranscodingJob job, bool startTimerIfNeeded) + private async void PingTimer(TranscodingJob job, bool isProgressCheckIn) { - // TODO: Lower this hls timeout + if (job.HasExited) + { + job.StopKillTimer(); + return; + } + var timerDuration = job.Type == TranscodingJobType.Progressive ? 1000 : 1800000; - if (job.KillTimer == null) + // We can really reduce the timeout for apps that are using the newer api + if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive) { - if (startTimerIfNeeded) - { - job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite); - } + timerDuration = 60000; + } + + job.PingTimeout = timerDuration; + job.LastPingDate = DateTime.UtcNow; + + // Don't start the timer for playback checkins with progressive streaming + if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn) + { + job.StartKillTimer(OnTranscodeKillTimerStopped); } else { - job.KillTimer.Change(timerDuration, Timeout.Infinite); + job.ChangeKillTimerIfStarted(); + } + + if (!string.IsNullOrWhiteSpace(job.LiveStreamId)) + { + try + { + await _mediaSourceManager.PingLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error closing live stream", ex); + } } } @@ -355,7 +383,20 @@ namespace MediaBrowser.Api { var job = (TranscodingJob)state; - KillTranscodingJob(job, path => true); + if (!job.HasExited && job.Type != TranscodingJobType.Progressive) + { + var timeSinceLastPing = (DateTime.UtcNow - job.LastPingDate).TotalMilliseconds; + + if (timeSinceLastPing < job.PingTimeout) + { + job.StartKillTimer(OnTranscodeKillTimerStopped, job.PingTimeout); + return; + } + } + + Logger.Debug("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId); + + KillTranscodingJob(job, true, path => true); } /// <summary> @@ -367,19 +408,14 @@ namespace MediaBrowser.Api /// <returns>Task.</returns> internal void KillTranscodingJobs(string deviceId, string playSessionId, Func<string, bool> deleteFiles) { - if (string.IsNullOrEmpty(deviceId)) - { - throw new ArgumentNullException("deviceId"); - } - KillTranscodingJobs(j => { - if (string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(playSessionId)) { - return string.IsNullOrWhiteSpace(playSessionId) || string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase); + return string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase); } - return false; + return string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase); }, deleteFiles); } @@ -408,7 +444,7 @@ namespace MediaBrowser.Api foreach (var job in jobs) { - KillTranscodingJob(job, deleteFiles); + KillTranscodingJob(job, false, deleteFiles); } } @@ -416,9 +452,14 @@ namespace MediaBrowser.Api /// Kills the transcoding job. /// </summary> /// <param name="job">The job.</param> + /// <param name="closeLiveStream">if set to <c>true</c> [close live stream].</param> /// <param name="delete">The delete.</param> - private void KillTranscodingJob(TranscodingJob job, Func<string, bool> delete) + private async void KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete) { + job.DisposeKillTimer(); + + Logger.Debug("KillTranscodingJob - JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId); + lock (_activeTranscodingJobs) { _activeTranscodingJobs.Remove(job); @@ -427,34 +468,23 @@ namespace MediaBrowser.Api { job.CancellationTokenSource.Cancel(); } - - job.DisposeKillTimer(); } lock (job.ProcessLock) { - var process = job.Process; - - var hasExited = true; - - try - { - hasExited = process.HasExited; - } - catch (Exception ex) + if (job.TranscodingThrottler != null) { - Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path); + job.TranscodingThrottler.Stop(); } + var process = job.Process; + + var hasExited = job.HasExited; + if (!hasExited) { try { - if (job.TranscodingThrottler != null) - { - job.TranscodingThrottler.Stop(); - } - Logger.Info("Killing ffmpeg process for {0}", job.Path); //process.Kill(); @@ -474,6 +504,18 @@ namespace MediaBrowser.Api { DeletePartialStreamFiles(job.Path, job.Type, 0, 1500); } + + if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId)) + { + try + { + await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error closing live stream for {0}", ex, job.Path); + } + } } private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs) @@ -582,6 +624,11 @@ namespace MediaBrowser.Api /// <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 the path. /// </summary> /// <value>The path.</value> @@ -596,6 +643,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The process.</value> public Process Process { get; set; } + public ILogger Logger { get; private set; } /// <summary> /// Gets or sets the active request count. /// </summary> @@ -605,7 +653,7 @@ namespace MediaBrowser.Api /// Gets or sets the kill timer. /// </summary> /// <value>The kill timer.</value> - public Timer KillTimer { get; set; } + private Timer KillTimer { get; set; } public string DeviceId { get; set; } @@ -628,12 +676,84 @@ namespace MediaBrowser.Api public TranscodingThrottler TranscodingThrottler { get; set; } + private readonly object _timerLock = new object(); + + public DateTime LastPingDate { get; set; } + public int PingTimeout { get; set; } + + public TranscodingJob(ILogger logger) + { + Logger = logger; + } + + public void StopKillTimer() + { + lock (_timerLock) + { + if (KillTimer != null) + { + KillTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + } + public void DisposeKillTimer() { - if (KillTimer != null) + lock (_timerLock) + { + if (KillTimer != null) + { + KillTimer.Dispose(); + KillTimer = null; + } + } + } + + public void StartKillTimer(TimerCallback callback) + { + StartKillTimer(callback, PingTimeout); + } + + public void StartKillTimer(TimerCallback callback, int intervalMs) + { + CheckHasExited(); + + lock (_timerLock) + { + if (KillTimer == null) + { + Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + KillTimer = new Timer(callback, this, intervalMs, Timeout.Infinite); + } + else + { + Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + KillTimer.Change(intervalMs, Timeout.Infinite); + } + } + } + + public void ChangeKillTimerIfStarted() + { + CheckHasExited(); + + lock (_timerLock) + { + if (KillTimer != null) + { + var intervalMs = PingTimeout; + + Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + KillTimer.Change(intervalMs, Timeout.Infinite); + } + } + } + + private void CheckHasExited() + { + if (HasExited) { - KillTimer.Dispose(); - KillTimer = null; + throw new ObjectDisposedException("Job"); } } } |
