aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs39
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs33
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs44
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs7
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs23
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs4
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs10
7 files changed, 124 insertions, 36 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 214fb7488..2f5b9e1e0 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -44,7 +45,13 @@ namespace MediaBrowser.Api
private readonly IFileSystem _fileSystem;
private readonly IMediaSourceManager _mediaSourceManager;
- public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
+ /// <summary>
+ /// The active transcoding jobs
+ /// </summary>
+ private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
+
+ private readonly Dictionary<string, SemaphoreSlim> _transcodingLocks =
+ new Dictionary<string, SemaphoreSlim>();
/// <summary>
/// Initializes a new instance of the <see cref="ApiEntryPoint" /> class.
@@ -67,6 +74,21 @@ namespace MediaBrowser.Api
_sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
}
+ public SemaphoreSlim GetTranscodingLock(string outputPath)
+ {
+ lock (_transcodingLocks)
+ {
+ SemaphoreSlim result;
+ if (!_transcodingLocks.TryGetValue(outputPath, out result))
+ {
+ result = new SemaphoreSlim(1, 1);
+ _transcodingLocks[outputPath] = result;
+ }
+
+ return result;
+ }
+ }
+
private void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
{
if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
@@ -149,11 +171,6 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// The active transcoding jobs
- /// </summary>
- private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
-
- /// <summary>
/// Called when [transcode beginning].
/// </summary>
/// <param name="path">The path.</param>
@@ -258,6 +275,11 @@ namespace MediaBrowser.Api
}
}
+ lock (_transcodingLocks)
+ {
+ _transcodingLocks.Remove(path);
+ }
+
if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
{
_sessionManager.ClearTranscodingInfo(state.Request.DeviceId);
@@ -497,6 +519,11 @@ namespace MediaBrowser.Api
}
}
+ lock (_transcodingLocks)
+ {
+ _transcodingLocks.Remove(job.Path);
+ }
+
lock (job.ProcessLock)
{
if (job.TranscodingThrottler != null)
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 3ad0ec1ba..a5f8fce6e 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -12,9 +12,13 @@ using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using CommonIO;
+using MediaBrowser.Api.Playback.Progressive;
+using MediaBrowser.Controller.Configuration;
namespace MediaBrowser.Api.LiveTv
{
@@ -613,16 +617,24 @@ namespace MediaBrowser.Api.LiveTv
}
+ [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")]
+ public class GetLiveStreamFile
+ {
+ public string Id { get; set; }
+ public string Container { get; set; }
+ }
+
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
private readonly IUserManager _userManager;
- private readonly IConfigurationManager _config;
+ private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
+ private readonly IFileSystem _fileSystem;
- public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService)
+ public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
@@ -630,6 +642,23 @@ namespace MediaBrowser.Api.LiveTv
_httpClient = httpClient;
_libraryManager = libraryManager;
_dtoService = dtoService;
+ _fileSystem = fileSystem;
+ }
+
+ public object Get(GetLiveStreamFile request)
+ {
+ var filePath = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, request.Id + ".ts");
+
+ var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath);
+
+ var streamSource = new ProgressiveFileCopier(_fileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None)
+ {
+ AllowEndOfFile = false
+ };
+
+ return ResultFactory.GetAsyncStreamWriter(streamSource);
}
public object Get(GetDefaultListingProvider request)
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 761b1eb4e..319e4bbb6 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -87,7 +87,8 @@ namespace MediaBrowser.Api.Playback.Hls
if (!FileSystem.FileExists(playlist))
{
- await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlist);
+ await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
if (!FileSystem.FileExists(playlist))
@@ -104,13 +105,13 @@ namespace MediaBrowser.Api.Playback.Hls
throw;
}
- var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 4);
+ var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3);
await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
finally
{
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
}
}
@@ -182,32 +183,41 @@ namespace MediaBrowser.Api.Playback.Hls
{
Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist);
- while (true)
+ while (!cancellationToken.IsCancellationRequested)
{
- // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
- using (var fileStream = GetPlaylistFileStream(playlist))
+ try
{
- using (var reader = new StreamReader(fileStream))
+ // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
+ using (var fileStream = GetPlaylistFileStream(playlist))
{
- var count = 0;
-
- while (!reader.EndOfStream)
+ using (var reader = new StreamReader(fileStream))
{
- var line = await reader.ReadLineAsync().ConfigureAwait(false);
+ var count = 0;
- if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
+ while (!reader.EndOfStream)
{
- count++;
- if (count >= segmentCount)
+ var line = await reader.ReadLineAsync().ConfigureAwait(false);
+
+ if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
{
- Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
- return;
+ count++;
+ if (count >= segmentCount)
+ {
+ Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
+ return;
+ }
}
}
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
}
+ catch (IOException)
+ {
+ // May get an error if the file is locked
+ }
+
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 9cd55528d..d4ddbd7c5 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -171,14 +171,15 @@ namespace MediaBrowser.Api.Playback.Hls
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
- await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlistPath);
+ await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
var released = false;
try
{
if (FileSystem.FileExists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
released = true;
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
@@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (!released)
{
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index b8cb6b14f..a68319109 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -17,6 +17,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using ServiceStack;
namespace MediaBrowser.Api.Playback.Progressive
{
@@ -129,6 +130,23 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state)
{
+ if (state.MediaPath.IndexOf("/livestreamfiles/", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ var parts = state.MediaPath.Split('/');
+ var filename = parts[parts.Length - 2] + Path.GetExtension(parts[parts.Length - 1]);
+ var filePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, filename);
+
+ var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath);
+
+ var streamSource = new ProgressiveFileCopier(FileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None)
+ {
+ AllowEndOfFile = false
+ };
+ return ResultFactory.GetAsyncStreamWriter(streamSource);
+ }
+
return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource)
.ConfigureAwait(false);
}
@@ -345,7 +363,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return streamResult;
}
- await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
+ await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
TranscodingJob job;
@@ -376,7 +395,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
finally
{
- ApiEntryPoint.Instance.TranscodingStartLock.Release();
+ transcodingLock.Release();
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 0a9a44641..80b5e357d 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -24,6 +24,8 @@ namespace MediaBrowser.Api.Playback.Progressive
private long _bytesWritten = 0;
+ public bool AllowEndOfFile = true;
+
public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
{
_fileSystem = fileSystem;
@@ -50,7 +52,7 @@ namespace MediaBrowser.Api.Playback.Progressive
using (var fs = _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
{
- while (eofCount < 15)
+ while (eofCount < 15 || !AllowEndOfFile)
{
var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 109aa85de..ef0282abc 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -73,10 +73,6 @@ namespace MediaBrowser.Api.Playback
{
get
{
- if (!RunTimeTicks.HasValue)
- {
- return 6;
- }
if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
var userAgent = UserAgent ?? string.Empty;
@@ -92,12 +88,16 @@ namespace MediaBrowser.Api.Playback
return 10;
}
+ if (!RunTimeTicks.HasValue)
+ {
+ return 3;
+ }
return 6;
}
if (!RunTimeTicks.HasValue)
{
- return 6;
+ return 3;
}
return 3;
}