aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs15
-rw-r--r--MediaBrowser.Api/ChannelService.cs12
-rw-r--r--MediaBrowser.Api/Images/ImageByNameService.cs10
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj1
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs179
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs24
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs10
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs9
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs4
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs6
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs1
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs7
-rw-r--r--MediaBrowser.Api/Playback/ProgressiveStreamService.cs73
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs9
14 files changed, 151 insertions, 209 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 9a7254c94..75c2d406d 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -117,7 +117,8 @@ namespace MediaBrowser.Api
/// <param name="startTimeTicks">The start time ticks.</param>
/// <param name="sourcePath">The source path.</param>
/// <param name="deviceId">The device id.</param>
- public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, long? startTimeTicks, string sourcePath, string deviceId)
+ /// <param name="cancellationTokenSource">The cancellation token source.</param>
+ public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, long? startTimeTicks, string sourcePath, string deviceId, CancellationTokenSource cancellationTokenSource)
{
lock (_activeTranscodingJobs)
{
@@ -129,7 +130,8 @@ namespace MediaBrowser.Api
ActiveRequestCount = 1,
StartTimeTicks = startTimeTicks,
SourcePath = sourcePath,
- DeviceId = deviceId
+ DeviceId = deviceId,
+ CancellationTokenSource = cancellationTokenSource
});
}
}
@@ -276,6 +278,11 @@ namespace MediaBrowser.Api
{
_activeTranscodingJobs.Remove(job);
+ if (!job.CancellationTokenSource.IsCancellationRequested)
+ {
+ job.CancellationTokenSource.Cancel();
+ }
+
if (job.KillTimer != null)
{
job.KillTimer.Dispose();
@@ -329,7 +336,7 @@ namespace MediaBrowser.Api
private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
{
- if (retryCount >= 10)
+ if (retryCount >= 8)
{
return;
}
@@ -432,6 +439,8 @@ namespace MediaBrowser.Api
public long? StartTimeTicks { get; set; }
public string SourcePath { get; set; }
public string DeviceId { get; set; }
+
+ public CancellationTokenSource CancellationTokenSource { get; set; }
}
/// <summary>
diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs
index 2a2d316d3..a410a093e 100644
--- a/MediaBrowser.Api/ChannelService.cs
+++ b/MediaBrowser.Api/ChannelService.cs
@@ -43,6 +43,11 @@ namespace MediaBrowser.Api
public string Id { get; set; }
}
+ [Route("/Channels/Features", "GET", Summary = "Gets features for a channel")]
+ public class GetAllChannelFeatures : IReturn<List<ChannelFeatures>>
+ {
+ }
+
[Route("/Channels/{Id}/Items", "GET", Summary = "Gets channel items")]
public class GetChannelItems : IReturn<QueryResult<BaseItemDto>>
{
@@ -108,6 +113,13 @@ namespace MediaBrowser.Api
_channelManager = channelManager;
}
+ public object Get(GetAllChannelFeatures request)
+ {
+ var result = _channelManager.GetAllChannelFeatures().ToList();
+
+ return ToOptimizedResult(result);
+ }
+
public object Get(GetChannelFeatures request)
{
var result = _channelManager.GetChannelFeatures(request.Id);
diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs
index 16f3f0c1d..d40762964 100644
--- a/MediaBrowser.Api/Images/ImageByNameService.cs
+++ b/MediaBrowser.Api/Images/ImageByNameService.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Dto;
using ServiceStack;
using System;
using System.Collections.Generic;
@@ -89,15 +90,6 @@ namespace MediaBrowser.Api.Images
{
}
- public class ImageByNameInfo
- {
- public string Name { get; set; }
- public string Theme { get; set; }
- public string Context { get; set; }
- public long FileLength { get; set; }
- public string Format { get; set; }
- }
-
/// <summary>
/// Class ImageByNameService
/// </summary>
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 62d5a6ce2..92bbb6130 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -105,7 +105,6 @@
<Compile Include="Playback\Hls\DynamicHlsService.cs" />
<Compile Include="Playback\Hls\HlsSegmentService.cs" />
<Compile Include="Playback\Hls\VideoHlsService.cs" />
- <Compile Include="Playback\ProgressiveStreamService.cs" />
<Compile Include="Playback\Progressive\AudioService.cs" />
<Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
<Compile Include="Playback\BaseStreamingService.cs" />
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 5999a2b55..9f7c1a6c4 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
@@ -13,6 +14,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
@@ -72,20 +74,14 @@ namespace MediaBrowser.Api.Playback
protected ILiveTvManager LiveTvManager { get; private set; }
protected IDlnaManager DlnaManager { get; private set; }
protected IChannelManager ChannelManager { get; private set; }
+ protected IHttpClient HttpClient { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </summary>
- /// <param name="serverConfig">The server configuration.</param>
- /// <param name="userManager">The user manager.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="isoManager">The iso manager.</param>
- /// <param name="mediaEncoder">The media encoder.</param>
- /// <param name="dtoService">The dto service.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="itemRepository">The item repository.</param>
- protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager)
+ protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient)
{
+ HttpClient = httpClient;
ChannelManager = channelManager;
DlnaManager = dlnaManager;
EncodingManager = encodingManager;
@@ -483,8 +479,12 @@ namespace MediaBrowser.Api.Playback
/// <param name="state">The state.</param>
/// <param name="outputVideoCodec">The output video codec.</param>
/// <param name="performTextSubtitleConversion">if set to <c>true</c> [perform text subtitle conversion].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>System.String.</returns>
- protected string GetOutputSizeParam(StreamState state, string outputVideoCodec, bool performTextSubtitleConversion)
+ protected string GetOutputSizeParam(StreamState state,
+ string outputVideoCodec,
+ bool performTextSubtitleConversion,
+ CancellationToken cancellationToken)
{
// http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/
@@ -496,7 +496,7 @@ namespace MediaBrowser.Api.Playback
if (state.SubtitleStream != null && !state.SubtitleStream.IsGraphicalSubtitleStream)
{
- assSubtitleParam = GetTextSubtitleParam(state, performTextSubtitleConversion);
+ assSubtitleParam = GetTextSubtitleParam(state, performTextSubtitleConversion, cancellationToken);
copyTsParam = " -copyts";
}
@@ -592,11 +592,15 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="state">The state.</param>
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>System.String.</returns>
- protected string GetTextSubtitleParam(StreamState state, bool performConversion)
+ protected string GetTextSubtitleParam(StreamState state,
+ bool performConversion,
+ CancellationToken cancellationToken)
{
- var path = state.SubtitleStream.IsExternal ? GetConvertedAssPath(state.MediaPath, state.SubtitleStream, performConversion) :
- GetExtractedAssPath(state, performConversion);
+ var path = state.SubtitleStream.IsExternal ?
+ GetConvertedAssPath(state.SubtitleStream, performConversion, cancellationToken) :
+ GetExtractedAssPath(state, performConversion, cancellationToken);
if (string.IsNullOrEmpty(path))
{
@@ -615,8 +619,11 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="state">The state.</param>
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>System.String.</returns>
- private string GetExtractedAssPath(StreamState state, bool performConversion)
+ private string GetExtractedAssPath(StreamState state,
+ bool performConversion,
+ CancellationToken cancellationToken)
{
var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass");
@@ -636,7 +643,7 @@ namespace MediaBrowser.Api.Playback
// See https://lists.ffmpeg.org/pipermail/ffmpeg-cvslog/2013-April/063616.html
var isAssSubtitle = string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase);
- var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, isAssSubtitle, path, CancellationToken.None);
+ var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, isAssSubtitle, path, cancellationToken);
Task.WaitAll(task);
}
@@ -652,11 +659,13 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the converted ass path.
/// </summary>
- /// <param name="mediaPath">The media path.</param>
/// <param name="subtitleStream">The subtitle stream.</param>
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>System.String.</returns>
- private string GetConvertedAssPath(string mediaPath, MediaStream subtitleStream, bool performConversion)
+ private string GetConvertedAssPath(MediaStream subtitleStream,
+ bool performConversion,
+ CancellationToken cancellationToken)
{
var path = EncodingManager.GetSubtitleCachePath(subtitleStream.Path, ".ass");
@@ -668,7 +677,7 @@ namespace MediaBrowser.Api.Playback
Directory.CreateDirectory(parentPath);
- var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, CancellationToken.None);
+ var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, cancellationToken);
Task.WaitAll(task);
}
@@ -696,7 +705,7 @@ namespace MediaBrowser.Api.Playback
// Add resolution params, if specified
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
{
- outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, false).TrimEnd('"');
+ outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, false, CancellationToken.None).TrimEnd('"');
outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
}
@@ -842,7 +851,7 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
protected string GetInputArgument(StreamState state)
{
- var type = InputType.File;
+ var type = state.IsRemote ? InputType.Url : InputType.File;
var inputPath = new[] { state.MediaPath };
@@ -862,8 +871,10 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="state">The state.</param>
/// <param name="outputPath">The output path.</param>
+ /// <param name="cancellationTokenSource">The cancellation token source.</param>
/// <returns>Task.</returns>
- protected async Task StartFfMpeg(StreamState state, string outputPath)
+ /// <exception cref="System.InvalidOperationException">ffmpeg was not found at + MediaEncoder.EncoderPath</exception>
+ protected async Task StartFfMpeg(StreamState state, string outputPath, CancellationTokenSource cancellationTokenSource)
{
if (!File.Exists(MediaEncoder.EncoderPath))
{
@@ -874,7 +885,7 @@ namespace MediaBrowser.Api.Playback
if (state.IsInputVideo && state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
{
- state.IsoMount = await IsoManager.Mount(state.MediaPath, CancellationToken.None).ConfigureAwait(false);
+ state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
var commandLineArgs = GetCommandLineArguments(outputPath, state, true);
@@ -906,7 +917,7 @@ namespace MediaBrowser.Api.Playback
EnableRaisingEvents = true
};
- ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId);
+ ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId, cancellationTokenSource);
var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
Logger.Info(commandLineLogMessage);
@@ -918,7 +929,7 @@ namespace MediaBrowser.Api.Playback
state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
- await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length).ConfigureAwait(false);
+ await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
process.Exited += (sender, args) => OnFfMpegProcessExited(process, state);
@@ -946,19 +957,19 @@ namespace MediaBrowser.Api.Playback
// Wait for the file to exist before proceeeding
while (!File.Exists(outputPath))
{
- await Task.Delay(100).ConfigureAwait(false);
+ await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
}
// Allow a small amount of time to buffer a little
if (state.IsInputVideo)
{
- await Task.Delay(500).ConfigureAwait(false);
+ await Task.Delay(500, cancellationTokenSource.Token).ConfigureAwait(false);
}
// This is arbitrary, but add a little buffer time when internet streaming
if (state.IsRemote)
{
- await Task.Delay(3000).ConfigureAwait(false);
+ await Task.Delay(3000, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
@@ -1050,13 +1061,19 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the user agent param.
/// </summary>
- /// <param name="path">The path.</param>
+ /// <param name="state">The state.</param>
/// <returns>System.String.</returns>
- private string GetUserAgentParam(string path)
+ private string GetUserAgentParam(StreamState state)
{
- var useragent = GetUserAgent(path);
+ string useragent;
+ state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
- if (!string.IsNullOrEmpty(useragent))
+ if (string.IsNullOrWhiteSpace(useragent))
+ {
+ useragent = GetUserAgent(state.MediaPath);
+ }
+
+ if (!string.IsNullOrWhiteSpace(useragent))
{
return "-user-agent \"" + useragent + "\"";
}
@@ -1337,9 +1354,7 @@ namespace MediaBrowser.Api.Playback
state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault();
}
- var item = string.IsNullOrEmpty(request.MediaSourceId) ?
- LibraryManager.GetItemById(request.Id) :
- LibraryManager.GetItemById(request.MediaSourceId);
+ var item = LibraryManager.GetItemById(request.Id);
if (user != null && item.GetPlayAccess(user) != PlayAccess.Full)
{
@@ -1427,19 +1442,24 @@ namespace MediaBrowser.Api.Playback
}
else if (item is IChannelMediaItem)
{
- var source = await GetChannelMediaInfo(request.Id, CancellationToken.None).ConfigureAwait(false);
+ var source = await GetChannelMediaInfo(request.Id, request.MediaSourceId, cancellationToken).ConfigureAwait(false);
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
- state.IsRemote = source.IsRemote;
+ state.IsRemote = source.LocationType == LocationType.Remote;
state.MediaPath = source.Path;
state.RunTimeTicks = item.RunTimeTicks;
- mediaStreams = GetMediaStreams(source).ToList();
+ state.RemoteHttpHeaders = source.RequiredHttpHeaders;
+ mediaStreams = source.MediaStreams;
}
else
{
- state.MediaPath = item.Path;
- state.IsRemote = item.LocationType == LocationType.Remote;
+ var mediaSource = string.IsNullOrWhiteSpace(request.MediaSourceId)
+ ? item
+ : LibraryManager.GetItemById(request.MediaSourceId);
+
+ state.MediaPath = mediaSource.Path;
+ state.IsRemote = mediaSource.LocationType == LocationType.Remote;
- var video = item as Video;
+ var video = mediaSource as Video;
if (video != null)
{
@@ -1461,20 +1481,20 @@ namespace MediaBrowser.Api.Playback
state.InputContainer = video.Container;
}
- var audio = item as Audio;
+ var audio = mediaSource as Audio;
if (audio != null)
{
state.InputContainer = audio.Container;
}
- state.RunTimeTicks = item.RunTimeTicks;
+ state.RunTimeTicks = mediaSource.RunTimeTicks;
}
var videoRequest = request as VideoStreamRequest;
mediaStreams = mediaStreams ?? ItemRepository.GetMediaStreams(new MediaStreamQuery
{
- ItemId = item.Id
+ ItemId = new Guid(string.IsNullOrWhiteSpace(request.MediaSourceId) ? request.Id : request.MediaSourceId)
}).ToList();
@@ -1545,65 +1565,32 @@ namespace MediaBrowser.Api.Playback
}
}
- private IEnumerable<MediaStream> GetMediaStreams(ChannelMediaInfo info)
- {
- var list = new List<MediaStream>();
-
- if (!string.IsNullOrWhiteSpace(info.VideoCodec) &&
- !string.IsNullOrWhiteSpace(info.AudioCodec))
- {
- list.Add(new MediaStream
- {
- Type = MediaStreamType.Video,
- Width = info.Width,
- RealFrameRate = info.Framerate,
- Profile = info.VideoProfile,
- Level = info.VideoLevel,
- Index = -1,
- Height = info.Height,
- Codec = info.VideoCodec,
- BitRate = info.VideoBitrate,
- AverageFrameRate = info.Framerate
- });
-
- list.Add(new MediaStream
- {
- Type = MediaStreamType.Audio,
- Index = -1,
- Codec = info.AudioCodec,
- BitRate = info.AudioBitrate,
- Channels = info.AudioChannels,
- SampleRate = info.AudioSampleRate
- });
- }
-
- return list;
- }
-
- private async Task<ChannelMediaInfo> GetChannelMediaInfo(string id, CancellationToken cancellationToken)
+ private async Task<MediaSourceInfo> GetChannelMediaInfo(string id,
+ string mediaSourceId,
+ CancellationToken cancellationToken)
{
var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(id, cancellationToken)
.ConfigureAwait(false);
var list = channelMediaSources.ToList();
- var preferredWidth = ServerConfigurationManager.Configuration.ChannelOptions.PreferredStreamingWidth;
-
- if (preferredWidth.HasValue)
+ if (!string.IsNullOrWhiteSpace(mediaSourceId))
{
- var val = preferredWidth.Value;
+ var source = list
+ .FirstOrDefault(i => string.Equals(mediaSourceId, i.Id));
- return list
- .OrderBy(i => Math.Abs(i.Width ?? 0 - val))
- .ThenByDescending(i => i.Width ?? 0)
- .ThenBy(list.IndexOf)
- .First();
+ if (source != null)
+ {
+ return source;
+ }
+
+ Logger.Warn("Invalid channel MediaSourceId requested, defaulting to first. Item: {0}. Requested MediaSourceId: {1}.",
+ id,
+ mediaSourceId
+ );
}
- return list
- .OrderByDescending(i => i.Width ?? 0)
- .ThenBy(list.IndexOf)
- .First();
+ return list.First();
}
private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
@@ -1932,7 +1919,11 @@ namespace MediaBrowser.Api.Playback
inputModifier += " " + probeSize;
inputModifier = inputModifier.Trim();
- inputModifier += " " + GetUserAgentParam(state.MediaPath);
+ if (state.IsRemote)
+ {
+ inputModifier += " " + GetUserAgentParam(state);
+ }
+
inputModifier = inputModifier.Trim();
inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 553f02368..a7412e3d8 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public abstract class BaseHlsService : BaseStreamingService
{
- protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
+ protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
{
}
@@ -82,7 +82,9 @@ namespace MediaBrowser.Api.Playback.Hls
/// </exception>
private async Task<object> ProcessRequestAsync(StreamRequest request)
{
- var state = GetState(request, CancellationToken.None).Result;
+ var cancellationTokenSource = new CancellationTokenSource();
+
+ var state = GetState(request, cancellationTokenSource.Token).Result;
var playlist = GetOutputFilePath(state);
@@ -92,7 +94,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
else
{
- await FfmpegStartLock.WaitAsync().ConfigureAwait(false);
+ await FfmpegStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
if (File.Exists(playlist))
@@ -104,16 +106,16 @@ namespace MediaBrowser.Api.Playback.Hls
// If the playlist doesn't already exist, startup ffmpeg
try
{
- await StartFfMpeg(state, playlist).ConfigureAwait(false);
+ await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
}
catch
{
state.Dispose();
throw;
}
- }
- await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false);
+ await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false);
+ }
}
finally
{
@@ -220,10 +222,12 @@ namespace MediaBrowser.Api.Playback.Hls
return builder.ToString();
}
- protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount)
+ protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken)
{
while (true)
{
+ cancellationToken.ThrowIfCancellationRequested();
+
string fileText;
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
@@ -240,7 +244,7 @@ namespace MediaBrowser.Api.Playback.Hls
break;
}
- await Task.Delay(25).ConfigureAwait(false);
+ await Task.Delay(25, cancellationToken).ConfigureAwait(false);
}
}
@@ -287,7 +291,7 @@ namespace MediaBrowser.Api.Playback.Hls
// If performSubtitleConversions is true we're actually starting ffmpeg
var startNumberParam = performSubtitleConversions ? GetStartNumber(state).ToString(UsCulture) : "0";
- var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} \"{10}\"",
+ var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
itsOffset,
inputModifier,
GetInputArgument(state),
@@ -309,7 +313,7 @@ namespace MediaBrowser.Api.Playback.Hls
var bitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? 64000;
- var lowBitrateParams = string.Format(" -threads {0} -vn -codec:a:0 libmp3lame -ac 2 -ab {1} -hls_time {2} -start_number {3} -hls_list_size {4} \"{5}\"",
+ var lowBitrateParams = string.Format(" -threads {0} -vn -codec:a:0 libmp3lame -ac 2 -ab {1} -hls_time {2} -start_number {3} -hls_list_size {4} -y \"{5}\"",
threads,
bitrate / 2,
state.SegmentLength.ToString(UsCulture),
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index c5405a3b5..96ecfb055 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
@@ -16,6 +17,7 @@ using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using MimeTypes = ServiceStack.MimeTypes;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -60,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
public class DynamicHlsService : BaseHlsService
{
- public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
+ public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
{
}
@@ -98,9 +100,9 @@ namespace MediaBrowser.Api.Playback.Hls
if (!File.Exists(playlistPath))
{
- await StartFfMpeg(state, playlistPath).ConfigureAwait(false);
+ await StartFfMpeg(state, playlistPath, new CancellationTokenSource()).ConfigureAwait(false);
- await WaitForMinimumSegmentCount(playlistPath, GetSegmentWait()).ConfigureAwait(false);
+ await WaitForMinimumSegmentCount(playlistPath, GetSegmentWait(), CancellationToken.None).ConfigureAwait(false);
}
return GetSegementResult(path);
@@ -283,7 +285,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
{
- args += GetOutputSizeParam(state, codec, performSubtitleConversion);
+ args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index 162ab741a..e4ee68241 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -1,4 +1,6 @@
+using System.Threading;
using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
@@ -54,7 +56,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public class VideoHlsService : BaseHlsService
{
- public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
+ public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
{
}
@@ -155,7 +157,8 @@ namespace MediaBrowser.Api.Playback.Hls
/// <param name="state">The state.</param>
/// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param>
/// <returns>System.String.</returns>
- protected override string GetVideoArguments(StreamState state, bool performSubtitleConversion)
+ protected override string GetVideoArguments(StreamState state,
+ bool performSubtitleConversion)
{
var codec = GetVideoCodec(state.VideoRequest);
@@ -178,7 +181,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
{
- args += GetOutputSizeParam(state, codec, performSubtitleConversion);
+ args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index 2e002cc9b..18cfbbdf2 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -44,7 +44,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class AudioService : BaseProgressiveStreamingService
{
- public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient)
+ public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient, imageProcessor)
{
}
@@ -105,7 +105,7 @@ namespace MediaBrowser.Api.Playback.Progressive
var inputModifier = GetInputModifier(state);
- return string.Format("{0} -i {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 \"{5}\"",
+ return string.Format("{0} -i {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
inputModifier,
GetInputArgument(state),
threads,
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index 06e8f7b2c..5e3e85140 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -25,12 +25,10 @@ namespace MediaBrowser.Api.Playback.Progressive
public abstract class BaseProgressiveStreamingService : BaseStreamingService
{
protected readonly IImageProcessor ImageProcessor;
- protected readonly IHttpClient HttpClient;
- protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
+ protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient)
{
ImageProcessor = imageProcessor;
- HttpClient = httpClient;
}
/// <summary>
@@ -280,7 +278,7 @@ namespace MediaBrowser.Api.Playback.Progressive
if (!File.Exists(outputPath))
{
- await StartFfMpeg(state, outputPath).ConfigureAwait(false);
+ await StartFfMpeg(state, outputPath, new CancellationTokenSource()).ConfigureAwait(false);
}
else
{
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 58f36a06c..8ba504e14 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -32,6 +32,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
/// <param name="path">The path.</param>
/// <param name="logger">The logger.</param>
+ /// <param name="fileSystem">The file system.</param>
public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem)
{
Path = path;
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 29bc3f897..65a62aad3 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -1,3 +1,4 @@
+using System.Threading;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
@@ -60,7 +61,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class VideoService : BaseProgressiveStreamingService
{
- public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient)
+ public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient, imageProcessor)
{
}
@@ -108,7 +109,7 @@ namespace MediaBrowser.Api.Playback.Progressive
var inputModifier = GetInputModifier(state);
- return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} \"{8}\"",
+ return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"",
inputModifier,
GetInputArgument(state),
keyFrame,
@@ -156,7 +157,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
{
- args += GetOutputSizeParam(state, codec, performSubtitleConversion);
+ args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
}
}
diff --git a/MediaBrowser.Api/Playback/ProgressiveStreamService.cs b/MediaBrowser.Api/Playback/ProgressiveStreamService.cs
deleted file mode 100644
index 531f79a22..000000000
--- a/MediaBrowser.Api/Playback/ProgressiveStreamService.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Api.Playback.Progressive;
-
-namespace MediaBrowser.Api.Playback
-{
- //public class GetProgressiveAudioStream : StreamRequest
- //{
-
- //}
-
- //public class ProgressiveStreamService : BaseApiService
- //{
- // public object Get(GetProgressiveAudioStream request)
- // {
- // return ProcessRequest(request, false);
- // }
-
- // /// <summary>
- // /// Gets the specified request.
- // /// </summary>
- // /// <param name="request">The request.</param>
- // /// <returns>System.Object.</returns>
- // public object Head(GetProgressiveAudioStream request)
- // {
- // return ProcessRequest(request, true);
- // }
-
- // protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
- // {
- // var state = GetState(request, CancellationToken.None).Result;
-
- // var responseHeaders = new Dictionary<string, string>();
-
- // if (request.Static && state.IsRemote)
- // {
- // AddDlnaHeaders(state, responseHeaders, true);
-
- // return GetStaticRemoteStreamResult(state.MediaPath, responseHeaders, isHeadRequest).Result;
- // }
-
- // var outputPath = GetOutputFilePath(state);
- // var outputPathExists = File.Exists(outputPath);
-
- // var isStatic = request.Static ||
- // (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive));
-
- // AddDlnaHeaders(state, responseHeaders, isStatic);
-
- // if (request.Static)
- // {
- // var contentType = state.GetMimeType(state.MediaPath);
-
- // return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
- // }
-
- // if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
- // {
- // var contentType = state.GetMimeType(outputPath);
-
- // return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
- // }
-
- // return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
- // }
-
- //}
-}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index f9e861e8d..295ee44e7 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -31,8 +31,11 @@ namespace MediaBrowser.Api.Playback
public StreamState()
{
PlayableStreamFileNames = new List<string>();
+ RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
+ public Dictionary<string, string> RemoteHttpHeaders { get; set; }
+
/// <summary>
/// Gets or sets the log file stream.
/// </summary>
@@ -40,7 +43,7 @@ namespace MediaBrowser.Api.Playback
public Stream LogFileStream { get; set; }
public string InputContainer { get; set; }
-
+
public MediaStream AudioStream { get; set; }
public MediaStream VideoStream { get; set; }
public MediaStream SubtitleStream { get; set; }
@@ -277,8 +280,8 @@ namespace MediaBrowser.Api.Playback
{
get
{
- var defaultValue = string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ?
- TransportStreamTimestamp.Valid :
+ var defaultValue = string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ?
+ TransportStreamTimestamp.Valid :
TransportStreamTimestamp.None;
return !Request.Static