Jelajahi Sumber

fix audio-only hls

Luke Pulverenti 10 tahun lalu
induk
melakukan
f26a639a36

+ 0 - 26
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;
 
         /// <summary>
         /// Gets the fast seek command line parameter.
@@ -161,23 +160,6 @@ namespace MediaBrowser.Api.Playback
             var time = request.StartTimeTicks ?? 0;
 
             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));
             }
@@ -185,14 +167,6 @@ namespace MediaBrowser.Api.Playback
             return string.Empty;
         }
 
-        protected virtual bool EnableSlowSeek
-        {
-            get
-            {
-                return false;
-            }
-        }
-
         /// <summary>
         /// Gets the map args.
         /// </summary>

+ 2 - 2
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

+ 171 - 85
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -30,27 +30,60 @@ namespace MediaBrowser.Api.Playback.Hls
     /// </summary>
     [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
     {
     }
 
-    /// <summary>
-    /// Class GetHlsVideoSegment
-    /// </summary>
     [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; }
+
+        /// <summary>
+        /// Gets or sets the segment id.
+        /// </summary>
+        /// <value>The segment id.</value>
+        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<object> Get(GetMasterHlsVideoStream request)
+        public Task<object> Get(GetMasterHlsVideoPlaylist request)
         {
-            return GetAsync(request, "GET");
+            return GetMasterPlaylistInternal(request, "GET");
         }
 
-        public Task<object> Head(GetMasterHlsVideoStream request)
+        public Task<object> Head(GetMasterHlsVideoPlaylist request)
         {
-            return GetAsync(request, "HEAD");
+            return GetMasterPlaylistInternal(request, "HEAD");
         }
 
-        public Task<object> Get(GetMainHlsVideoStream request)
+        public Task<object> Get(GetMasterHlsAudioPlaylist request)
         {
-            return GetPlaylistAsync(request, "main");
+            return GetMasterPlaylistInternal(request, "GET");
         }
 
-        public Task<object> Get(GetDynamicHlsVideoSegment request)
+        public Task<object> Head(GetMasterHlsAudioPlaylist request)
+        {
+            return GetMasterPlaylistInternal(request, "HEAD");
+        }
+
+        public Task<object> Get(GetVariantHlsVideoPlaylist request)
+        {
+            return GetVariantPlaylistInternal(request, true, "main");
+        }
+
+        public Task<object> Get(GetVariantHlsAudioPlaylist request)
+        {
+            return GetVariantPlaylistInternal(request, false, "main");
+        }
+
+        public Task<object> Get(GetHlsVideoSegment request)
+        {
+            return GetDynamicSegment(request, request.SegmentId);
+        }
+
+        public Task<object> Get(GetHlsAudioSegment request)
         {
             return GetDynamicSegment(request, request.SegmentId);
         }
 
-        private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId)
+        private async Task<object> 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<object> GetSegmentResult(string playlistPath,
@@ -474,7 +527,7 @@ namespace MediaBrowser.Api.Playback.Hls
             });
         }
 
-        private async Task<object> GetAsync(GetMasterHlsVideoStream request, string method)
+        private async Task<object> 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<object> GetPlaylistAsync(VideoStreamRequest request, string name)
+        private async Task<object> 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<string>();
+                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;
             }
+            else
+            {
+                var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
+                    state.SegmentLength.ToString(UsCulture));
 
-            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;
 
-            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
+                args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg;
 
-            args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg;
+                //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
 
-            //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);
+                }
 
-            // 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;
-            }
-        }
-
         /// <summary>
         /// Gets the segment file extension.
         /// </summary>
@@ -887,7 +968,12 @@ namespace MediaBrowser.Api.Playback.Hls
         /// <returns>System.String.</returns>
         protected override string GetSegmentFileExtension(StreamState state)
         {
-            return ".ts";
+            return GetSegmentFileExtension(state.IsOutputVideo);
+        }
+
+        protected string GetSegmentFileExtension(bool isOutputVideo)
+        {
+            return isOutputVideo ? ".ts" : ".ts";
         }
     }
 }

+ 29 - 6
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
+
         /// <summary>
         /// Gets or sets the id.
         /// </summary>
@@ -29,12 +31,31 @@ namespace MediaBrowser.Api.Playback.Hls
         public string SegmentId { get; set; }
     }
 
+    /// <summary>
+    /// Class GetHlsVideoStream
+    /// </summary>
+    [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; }
+    }
+
     /// <summary>
     /// Class GetHlsVideoSegment
     /// </summary>
     [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
     /// </summary>
     [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; }
 
         /// <summary>
@@ -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
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.Object.</returns>
-        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
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.Object.</returns>
-        public object Get(GetHlsAudioSegment request)
+        public object Get(GetHlsAudioSegmentLegacy request)
         {
             // TODO: Deprecate with new iOS app
             var file = request.SegmentId + Path.GetExtension(Request.PathInfo);

+ 1 - 20
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -11,25 +11,6 @@ using System;
 
 namespace MediaBrowser.Api.Playback.Hls
 {
-    /// <summary>
-    /// Class GetHlsVideoStream
-    /// </summary>
-    [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
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.Object.</returns>
-        public object Get(GetHlsVideoStream request)
+        public object Get(GetHlsVideoStreamLegacy request)
         {
             return ProcessRequest(request, false);
         }

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -15,7 +15,7 @@ using System.IO;
 namespace MediaBrowser.Api.Playback.Progressive
 {
     /// <summary>
-    /// Class GetAudioStream
+    /// Class GetVideoStream
     /// </summary>
     [Route("/Videos/{Id}/stream.ts", "GET")]
     [Route("/Videos/{Id}/stream.webm", "GET")]

+ 5 - 2
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<string> PlayableStreamFileNames { get; set; }
 
         public int SegmentLength = 3;
-        public bool EnableGenericHlsSegmenter = false;
         public int HlsListSize
         {
             get

+ 1 - 1
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 []
                 {

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml

@@ -2,7 +2,7 @@
 <Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>WDTV Live</Name>
   <Identification>
-    <ModelName>WD TV HD Live</ModelName>
+    <ModelName>WD TV</ModelName>
     <Headers>
       <HttpHeaderInfo name="User-Agent" value="alphanetworks" match="Substring" />
       <HttpHeaderInfo name="User-Agent" value="ALPHA Networks" match="Substring" />

+ 5 - 0
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);
             }
 

+ 3 - 3
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);

+ 2 - 2
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")]