| 
					
				 | 
			
			
				@@ -27,45 +27,6 @@ namespace MediaBrowser.Model.Dlna 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public void SetOption(string qualifier, string name, string value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(qualifier)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                SetOption(name, value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                SetOption(qualifier + "-" + name, value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public void SetOption(string name, string value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            StreamOptions[name] = value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public string GetOption(string qualifier, string name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var value = GetOption(qualifier + "-" + name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                value = GetOption(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public string GetOption(string name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (StreamOptions.TryGetValue(name, out var value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         public Guid ItemId { get; set; } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         public PlayMethod PlayMethod { get; set; } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -152,887 +113,906 @@ namespace MediaBrowser.Model.Dlna 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             PlayMethod == PlayMethod.DirectStream || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             PlayMethod == PlayMethod.DirectPlay; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public string ToUrl(string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (PlayMethod == PlayMethod.DirectPlay) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return MediaSource.Path; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio stream that will be used. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public MediaStream TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(baseUrl)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the video stream that will be used. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public MediaStream TargetVideoStream => MediaSource?.VideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetAudioSampleRate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                throw new ArgumentNullException(nameof(baseUrl)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetAudioStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return AudioSampleRate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? AudioSampleRate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : stream == null ? null : stream.SampleRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var list = new List<string>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            foreach (NameValuePair pair in BuildParams(this, accessToken)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetAudioBitDepth 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (string.IsNullOrEmpty(pair.Value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // Try to keep the url clean by omitting defaults 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (string.Equals(pair.Name, "StartTimeTicks", StringComparison.OrdinalIgnoreCase) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    string.Equals(pair.Value, "0", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var targetAudioCodecs = TargetAudioCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var audioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!string.IsNullOrEmpty(audioCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetTargetAudioBitDepth(audioCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetVideoBitDepth 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // Be careful, IsDirectStream==true by default (Static != false or not in query). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? true. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    string.Equals(pair.Value, "true", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetTargetVideoBitDepth(videoCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var encodedValue = pair.Value.Replace(" ", "%20"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            string queryString = string.Join("&", list.ToArray()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return GetUrl(baseUrl, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        private string GetUrl(string baseUrl, string queryString) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the target reference frames. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <value>The target reference frames.</value> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetRefFrames 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(baseUrl)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                throw new ArgumentNullException(nameof(baseUrl)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            baseUrl = baseUrl.TrimEnd('/'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (MediaType == DlnaProfileType.Audio) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetTargetRefFrames(videoCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public float? TargetFramerate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var list = new List<NameValuePair>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            string audioCodecs = item.AudioCodecs.Length == 0 ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                string.Empty : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                string.Join(",", item.AudioCodecs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            string videoCodecs = item.VideoCodecs.Length == 0 ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                string.Empty : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                string.Join(",", item.VideoCodecs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("Static", item.IsDirectStream.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("VideoCodec", videoCodecs)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("AudioCodec", audioCodecs)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("AudioSampleRate", item.AudioSampleRate.HasValue ? item.AudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            long startPositionTicks = item.StartPositionTicks; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var isHls = string.Equals(item.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (isHls) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair("StartTimeTicks", string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return MaxFramerate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? MaxFramerate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            string liveStreamId = item.MediaSource?.LiveStreamId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!item.IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public double? TargetVideoLevel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.RequireNonAnamorphic) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.EnableSubtitlesInManifest) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetTargetVideoLevel(videoCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.EnableMpegtsM2TsMode) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.EstimateContentLength) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetPacketLength 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? null 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : stream == null ? null : stream.PacketLength; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string TargetVideoProfile 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetVideoStream == null ? null : TargetVideoStream.Profile; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetOption(videoCodec, "profile"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.CopyTimestamps) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            string subtitleCodecs = item.SubtitleCodecs.Length == 0 ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-               string.Empty : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-               string.Join(",", item.SubtitleCodecs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (isHls) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.SegmentLength.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (item.MinSegments.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            foreach (var pair in item.StreamOptions) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (string.IsNullOrEmpty(pair.Value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // strip spaces to avoid having to encode h264 profile names 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", ""))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return TargetVideoStream == null ? null : TargetVideoStream.Profile; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!item.IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the target video codec tag. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <value>The target video codec tag.</value> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string TargetVideoCodecTag 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString())))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? null 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : stream == null ? null : stream.CodecTag; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return list; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio bitrate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetAudioBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetAudioStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return AudioBitrate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? AudioBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : stream == null ? null : stream.BitRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio channels that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetAudioChannels 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var newList = new List<SubtitleStreamInfo>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // First add the selected track 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            foreach (SubtitleStreamInfo stream in list) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (stream.DeliveryMethod == SubtitleDeliveryMethod.External) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    newList.Add(stream); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return newList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var targetAudioCodecs = TargetAudioCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var codec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!string.IsNullOrEmpty(codec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetTargetRefFrames(codec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio codec that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string[] TargetAudioCodec 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var list = new List<SubtitleStreamInfo>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetAudioStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // HLS will preserve timestamps so we can just grab the full subtitle stream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            long startPositionTicks = string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ? 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                string inputCodec = stream?.Codec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // First add the selected track 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (SubtitleStreamIndex.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                foreach (var stream in MediaSource.MediaStreams) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!includeSelectedTrackOnly) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                foreach (var stream in MediaSource.MediaStreams) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                foreach (string codec in AudioCodecs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return list; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (enableAllProfiles) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                foreach (var profile in DeviceProfile.SubtitleProfiles) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    list.Add(info); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                list.Add(info); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return AudioCodecs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string[] TargetVideoCodec 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var info = new SubtitleStreamInfo 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                IsForced = stream.IsForced, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Language = stream.Language, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Name = stream.Language ?? "Unknown", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Format = subtitleProfile.Format, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Index = stream.Index, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                DeliveryMethod = subtitleProfile.Method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                DisplayTitle = stream.DisplayTitle 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (info.DeliveryMethod == SubtitleDeliveryMethod.External) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                string inputCodec = stream?.Codec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    info.Url = string.Format(CultureInfo.InvariantCulture, "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        baseUrl, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        ItemId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        MediaSourceId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        stream.Index.ToString(CultureInfo.InvariantCulture), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        startPositionTicks.ToString(CultureInfo.InvariantCulture), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        subtitleProfile.Format); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (!string.IsNullOrEmpty(accessToken)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                foreach (string codec in VideoCodecs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        info.Url += "?api_key=" + accessToken; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    info.IsExternalUrl = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    info.Url = stream.Path; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    info.IsExternalUrl = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return info; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return VideoCodecs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Returns the audio stream that will be used. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        /// Gets the audio channels that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public MediaStream TargetAudioStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public long? TargetSize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (MediaSource != null) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return MediaSource.Size; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (RunTimeTicks.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return MediaSource.GetDefaultAudioStream(AudioStreamIndex); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    int? totalBitrate = TargetTotalBitrate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    double totalSeconds = RunTimeTicks.Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    // Convert to ms 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    totalSeconds /= 10000; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    // Convert to seconds 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    totalSeconds /= 1000; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return totalBitrate.HasValue ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        Convert.ToInt64(totalBitrate.Value * totalSeconds) : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        (long?)null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Returns the video stream that will be used. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public MediaStream TargetVideoStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetVideoBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (MediaSource != null) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return MediaSource.VideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return VideoBitrate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? VideoBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : stream == null ? null : stream.BitRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetAudioSampleRate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public TransportStreamTimestamp TargetTimestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetAudioStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return AudioSampleRate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? AudioSampleRate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : stream == null ? null : stream.SampleRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? TransportStreamTimestamp.Valid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : TransportStreamTimestamp.None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ? defaultValue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetAudioBitDepth 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetTotalBitrate => (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public bool? IsTargetAnamorphic 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var targetAudioCodecs = TargetAudioCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var audioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (!string.IsNullOrEmpty(audioCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetTargetAudioBitDepth(audioCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetVideoBitDepth 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public bool? IsTargetInterlaced 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetTargetVideoBitDepth(videoCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Gets the target reference frames. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <value>The target reference frames.</value> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetRefFrames 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public bool? IsTargetAVC 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return TargetVideoStream == null ? null : TargetVideoStream.IsAVC; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetWidth 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var videoStream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetTargetRefFrames(videoCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return size.Width; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return MaxWidth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public float? TargetFramerate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetHeight 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return MaxFramerate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? MaxFramerate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var videoStream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return size.Height; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return MaxHeight; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public double? TargetVideoLevel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetVideoStreamCount 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetTargetVideoLevel(videoCodec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return GetMediaStreamCount(MediaStreamType.Video, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? GetTargetVideoBitDepth(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? TargetAudioStreamCount 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var value = GetOption(codec, "videobitdepth"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return GetMediaStreamCount(MediaStreamType.Audio, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? GetTargetAudioBitDepth(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public void SetOption(string qualifier, string name, string value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var value = GetOption(codec, "audiobitdepth"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(qualifier)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SetOption(name, value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SetOption(qualifier + "-" + name, value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public void SetOption(string name, string value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            StreamOptions[name] = value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public double? GetTargetVideoLevel(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string GetOption(string qualifier, string name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var value = GetOption(codec, "level"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var value = GetOption(qualifier + "-" + name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                value = GetOption(name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string GetOption(string name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (StreamOptions.TryGetValue(name, out var value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? GetTargetRefFrames(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public string ToUrl(string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var value = GetOption(codec, "maxrefframes"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (PlayMethod == PlayMethod.DirectPlay) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return MediaSource.Path; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(baseUrl)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                throw new ArgumentNullException(nameof(baseUrl)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetPacketLength 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var list = new List<string>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (NameValuePair pair in BuildParams(this, accessToken)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? null 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : stream == null ? null : stream.PacketLength; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.IsNullOrEmpty(pair.Value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio sample rate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public string TargetVideoProfile 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // Try to keep the url clean by omitting defaults 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.Equals(pair.Name, "StartTimeTicks", StringComparison.OrdinalIgnoreCase) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    string.Equals(pair.Value, "0", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetVideoStream == null ? null : TargetVideoStream.Profile; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetOption(videoCodec, "profile"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return TargetVideoStream == null ? null : TargetVideoStream.Profile; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // Be careful, IsDirectStream==true by default (Static != false or not in query). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? true. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    string.Equals(pair.Value, "true", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Gets the target video codec tag. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <value>The target video codec tag.</value> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public string TargetVideoCodecTag 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? null 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : stream == null ? null : stream.CodecTag; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var encodedValue = pair.Value.Replace(" ", "%20"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            string queryString = string.Join("&", list.ToArray()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return GetUrl(baseUrl, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio bitrate that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetAudioBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string GetUrl(string baseUrl, string queryString) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(baseUrl)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetAudioStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return AudioBitrate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? AudioBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : stream == null ? null : stream.BitRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                throw new ArgumentNullException(nameof(baseUrl)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio channels that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetAudioChannels 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            baseUrl = baseUrl.TrimEnd('/'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (MediaType == DlnaProfileType.Audio) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var targetAudioCodecs = TargetAudioCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var codec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (!string.IsNullOrEmpty(codec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetTargetRefFrames(codec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? GetTargetAudioChannels(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var value = GetOption(codec, "audiochannels"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return defaultValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var list = new List<NameValuePair>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            string audioCodecs = item.AudioCodecs.Length == 0 ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                string.Empty : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                string.Join(",", item.AudioCodecs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            string videoCodecs = item.VideoCodecs.Length == 0 ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                string.Empty : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                string.Join(",", item.VideoCodecs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("Static", item.IsDirectStream.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("VideoCodec", videoCodecs)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("AudioCodec", audioCodecs)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("AudioSampleRate", item.AudioSampleRate.HasValue ? item.AudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            long startPositionTicks = item.StartPositionTicks; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var isHls = string.Equals(item.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isHls) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return Math.Min(result, defaultValue ?? result); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair("StartTimeTicks", string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return defaultValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio codec that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public string[] TargetAudioCodec 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetAudioStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            string liveStreamId = item.MediaSource?.LiveStreamId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                string inputCodec = stream?.Codec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!item.IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.RequireNonAnamorphic) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                foreach (string codec in AudioCodecs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.EnableSubtitlesInManifest) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return AudioCodecs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public string[] TargetVideoCodec 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.EnableMpegtsM2TsMode) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                string inputCodec = stream?.Codec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.EstimateContentLength) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                foreach (string codec in VideoCodecs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.CopyTimestamps) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return VideoCodecs; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// <summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// Predicts the audio channels that will be in the output stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        /// </summary> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public long? TargetSize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            string subtitleCodecs = item.SubtitleCodecs.Length == 0 ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               string.Empty : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               string.Join(",", item.SubtitleCodecs); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (isHls) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.SegmentLength.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return MediaSource.Size; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (RunTimeTicks.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (item.MinSegments.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    int? totalBitrate = TargetTotalBitrate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    double totalSeconds = RunTimeTicks.Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // Convert to ms 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    totalSeconds /= 10000; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // Convert to seconds 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    totalSeconds /= 1000; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return totalBitrate.HasValue ? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        Convert.ToInt64(totalBitrate.Value * totalSeconds) : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        (long?)null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetVideoBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (var pair in item.StreamOptions) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var stream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (string.IsNullOrEmpty(pair.Value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return VideoBitrate.HasValue && !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? VideoBitrate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : stream == null ? null : stream.BitRate; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // strip spaces to avoid having to encode h264 profile names 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", string.Empty))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public TransportStreamTimestamp TargetTimestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!item.IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? TransportStreamTimestamp.Valid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : TransportStreamTimestamp.None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return !IsDirectStream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ? defaultValue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(new NameValuePair("TranscodeReasons", string.Join(',', item.TranscodeReasons.Distinct()))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return list; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetTotalBitrate => (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public bool? IsTargetAnamorphic 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var newList = new List<SubtitleStreamInfo>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // First add the selected track 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            foreach (SubtitleStreamInfo stream in list) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (stream.DeliveryMethod == SubtitleDeliveryMethod.External) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    newList.Add(stream); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return newList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public bool? IsTargetInterlaced 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var list = new List<SubtitleStreamInfo>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // HLS will preserve timestamps so we can just grab the full subtitle stream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            long startPositionTicks = string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ? 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // First add the selected track 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (SubtitleStreamIndex.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                foreach (var stream in MediaSource.MediaStreams) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var targetVideoCodecs = TargetVideoCodec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (!string.IsNullOrEmpty(videoCodec)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!includeSelectedTrackOnly) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                foreach (var stream in MediaSource.MediaStreams) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return list; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public bool? IsTargetAVC 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (enableAllProfiles) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                foreach (var profile in DeviceProfile.SubtitleProfiles) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return TargetVideoStream == null ? null : TargetVideoStream.IsAVC; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    list.Add(info); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                list.Add(info); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetWidth 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var info = new SubtitleStreamInfo 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var videoStream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                IsForced = stream.IsForced, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Language = stream.Language, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Name = stream.Language ?? "Unknown", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Format = subtitleProfile.Format, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Index = stream.Index, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                DeliveryMethod = subtitleProfile.Method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                DisplayTitle = stream.DisplayTitle 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (info.DeliveryMethod == SubtitleDeliveryMethod.External) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    info.Url = string.Format( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        CultureInfo.InvariantCulture, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        baseUrl, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        ItemId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        MediaSourceId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        stream.Index.ToString(CultureInfo.InvariantCulture), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        startPositionTicks.ToString(CultureInfo.InvariantCulture), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        subtitleProfile.Format); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (!string.IsNullOrEmpty(accessToken)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        info.Url += "?api_key=" + accessToken; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return size.Width; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    info.IsExternalUrl = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    info.Url = stream.Path; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    info.IsExternalUrl = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return MaxWidth; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return info; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? GetTargetVideoBitDepth(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var value = GetOption(codec, "videobitdepth"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetHeight 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? GetTargetAudioBitDepth(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var value = GetOption(codec, "audiobitdepth"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var videoStream = TargetVideoStream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return size.Height; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public double? GetTargetVideoLevel(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var value = GetOption(codec, "level"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return MaxHeight; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetVideoStreamCount 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? GetTargetRefFrames(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var value = GetOption(codec, "maxrefframes"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return GetMediaStreamCount(MediaStreamType.Video, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return result; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public int? TargetAudioStreamCount 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public int? GetTargetAudioChannels(string codec) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var value = GetOption(codec, "audiochannels"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if (IsDirectStream) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return defaultValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return GetMediaStreamCount(MediaStreamType.Audio, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return Math.Min(result, defaultValue ?? result); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return defaultValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private int? GetMediaStreamCount(MediaStreamType type, int limit) 
			 |