diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 2a72854fb4..05ff503e47 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -339,14 +339,17 @@ namespace MediaBrowser.Api
return;
}
- var timerDuration = job.Type == TranscodingJobType.Progressive ?
- 1000 :
- 1800000;
+ var timerDuration = 1000;
- // We can really reduce the timeout for apps that are using the newer api
- if (!string.IsNullOrWhiteSpace(job.PlaySessionId) && job.Type != TranscodingJobType.Progressive)
+ if (job.Type != TranscodingJobType.Progressive)
{
- timerDuration = 50000;
+ timerDuration = 1800000;
+
+ // We can really reduce the timeout for apps that are using the newer api
+ if (!string.IsNullOrWhiteSpace(job.PlaySessionId))
+ {
+ timerDuration = 60000;
+ }
}
job.PingTimeout = timerDuration;
@@ -628,6 +631,9 @@ namespace MediaBrowser.Api
///
/// The live stream identifier.
public string LiveStreamId { get; set; }
+
+ public bool IsLiveOutput { get; set; }
+
///
/// Gets or sets the path.
///
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 80079adf4a..8c6cc0a18e 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -625,6 +625,8 @@ namespace MediaBrowser.Api.Images
var file = await _imageProcessor.ProcessImage(options).ConfigureAwait(false);
+ headers["Vary"] = "Accept";
+
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
CacheDuration = cacheDuration,
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index 78c6a8bf41..46034dc61a 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Api.Music
[Route("/MusicGenres/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")]
public class GetInstantMixFromMusicGenreId : BaseGetSimilarItems
{
- [ApiMember(Name = "Id", Description = "The genre Id", IsRequired = true, DataType = "string", ParameterType = "querypath", Verb = "GET")]
+ [ApiMember(Name = "Id", Description = "The genre Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Id { get; set; }
}
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 6f1edb1650..31679aad3c 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -148,7 +148,6 @@ namespace MediaBrowser.Api.Playback
}
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
- private readonly long _slowSeekTicks = TimeSpan.FromSeconds(0).Ticks;
///
/// Gets the fast seek command line parameter.
@@ -162,37 +161,12 @@ namespace MediaBrowser.Api.Playback
if (time > 0)
{
- if (time > _slowSeekTicks && EnableSlowSeek)
- {
- time -= _slowSeekTicks;
- }
-
return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time));
}
return string.Empty;
}
- protected string GetSlowSeekCommandLineParameter(StreamRequest request)
- {
- var time = request.StartTimeTicks ?? 0;
-
- if (time > _slowSeekTicks && _slowSeekTicks > 0)
- {
- return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(_slowSeekTicks));
- }
-
- return string.Empty;
- }
-
- protected virtual bool EnableSlowSeek
- {
- get
- {
- return false;
- }
- }
-
///
/// Gets the map args.
///
@@ -716,7 +690,7 @@ namespace MediaBrowser.Api.Playback
// TODO: Perhaps also use original_size=1920x800 ??
return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
- subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"),
+ subtitlePath.Replace("'", "\\'").Replace('\\', '/').Replace(":/", "\\:/"),
charsetParam,
seconds.ToString(UsCulture));
}
@@ -724,7 +698,7 @@ namespace MediaBrowser.Api.Playback
var mediaPath = state.MediaPath ?? string.Empty;
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
- mediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
+ mediaPath.Replace("'", "\\'").Replace('\\', '/').Replace(":/", "\\:/"),
state.InternalSubtitleStreamOffset.ToString(UsCulture),
seconds.ToString(UsCulture));
}
@@ -1086,7 +1060,7 @@ namespace MediaBrowser.Api.Playback
private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
{
- if (EnableThrottling && state.InputProtocol == MediaProtocol.File &&
+ if (EnableThrottling(state) && state.InputProtocol == MediaProtocol.File &&
state.RunTimeTicks.HasValue &&
state.VideoType == VideoType.VideoFile &&
!string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
@@ -1099,12 +1073,9 @@ namespace MediaBrowser.Api.Playback
}
}
- protected virtual bool EnableThrottling
+ protected virtual bool EnableThrottling(StreamState state)
{
- get
- {
- return true;
- }
+ return true;
}
private async void StartStreamingLog(TranscodingJob transcodingJob, StreamState state, Stream source, Stream target)
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index b10c02e17c..b2ffeca3db 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -7,13 +7,13 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -100,6 +100,7 @@ namespace MediaBrowser.Api.Playback.Hls
try
{
job = await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
+ job.IsLiveOutput = isLive;
}
catch
{
@@ -133,7 +134,7 @@ namespace MediaBrowser.Api.Playback.Hls
var appendBaselineStream = false;
var baselineStreamBitrate = 64000;
- var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
+ var hlsVideoRequest = state.VideoRequest as GetHlsVideoStreamLegacy;
if (hlsVideoRequest != null)
{
appendBaselineStream = hlsVideoRequest.AppendBaselineStream;
@@ -244,7 +245,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
- var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
+ var hlsVideoRequest = state.VideoRequest as GetHlsVideoStreamLegacy;
var itsOffsetMs = hlsVideoRequest == null
? 0
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 2d8b71a0cc..fdddc0c37f 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -30,27 +30,60 @@ namespace MediaBrowser.Api.Playback.Hls
///
[Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
[Route("/Videos/{Id}/master.m3u8", "HEAD", Summary = "Gets a video stream using HTTP live streaming.")]
- public class GetMasterHlsVideoStream : VideoStreamRequest
+ public class GetMasterHlsVideoPlaylist : VideoStreamRequest, IMasterHlsRequest
{
public bool EnableAdaptiveBitrateStreaming { get; set; }
- public GetMasterHlsVideoStream()
+ public GetMasterHlsVideoPlaylist()
{
EnableAdaptiveBitrateStreaming = true;
}
}
+ [Route("/Audio/{Id}/master.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")]
+ [Route("/Audio/{Id}/master.m3u8", "HEAD", Summary = "Gets an audio stream using HTTP live streaming.")]
+ public class GetMasterHlsAudioPlaylist : StreamRequest, IMasterHlsRequest
+ {
+ public bool EnableAdaptiveBitrateStreaming { get; set; }
+
+ public GetMasterHlsAudioPlaylist()
+ {
+ EnableAdaptiveBitrateStreaming = true;
+ }
+ }
+
+ public interface IMasterHlsRequest
+ {
+ bool EnableAdaptiveBitrateStreaming { get; set; }
+ }
+
[Route("/Videos/{Id}/main.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
- public class GetMainHlsVideoStream : VideoStreamRequest
+ public class GetVariantHlsVideoPlaylist : VideoStreamRequest
+ {
+ }
+
+ [Route("/Audio/{Id}/main.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")]
+ public class GetVariantHlsAudioPlaylist : StreamRequest
{
}
- ///
- /// Class GetHlsVideoSegment
- ///
[Route("/Videos/{Id}/hlsdynamic/{PlaylistId}/{SegmentId}.ts", "GET")]
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
- public class GetDynamicHlsVideoSegment : VideoStreamRequest
+ public class GetHlsVideoSegment : VideoStreamRequest
+ {
+ public string PlaylistId { get; set; }
+
+ ///
+ /// Gets or sets the segment id.
+ ///
+ /// The segment id.
+ public string SegmentId { get; set; }
+ }
+
+ [Route("/Audio/{Id}/hlsdynamic/{PlaylistId}/{SegmentId}.aac", "GET")]
+ [Route("/Audio/{Id}/hlsdynamic/{PlaylistId}/{SegmentId}.ts", "GET")]
+ [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
+ public class GetHlsAudioSegment : StreamRequest
{
public string PlaylistId { get; set; }
@@ -71,27 +104,47 @@ namespace MediaBrowser.Api.Playback.Hls
protected INetworkManager NetworkManager { get; private set; }
- public Task