|
@@ -2,6 +2,7 @@
|
|
using MediaBrowser.Common.Configuration;
|
|
using MediaBrowser.Common.Configuration;
|
|
using MediaBrowser.Common.IO;
|
|
using MediaBrowser.Common.IO;
|
|
using MediaBrowser.Controller.Configuration;
|
|
using MediaBrowser.Controller.Configuration;
|
|
|
|
+using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Controller.Plugins;
|
|
using MediaBrowser.Controller.Plugins;
|
|
using MediaBrowser.Controller.Session;
|
|
using MediaBrowser.Controller.Session;
|
|
using MediaBrowser.Model.Configuration;
|
|
using MediaBrowser.Model.Configuration;
|
|
@@ -40,6 +41,7 @@ namespace MediaBrowser.Api
|
|
|
|
|
|
private readonly ISessionManager _sessionManager;
|
|
private readonly ISessionManager _sessionManager;
|
|
private readonly IFileSystem _fileSystem;
|
|
private readonly IFileSystem _fileSystem;
|
|
|
|
+ private readonly IMediaSourceManager _mediaSourceManager;
|
|
|
|
|
|
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
|
|
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
|
|
|
|
|
|
@@ -49,12 +51,15 @@ namespace MediaBrowser.Api
|
|
/// <param name="logger">The logger.</param>
|
|
/// <param name="logger">The logger.</param>
|
|
/// <param name="sessionManager">The session manager.</param>
|
|
/// <param name="sessionManager">The session manager.</param>
|
|
/// <param name="config">The configuration.</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;
|
|
Logger = logger;
|
|
_sessionManager = sessionManager;
|
|
_sessionManager = sessionManager;
|
|
_config = config;
|
|
_config = config;
|
|
_fileSystem = fileSystem;
|
|
_fileSystem = fileSystem;
|
|
|
|
+ _mediaSourceManager = mediaSourceManager;
|
|
|
|
|
|
Instance = this;
|
|
Instance = this;
|
|
}
|
|
}
|
|
@@ -114,7 +119,7 @@ namespace MediaBrowser.Api
|
|
{
|
|
{
|
|
var jobCount = _activeTranscodingJobs.Count;
|
|
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
|
|
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
|
|
if (jobCount > 0)
|
|
if (jobCount > 0)
|
|
@@ -133,6 +138,7 @@ namespace MediaBrowser.Api
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <param name="path">The path.</param>
|
|
/// <param name="path">The path.</param>
|
|
/// <param name="playSessionId">The play session identifier.</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="transcodingJobId">The transcoding job identifier.</param>
|
|
/// <param name="type">The type.</param>
|
|
/// <param name="type">The type.</param>
|
|
/// <param name="process">The process.</param>
|
|
/// <param name="process">The process.</param>
|
|
@@ -142,6 +148,7 @@ namespace MediaBrowser.Api
|
|
/// <returns>TranscodingJob.</returns>
|
|
/// <returns>TranscodingJob.</returns>
|
|
public TranscodingJob OnTranscodeBeginning(string path,
|
|
public TranscodingJob OnTranscodeBeginning(string path,
|
|
string playSessionId,
|
|
string playSessionId,
|
|
|
|
+ string liveStreamId,
|
|
string transcodingJobId,
|
|
string transcodingJobId,
|
|
TranscodingJobType type,
|
|
TranscodingJobType type,
|
|
Process process,
|
|
Process process,
|
|
@@ -160,7 +167,8 @@ namespace MediaBrowser.Api
|
|
DeviceId = deviceId,
|
|
DeviceId = deviceId,
|
|
CancellationTokenSource = cancellationTokenSource,
|
|
CancellationTokenSource = cancellationTokenSource,
|
|
Id = transcodingJobId,
|
|
Id = transcodingJobId,
|
|
- PlaySessionId = playSessionId
|
|
|
|
|
|
+ PlaySessionId = playSessionId,
|
|
|
|
+ LiveStreamId = liveStreamId
|
|
};
|
|
};
|
|
|
|
|
|
_activeTranscodingJobs.Add(job);
|
|
_activeTranscodingJobs.Add(job);
|
|
@@ -323,7 +331,7 @@ namespace MediaBrowser.Api
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private void PingTimer(TranscodingJob job, bool isProgressCheckIn)
|
|
|
|
|
|
+ private async void PingTimer(TranscodingJob job, bool isProgressCheckIn)
|
|
{
|
|
{
|
|
if (job.HasExited)
|
|
if (job.HasExited)
|
|
{
|
|
{
|
|
@@ -331,7 +339,6 @@ namespace MediaBrowser.Api
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- // TODO: Lower this hls timeout
|
|
|
|
var timerDuration = job.Type == TranscodingJobType.Progressive ?
|
|
var timerDuration = job.Type == TranscodingJobType.Progressive ?
|
|
1000 :
|
|
1000 :
|
|
1800000;
|
|
1800000;
|
|
@@ -339,17 +346,32 @@ namespace MediaBrowser.Api
|
|
// We can really reduce the timeout for apps that are using the newer api
|
|
// We can really reduce the timeout for apps that are using the newer api
|
|
if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive)
|
|
if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive)
|
|
{
|
|
{
|
|
- timerDuration = 20000;
|
|
|
|
|
|
+ timerDuration = 60000;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ job.PingTimeout = timerDuration;
|
|
|
|
+ job.LastPingDate = DateTime.UtcNow;
|
|
|
|
+
|
|
// Don't start the timer for playback checkins with progressive streaming
|
|
// Don't start the timer for playback checkins with progressive streaming
|
|
if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
|
|
if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
|
|
{
|
|
{
|
|
- job.StartKillTimer(timerDuration, OnTranscodeKillTimerStopped);
|
|
|
|
|
|
+ job.StartKillTimer(OnTranscodeKillTimerStopped);
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- job.ChangeKillTimerIfStarted(timerDuration);
|
|
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -361,9 +383,20 @@ namespace MediaBrowser.Api
|
|
{
|
|
{
|
|
var job = (TranscodingJob)state;
|
|
var job = (TranscodingJob)state;
|
|
|
|
|
|
|
|
+ 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);
|
|
Logger.Debug("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
|
|
|
|
|
|
- KillTranscodingJob(job, path => true);
|
|
|
|
|
|
+ KillTranscodingJob(job, true, path => true);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -411,7 +444,7 @@ namespace MediaBrowser.Api
|
|
|
|
|
|
foreach (var job in jobs)
|
|
foreach (var job in jobs)
|
|
{
|
|
{
|
|
- KillTranscodingJob(job, deleteFiles);
|
|
|
|
|
|
+ KillTranscodingJob(job, false, deleteFiles);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -419,8 +452,9 @@ namespace MediaBrowser.Api
|
|
/// Kills the transcoding job.
|
|
/// Kills the transcoding job.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <param name="job">The job.</param>
|
|
/// <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>
|
|
/// <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();
|
|
job.DisposeKillTimer();
|
|
|
|
|
|
@@ -470,6 +504,18 @@ namespace MediaBrowser.Api
|
|
{
|
|
{
|
|
DeletePartialStreamFiles(job.Path, job.Type, 0, 1500);
|
|
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)
|
|
private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
|
|
@@ -578,6 +624,11 @@ namespace MediaBrowser.Api
|
|
/// <value>The play session identifier.</value>
|
|
/// <value>The play session identifier.</value>
|
|
public string PlaySessionId { get; set; }
|
|
public string PlaySessionId { get; set; }
|
|
/// <summary>
|
|
/// <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.
|
|
/// Gets or sets the path.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <value>The path.</value>
|
|
/// <value>The path.</value>
|
|
@@ -627,6 +678,9 @@ namespace MediaBrowser.Api
|
|
|
|
|
|
private readonly object _timerLock = new object();
|
|
private readonly object _timerLock = new object();
|
|
|
|
|
|
|
|
+ public DateTime LastPingDate { get; set; }
|
|
|
|
+ public int PingTimeout { get; set; }
|
|
|
|
+
|
|
public TranscodingJob(ILogger logger)
|
|
public TranscodingJob(ILogger logger)
|
|
{
|
|
{
|
|
Logger = logger;
|
|
Logger = logger;
|
|
@@ -655,7 +709,12 @@ namespace MediaBrowser.Api
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- public void StartKillTimer(int intervalMs, TimerCallback callback)
|
|
|
|
|
|
+ public void StartKillTimer(TimerCallback callback)
|
|
|
|
+ {
|
|
|
|
+ StartKillTimer(callback, PingTimeout);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void StartKillTimer(TimerCallback callback, int intervalMs)
|
|
{
|
|
{
|
|
CheckHasExited();
|
|
CheckHasExited();
|
|
|
|
|
|
@@ -674,7 +733,7 @@ namespace MediaBrowser.Api
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- public void ChangeKillTimerIfStarted(int intervalMs)
|
|
|
|
|
|
+ public void ChangeKillTimerIfStarted()
|
|
{
|
|
{
|
|
CheckHasExited();
|
|
CheckHasExited();
|
|
|
|
|
|
@@ -682,6 +741,8 @@ namespace MediaBrowser.Api
|
|
{
|
|
{
|
|
if (KillTimer != null)
|
|
if (KillTimer != null)
|
|
{
|
|
{
|
|
|
|
+ var intervalMs = PingTimeout;
|
|
|
|
+
|
|
Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
|
|
Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
|
|
KillTimer.Change(intervalMs, Timeout.Infinite);
|
|
KillTimer.Change(intervalMs, Timeout.Infinite);
|
|
}
|
|
}
|