| 
					
				 | 
			
			
				@@ -5,6 +5,8 @@ using MediaBrowser.Controller.Dlna; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using MediaBrowser.Controller.Library; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using MediaBrowser.Controller.LiveTv; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using MediaBrowser.Controller.MediaEncoding; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using MediaBrowser.Model.Dlna; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using MediaBrowser.Model.Entities; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using MediaBrowser.Model.IO; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using ServiceStack; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using System; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -18,20 +20,20 @@ using System.Threading.Tasks; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 namespace MediaBrowser.Api.Playback.Hls 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    [Route("/Videos/{Id}/master.m3u8", "GET")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    [Api(Description = "Gets a video stream using HTTP live streaming.")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    [Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public class GetMasterHlsVideoStream : VideoStreamRequest 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         public bool EnableAdaptiveBitrateStreaming { get; set; } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public SubtitleDeliveryMethod SubtitleMethod { get; set; } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         public GetMasterHlsVideoStream() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             EnableAdaptiveBitrateStreaming = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    [Route("/Videos/{Id}/main.m3u8", "GET")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    [Api(Description = "Gets a video stream using HTTP live streaming.")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    [Route("/Videos/{Id}/main.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public class GetMainHlsVideoStream : VideoStreamRequest 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -359,7 +361,17 @@ namespace MediaBrowser.Api.Playback.Hls 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             playlistUrl += queryString; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            AppendPlaylist(builder, playlistUrl, totalBitrate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var request = (GetMasterHlsVideoStream) state.Request; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var subtitleStreams = state.AllMediaStreams 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .Where(i => i.IsTextSubtitleStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .ToList(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var subtitleGroup = subtitleStreams.Count > 0 && request.SubtitleMethod == SubtitleDeliveryMethod.Hls ?  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "subs" :  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            AppendPlaylist(builder, playlistUrl, totalBitrate, subtitleGroup); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (EnableAdaptiveBitrateStreaming(state)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -369,16 +381,52 @@ namespace MediaBrowser.Api.Playback.Hls 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var variation = GetBitrateVariation(totalBitrate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var newBitrate = totalBitrate - variation; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate, subtitleGroup); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 variation *= 2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 newBitrate = totalBitrate - variation; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate, subtitleGroup); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!string.IsNullOrWhiteSpace(subtitleGroup)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                AddSubtitles(state, subtitleStreams, builder); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return builder.ToString(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private void AddSubtitles(StreamState state, IEnumerable<MediaStream> subtitles, StringBuilder builder) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var selectedIndex = state.SubtitleStream == null ? (int?)null : state.SubtitleStream.Index; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (var stream in subtitles) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},URI=\"{3}\",LANGUAGE=\"{4}\""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var name = stream.Language; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var isForced = stream.IsForced; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.IsNullOrWhiteSpace(name)) name = stream.Codec ?? "Unknown"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    state.Request.MediaSourceId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    stream.Index.ToString(UsCulture), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    30.ToString(UsCulture)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var line = string.Format(format, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    isDefault ? "YES" : "NO", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    isForced ? "YES" : "NO", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    url, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    stream.Language ?? "Unknown"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                builder.AppendLine(line); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private bool EnableAdaptiveBitrateStreaming(StreamState state) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var request = state.Request as GetMasterHlsVideoStream; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -397,9 +445,16 @@ namespace MediaBrowser.Api.Playback.Hls 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return state.VideoRequest.VideoBitRate.HasValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        private void AppendPlaylist(StringBuilder builder, string url, int bitrate) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var header = "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!string.IsNullOrWhiteSpace(subtitleGroup)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                header += string.Format(",SUBTITLES=\"{0}\"", subtitleGroup); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            builder.AppendLine(header); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             builder.AppendLine(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |