From f26a639a36aed431a9090fb833358871f2192e74 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 24 May 2015 14:33:28 -0400 Subject: [PATCH] fix audio-only hls --- .../Playback/BaseStreamingService.cs | 26 -- .../Playback/Hls/BaseHlsService.cs | 4 +- .../Playback/Hls/DynamicHlsService.cs | 264 ++++++++++++------ .../Playback/Hls/HlsSegmentService.cs | 35 ++- .../Playback/Hls/VideoHlsService.cs | 21 +- .../Playback/Progressive/VideoService.cs | 2 +- MediaBrowser.Api/Playback/StreamState.cs | 7 +- MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs | 2 +- MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml | 2 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 5 + .../IO/LibraryMonitor.cs | 6 +- SharedVersion.cs | 4 +- 12 files changed, 225 insertions(+), 153 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a36b65f74..0b8f21129 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(time)); - } - - return string.Empty; - } - - protected virtual bool EnableSlowSeek - { - get - { - return false; - } - } - /// /// Gets the map args. /// diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index a143da772..b2ffeca3d 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -134,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; @@ -245,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 a2e327f7d..6774cc859 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 Get(GetMasterHlsVideoStream request) + public Task Get(GetMasterHlsVideoPlaylist request) { - return GetAsync(request, "GET"); + return GetMasterPlaylistInternal(request, "GET"); } - public Task Head(GetMasterHlsVideoStream request) + public Task Head(GetMasterHlsVideoPlaylist request) { - return GetAsync(request, "HEAD"); + return GetMasterPlaylistInternal(request, "HEAD"); } - public Task Get(GetMainHlsVideoStream request) + public Task Get(GetMasterHlsAudioPlaylist request) { - return GetPlaylistAsync(request, "main"); + return GetMasterPlaylistInternal(request, "GET"); } - public Task Get(GetDynamicHlsVideoSegment request) + public Task Head(GetMasterHlsAudioPlaylist request) + { + return GetMasterPlaylistInternal(request, "HEAD"); + } + + public Task Get(GetVariantHlsVideoPlaylist request) + { + return GetVariantPlaylistInternal(request, true, "main"); + } + + public Task Get(GetVariantHlsAudioPlaylist request) + { + return GetVariantPlaylistInternal(request, false, "main"); + } + + public Task Get(GetHlsVideoSegment request) { return GetDynamicSegment(request, request.SegmentId); } - private async Task GetDynamicSegment(VideoStreamRequest request, string segmentId) + public Task Get(GetHlsAudioSegment request) + { + return GetDynamicSegment(request, request.SegmentId); + } + + private async Task GetDynamicSegment(StreamRequest request, string segmentId) { if ((request.StartTimeTicks ?? 0) > 0) { @@ -107,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8"); - var segmentPath = GetSegmentPath(playlistPath, requestedIndex); + var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex); var segmentLength = state.SegmentLength; var segmentExtension = GetSegmentFileExtension(state); @@ -191,11 +244,11 @@ namespace MediaBrowser.Api.Playback.Hls ApiEntryPoint.Instance.TranscodingStartLock.Release(); } - Logger.Info("waiting for {0}", segmentPath); - while (!File.Exists(segmentPath)) - { - await Task.Delay(50, cancellationToken).ConfigureAwait(false); - } + //Logger.Info("waiting for {0}", segmentPath); + //while (!File.Exists(segmentPath)) + //{ + // await Task.Delay(50, cancellationToken).ConfigureAwait(false); + //} Logger.Info("returning {0}", segmentPath); job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); @@ -254,7 +307,7 @@ namespace MediaBrowser.Api.Playback.Hls for (var i = 0; i < requestedIndex; i++) { - var segmentPath = GetSegmentPath(playlist, i); + var segmentPath = GetSegmentPath(state, playlist, i); double length; if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length)) @@ -360,7 +413,7 @@ namespace MediaBrowser.Api.Playback.Hls { var segmentId = "0"; - var segmentRequest = request as GetDynamicHlsVideoSegment; + var segmentRequest = request as GetHlsVideoSegment; if (segmentRequest != null) { segmentId = segmentRequest.SegmentId; @@ -369,13 +422,13 @@ namespace MediaBrowser.Api.Playback.Hls return int.Parse(segmentId, NumberStyles.Integer, UsCulture); } - private string GetSegmentPath(string playlist, int index) + private string GetSegmentPath(StreamState state, string playlist, int index) { var folder = Path.GetDirectoryName(playlist); var filename = Path.GetFileNameWithoutExtension(playlist); - return Path.Combine(folder, filename + index.ToString(UsCulture) + ".ts"); + return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state)); } private async Task GetSegmentResult(string playlistPath, @@ -474,7 +527,7 @@ namespace MediaBrowser.Api.Playback.Hls }); } - private async Task GetAsync(GetMasterHlsVideoStream request, string method) + private async Task GetMasterPlaylistInternal(StreamRequest request, string method) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); @@ -511,14 +564,16 @@ namespace MediaBrowser.Api.Playback.Hls var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8"; playlistUrl += queryString; - var request = (GetMasterHlsVideoStream)state.Request; + var request = state.Request; var subtitleStreams = state.MediaSource .MediaStreams .Where(i => i.IsTextSubtitleStream) .ToList(); - var subtitleGroup = subtitleStreams.Count > 0 && request.SubtitleMethod == SubtitleDeliveryMethod.Hls ? + var subtitleGroup = subtitleStreams.Count > 0 && + (request is GetMasterHlsVideoPlaylist) && + ((GetMasterHlsVideoPlaylist)request).SubtitleMethod == SubtitleDeliveryMethod.Hls ? "subs" : null; @@ -526,7 +581,7 @@ namespace MediaBrowser.Api.Playback.Hls if (EnableAdaptiveBitrateStreaming(state, isLiveStream)) { - var requestedVideoBitrate = state.VideoRequest.VideoBitRate.Value; + var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0; // By default, vary by just 200k var variation = GetBitrateVariation(totalBitrate); @@ -596,7 +651,7 @@ namespace MediaBrowser.Api.Playback.Hls return false; } - var request = state.Request as GetMasterHlsVideoStream; + var request = state.Request as IMasterHlsRequest; if (request != null && !request.EnableAdaptiveBitrateStreaming) { return false; @@ -618,6 +673,11 @@ namespace MediaBrowser.Api.Playback.Hls return false; } + if (!state.IsOutputVideo) + { + return false; + } + // Having problems in android return false; //return state.VideoRequest.VideoBitRate.HasValue; @@ -673,7 +733,7 @@ namespace MediaBrowser.Api.Playback.Hls return variation; } - private async Task GetPlaylistAsync(VideoStreamRequest request, string name) + private async Task GetVariantPlaylistInternal(StreamRequest request, bool isOutputVideo, string name) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); @@ -697,10 +757,11 @@ namespace MediaBrowser.Api.Playback.Hls builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ","); - builder.AppendLine(string.Format("hlsdynamic/{0}/{1}.ts{2}", + builder.AppendLine(string.Format("hlsdynamic/{0}/{1}{2}{3}", name, index.ToString(UsCulture), + GetSegmentFileExtension(isOutputVideo), queryString)); seconds -= state.SegmentLength; @@ -716,6 +777,28 @@ namespace MediaBrowser.Api.Playback.Hls protected override string GetAudioArguments(StreamState state) { + if (!state.IsOutputVideo) + { + var audioTranscodeParams = new List(); + if (state.OutputAudioBitrate.HasValue) + { + audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(UsCulture)); + } + + if (state.OutputAudioChannels.HasValue) + { + audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture)); + } + + if (state.OutputAudioSampleRate.HasValue) + { + audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture)); + } + + audioTranscodeParams.Add("-vn"); + return string.Join(" ", audioTranscodeParams.ToArray()); + } + var codec = state.OutputAudioCodec; if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) @@ -746,6 +829,11 @@ namespace MediaBrowser.Api.Playback.Hls protected override string GetVideoArguments(StreamState state) { + if (!state.IsOutputVideo) + { + return string.Empty; + } + var codec = state.OutputVideoCodec; var args = "-codec:v:0 " + codec; @@ -758,31 +846,35 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return state.VideoStream != null && IsH264(state.VideoStream) ? - args + " -bsf:v h264_mp4toannexb" : - args; + args += state.VideoStream != null && IsH264(state.VideoStream) + ? args + " -bsf:v h264_mp4toannexb" + : args; } - - var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", - state.SegmentLength.ToString(UsCulture)); - - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - - args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; - - //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; - - // Add resolution params, if specified - if (!hasGraphicalSubs) + else { - args += GetOutputSizeParam(state, codec, false); + var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", + state.SegmentLength.ToString(UsCulture)); + + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + + args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; + + //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; + + // Add resolution params, if specified + if (!hasGraphicalSubs) + { + args += GetOutputSizeParam(state, codec, false); + } + + // This is for internal graphical subs + if (hasGraphicalSubs) + { + args += GetGraphicalSubtitleParam(state, codec); + } } - // This is for internal graphical subs - if (hasGraphicalSubs) - { - args += GetGraphicalSubtitleParam(state, codec); - } + args += " -flags +loop-global_header -sc_threshold 0"; return args; } @@ -797,7 +889,7 @@ namespace MediaBrowser.Api.Playback.Hls var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; var toTimeParam = string.Empty; - if (state.RunTimeTicks.HasValue) + if (state.RunTimeTicks.HasValue && state.IsOutputVideo) { var startTime = state.Request.StartTimeTicks ?? 0; var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds; @@ -812,46 +904,43 @@ namespace MediaBrowser.Api.Playback.Hls } } - var slowSeekParam = GetSlowSeekCommandLineParameter(state.Request); - if (!string.IsNullOrWhiteSpace(slowSeekParam)) + var timestampOffsetParam = string.Empty; + if (state.IsOutputVideo) { - slowSeekParam = " " + slowSeekParam; + timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture); } + + var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty; - //state.EnableGenericHlsSegmenter = true; + //var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state); - if (state.EnableGenericHlsSegmenter) - { - var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d.ts"; + //return string.Format("{0} {11} {1}{10} -map_metadata -1 -threads {2} {3} {4} {5} -f segment -segment_time {6} -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + // inputModifier, + // GetInputArgument(state), + // threads, + // mapArgs, + // GetVideoArguments(state), + // GetAudioArguments(state), + // state.SegmentLength.ToString(UsCulture), + // startNumberParam, + // outputPath, + // outputTsArg, + // slowSeekParam, + // toTimeParam + // ).Trim(); - return string.Format("{0} {11} {1}{10} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", - inputModifier, - GetInputArgument(state), - threads, - GetMapArgs(state), - GetVideoArguments(state), - GetAudioArguments(state), - state.SegmentLength.ToString(UsCulture), - startNumberParam, - outputPath, - outputTsArg, - slowSeekParam, - toTimeParam - ).Trim(); - } - - return string.Format("{0}{11} {1}{10} -map_metadata -1 -threads {2} {3} {4} -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0) + " -flags -global_header -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + return string.Format("{0}{11} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", inputModifier, GetInputArgument(state), threads, - GetMapArgs(state), + mapArgs, GetVideoArguments(state), + timestampOffsetParam, GetAudioArguments(state), state.SegmentLength.ToString(UsCulture), startNumberParam, state.HlsListSize.ToString(UsCulture), outputPath, - slowSeekParam, toTimeParam ).Trim(); } @@ -872,14 +961,6 @@ namespace MediaBrowser.Api.Playback.Hls } } - protected override bool EnableSlowSeek - { - get - { - return true; - } - } - /// /// Gets the segment file extension. /// @@ -887,7 +968,12 @@ namespace MediaBrowser.Api.Playback.Hls /// System.String. protected override string GetSegmentFileExtension(StreamState state) { - return ".ts"; + return GetSegmentFileExtension(state.IsOutputVideo); + } + + protected string GetSegmentFileExtension(bool isOutputVideo) + { + return isOutputVideo ? ".ts" : ".ts"; } } } diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 5d8c67abe..b44d7f660 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -14,8 +14,10 @@ namespace MediaBrowser.Api.Playback.Hls [Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")] [Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] - public class GetHlsAudioSegment + public class GetHlsAudioSegmentLegacy { + // TODO: Deprecate with new iOS app + /// /// Gets or sets the id. /// @@ -29,12 +31,31 @@ namespace MediaBrowser.Api.Playback.Hls public string SegmentId { get; set; } } + /// + /// Class GetHlsVideoStream + /// + [Route("/Videos/{Id}/stream.m3u8", "GET")] + [Api(Description = "Gets a video stream using HTTP live streaming.")] + public class GetHlsVideoStreamLegacy : VideoStreamRequest + { + // TODO: Deprecate with new iOS app + + [ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? BaselineStreamAudioBitRate { get; set; } + + [ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool AppendBaselineStream { get; set; } + + [ApiMember(Name = "TimeStampOffsetMs", Description = "Optional. Alter the timestamps in the playlist by a given amount, in ms. Default is 1000.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int TimeStampOffsetMs { get; set; } + } + /// /// Class GetHlsVideoSegment /// [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] - public class GetHlsPlaylist + public class GetHlsPlaylistLegacy { // TODO: Deprecate with new iOS app @@ -63,8 +84,10 @@ namespace MediaBrowser.Api.Playback.Hls /// [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] - public class GetHlsVideoSegment : VideoStreamRequest + public class GetHlsVideoSegmentLegacy : VideoStreamRequest { + // TODO: Deprecate with new iOS app + public string PlaylistId { get; set; } /// @@ -85,7 +108,7 @@ namespace MediaBrowser.Api.Playback.Hls _config = config; } - public object Get(GetHlsPlaylist request) + public object Get(GetHlsPlaylistLegacy request) { var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); file = Path.Combine(_appPaths.TranscodingTempPath, file); @@ -103,7 +126,7 @@ namespace MediaBrowser.Api.Playback.Hls /// /// The request. /// System.Object. - public object Get(GetHlsVideoSegment request) + public object Get(GetHlsVideoSegmentLegacy request) { var file = request.SegmentId + Path.GetExtension(Request.PathInfo); file = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, file); @@ -121,7 +144,7 @@ namespace MediaBrowser.Api.Playback.Hls /// /// The request. /// System.Object. - public object Get(GetHlsAudioSegment request) + public object Get(GetHlsAudioSegmentLegacy request) { // TODO: Deprecate with new iOS app var file = request.SegmentId + Path.GetExtension(Request.PathInfo); diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 626df59f2..f21be190f 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -11,25 +11,6 @@ using System; namespace MediaBrowser.Api.Playback.Hls { - /// - /// Class GetHlsVideoStream - /// - [Route("/Videos/{Id}/stream.m3u8", "GET")] - [Api(Description = "Gets a video stream using HTTP live streaming.")] - public class GetHlsVideoStream : VideoStreamRequest - { - // TODO: Deprecate with new iOS app - - [ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? BaselineStreamAudioBitRate { get; set; } - - [ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool AppendBaselineStream { get; set; } - - [ApiMember(Name = "TimeStampOffsetMs", Description = "Optional. Alter the timestamps in the playlist by a given amount, in ms. Default is 1000.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int TimeStampOffsetMs { get; set; } - } - [Route("/Videos/{Id}/live.m3u8", "GET")] [Api(Description = "Gets a video stream using HTTP live streaming.")] public class GetLiveHlsStream : VideoStreamRequest @@ -50,7 +31,7 @@ namespace MediaBrowser.Api.Playback.Hls /// /// The request. /// System.Object. - public object Get(GetHlsVideoStream request) + public object Get(GetHlsVideoStreamLegacy request) { return ProcessRequest(request, false); } diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 27482c50c..283f9671f 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -15,7 +15,7 @@ using System.IO; namespace MediaBrowser.Api.Playback.Progressive { /// - /// Class GetAudioStream + /// Class GetVideoStream /// [Route("/Videos/{Id}/stream.ts", "GET")] [Route("/Videos/{Id}/stream.webm", "GET")] diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 2d1e896db..02b7720a4 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Api.Playback public string InputContainer { get; set; } public MediaSourceInfo MediaSource { get; set; } - + public MediaStream AudioStream { get; set; } public MediaStream VideoStream { get; set; } public MediaStream SubtitleStream { get; set; } @@ -57,6 +57,10 @@ namespace MediaBrowser.Api.Playback public MediaProtocol InputProtocol { get; set; } + public bool IsOutputVideo + { + get { return Request is VideoStreamRequest; } + } public bool IsInputVideo { get; set; } public bool IsInputArchive { get; set; } @@ -66,7 +70,6 @@ namespace MediaBrowser.Api.Playback public List PlayableStreamFileNames { get; set; } public int SegmentLength = 3; - public bool EnableGenericHlsSegmenter = false; public int HlsListSize { get diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index a26c43911..63bb0b52a 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Dlna.Profiles Identification = new DeviceIdentification { - ModelName = "WD TV HD Live", + ModelName = "WD TV", Headers = new [] { diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index 684e61c42..4f8000c3b 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -2,7 +2,7 @@ WDTV Live - WD TV HD Live + WD TV diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 645c1c7d0..92eb0372c 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -158,6 +158,11 @@ namespace MediaBrowser.Model.Dlna if (MediaType == DlnaProfileType.Audio) { + if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) + { + return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + } + return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 2d4770fac..491549d64 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -82,9 +82,9 @@ namespace MediaBrowser.Server.Implementations.IO } // This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called. - // Seeing long delays in some situations, especially over the network. - // Seeing delays up to 40 seconds, but not going to ignore changes for that long. - await Task.Delay(5000).ConfigureAwait(false); + // Seeing long delays in some situations, especially over the network, sometimes up to 45 seconds + // But if we make this delay too high, we risk missing legitimate changes + await Task.Delay(10000).ConfigureAwait(false); string val; _tempIgnoredPaths.TryRemove(path, out val); diff --git a/SharedVersion.cs b/SharedVersion.cs index 653297d70..2a2ce1e51 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -//[assembly: AssemblyVersion("3.0.*")] -[assembly: AssemblyVersion("3.0.5621.1")] +[assembly: AssemblyVersion("3.0.*")] +//[assembly: AssemblyVersion("3.0.5621.1")]