aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs132
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs24
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs7
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs40
4 files changed, 158 insertions, 45 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index ba51f4f8c..e91dd1a9b 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -151,7 +151,7 @@ namespace MediaBrowser.Api
{
lock (_activeTranscodingJobs)
{
- var job = new TranscodingJob
+ var job = new TranscodingJob(Logger)
{
Type = type,
Path = path,
@@ -286,7 +286,7 @@ namespace MediaBrowser.Api
if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive)
{
- job.DisposeKillTimer();
+ job.StopKillTimer();
}
}
@@ -299,29 +299,22 @@ namespace MediaBrowser.Api
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)
@@ -332,6 +325,12 @@ namespace MediaBrowser.Api
private void PingTimer(TranscodingJob job, bool isProgressCheckIn)
{
+ if (job.HasExited)
+ {
+ job.StopKillTimer();
+ return;
+ }
+
// TODO: Lower this hls timeout
var timerDuration = job.Type == TranscodingJobType.Progressive ?
1000 :
@@ -343,19 +342,14 @@ namespace MediaBrowser.Api
timerDuration = 20000;
}
- if (job.KillTimer == null)
+ // Don't start the timer for playback checkins with progressive streaming
+ if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
{
- // Don't start the timer for playback checkins with progressive streaming
- if (job.Type != TranscodingJobType.Progressive || !isProgressCheckIn)
- {
- Logger.Debug("Starting kill timer at {0}ms", timerDuration);
- job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite);
- }
+ job.StartKillTimer(timerDuration, OnTranscodeKillTimerStopped);
}
else
{
- Logger.Debug("Changing kill timer to {0}ms", timerDuration);
- job.KillTimer.Change(timerDuration, Timeout.Infinite);
+ job.ChangeKillTimerIfStarted(timerDuration);
}
}
@@ -367,6 +361,8 @@ namespace MediaBrowser.Api
{
var job = (TranscodingJob)state;
+ Logger.Debug("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
+
KillTranscodingJob(job, path => true);
}
@@ -379,19 +375,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);
}
@@ -431,6 +422,10 @@ namespace MediaBrowser.Api
/// <param name="delete">The delete.</param>
private void KillTranscodingJob(TranscodingJob job, Func<string, bool> delete)
{
+ job.DisposeKillTimer();
+
+ Logger.Debug("KillTranscodingJob - JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
+
lock (_activeTranscodingJobs)
{
_activeTranscodingJobs.Remove(job);
@@ -439,8 +434,6 @@ namespace MediaBrowser.Api
{
job.CancellationTokenSource.Cancel();
}
-
- job.DisposeKillTimer();
}
lock (job.ProcessLock)
@@ -599,6 +592,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>
@@ -608,7 +602,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; }
@@ -631,12 +625,74 @@ namespace MediaBrowser.Api
public TranscodingThrottler TranscodingThrottler { get; set; }
+ private readonly object _timerLock = new object();
+
+ 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(int intervalMs, TimerCallback callback)
+ {
+ 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(int intervalMs)
+ {
+ CheckHasExited();
+
+ lock (_timerLock)
+ {
+ if (KillTimer != null)
+ {
+ 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");
}
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 455113da9..c06bbe143 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -127,9 +127,27 @@ namespace MediaBrowser.Api.Playback.Hls
}
else
{
+ var startTranscoding = false;
+
var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
- var segmentGapRequiringTranscodingChange = 24/state.SegmentLength;
- if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
+ var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
+
+ if (currentTranscodingIndex == null)
+ {
+ Logger.Debug("Starting transcoding because currentTranscodingIndex=null");
+ startTranscoding = true;
+ }
+ else if (requestedIndex < currentTranscodingIndex.Value)
+ {
+ Logger.Debug("Starting transcoding because requestedIndex={0} and currentTranscodingIndex={1}", requestedIndex, currentTranscodingIndex);
+ startTranscoding = true;
+ }
+ else if ((requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
+ {
+ Logger.Debug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", (requestedIndex - currentTranscodingIndex.Value), segmentGapRequiringTranscodingChange, requestedIndex);
+ startTranscoding = true;
+ }
+ if (startTranscoding)
{
// If the playlist doesn't already exist, startup ffmpeg
try
@@ -151,7 +169,7 @@ namespace MediaBrowser.Api.Playback.Hls
throw;
}
- await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
+ //await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
}
else
{
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 4a51f8644..6a3443f35 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -63,6 +63,13 @@ namespace MediaBrowser.Api.Playback.Progressive
new ProgressiveFileCopier(_fileSystem, _job)
.StreamFile(Path, responseStream);
}
+ catch (IOException)
+ {
+ // These error are always the same so don't dump the whole stack trace
+ Logger.Error("Error streaming media. The client has most likely disconnected or transcoding has failed.");
+
+ throw;
+ }
catch (Exception ex)
{
Logger.ErrorException("Error streaming media. The client has most likely disconnected or transcoding has failed.", ex);
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index 6c767596e..4661abf4c 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -114,6 +114,15 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "SubtitleStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
public int? SubtitleStreamIndex { get; set; }
+
+ [ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public PlayMethod PlayMethod { get; set; }
+
+ [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string LiveStreamId { get; set; }
+
+ [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string PlaySessionId { get; set; }
}
/// <summary>
@@ -160,6 +169,15 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "VolumeLevel", Description = "Scale of 0-100", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
public int? VolumeLevel { get; set; }
+
+ [ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public PlayMethod PlayMethod { get; set; }
+
+ [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string LiveStreamId { get; set; }
+
+ [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string PlaySessionId { get; set; }
}
/// <summary>
@@ -191,6 +209,12 @@ namespace MediaBrowser.Api.UserLibrary
/// <value>The position ticks.</value>
[ApiMember(Name = "PositionTicks", Description = "Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "DELETE")]
public long? PositionTicks { get; set; }
+
+ [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string LiveStreamId { get; set; }
+
+ [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string PlaySessionId { get; set; }
}
[Authenticated]
@@ -260,7 +284,10 @@ namespace MediaBrowser.Api.UserLibrary
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList(),
MediaSourceId = request.MediaSourceId,
AudioStreamIndex = request.AudioStreamIndex,
- SubtitleStreamIndex = request.SubtitleStreamIndex
+ SubtitleStreamIndex = request.SubtitleStreamIndex,
+ PlayMethod = request.PlayMethod,
+ PlaySessionId = request.PlaySessionId,
+ LiveStreamId = request.LiveStreamId
});
}
@@ -288,7 +315,10 @@ namespace MediaBrowser.Api.UserLibrary
MediaSourceId = request.MediaSourceId,
AudioStreamIndex = request.AudioStreamIndex,
SubtitleStreamIndex = request.SubtitleStreamIndex,
- VolumeLevel = request.VolumeLevel
+ VolumeLevel = request.VolumeLevel,
+ PlayMethod = request.PlayMethod,
+ PlaySessionId = request.PlaySessionId,
+ LiveStreamId = request.LiveStreamId
});
}
@@ -296,7 +326,7 @@ namespace MediaBrowser.Api.UserLibrary
{
if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
{
- ApiEntryPoint.Instance.PingTranscodingJob(AuthorizationContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId);
+ ApiEntryPoint.Instance.PingTranscodingJob(request.PlaySessionId);
}
request.SessionId = GetSession().Result.Id;
@@ -316,7 +346,9 @@ namespace MediaBrowser.Api.UserLibrary
{
ItemId = request.Id,
PositionTicks = request.PositionTicks,
- MediaSourceId = request.MediaSourceId
+ MediaSourceId = request.MediaSourceId,
+ PlaySessionId = request.PlaySessionId,
+ LiveStreamId = request.LiveStreamId
});
}