aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model/Dlna/StreamInfo.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Model/Dlna/StreamInfo.cs')
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs1049
1 files changed, 515 insertions, 534 deletions
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 10efb9b38..4414415a2 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -1,3 +1,6 @@
+#nullable disable
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -5,7 +8,6 @@ using System.Linq;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
@@ -25,48 +27,10 @@ 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; }
+
public EncodingContext Context { get; set; }
public DlnaProfileType MediaType { get; set; }
@@ -78,15 +42,23 @@ namespace MediaBrowser.Model.Dlna
public long StartPositionTicks { get; set; }
public int? SegmentLength { get; set; }
+
public int? MinSegments { get; set; }
+
public bool BreakOnNonKeyFrames { get; set; }
public bool RequireAvc { get; set; }
+
public bool RequireNonAnamorphic { get; set; }
+
public bool CopyTimestamps { get; set; }
+
public bool EnableMpegtsM2TsMode { get; set; }
+
public bool EnableSubtitlesInManifest { get; set; }
+
public string[] AudioCodecs { get; set; }
+
public string[] VideoCodecs { get; set; }
public int? AudioStreamIndex { get; set; }
@@ -94,19 +66,25 @@ namespace MediaBrowser.Model.Dlna
public int? SubtitleStreamIndex { get; set; }
public int? TranscodingMaxAudioChannels { get; set; }
+
public int? GlobalMaxAudioChannels { get; set; }
public int? AudioBitrate { get; set; }
+ public int? AudioSampleRate { get; set; }
+
public int? VideoBitrate { get; set; }
public int? MaxWidth { get; set; }
+
public int? MaxHeight { get; set; }
public float? MaxFramerate { get; set; }
public DeviceProfile DeviceProfile { get; set; }
+
public string DeviceProfileId { get; set; }
+
public string DeviceId { get; set; }
public long? RunTimeTicks { get; set; }
@@ -118,396 +96,49 @@ namespace MediaBrowser.Model.Dlna
public MediaSourceInfo MediaSource { get; set; }
public string[] SubtitleCodecs { get; set; }
+
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
+
public string SubtitleFormat { get; set; }
public string PlaySessionId { get; set; }
+
public TranscodeReason[] TranscodeReasons { get; set; }
public Dictionary<string, string> StreamOptions { get; private set; }
- public string MediaSourceId => MediaSource == null ? null : MediaSource.Id;
+ public string MediaSourceId => MediaSource?.Id;
public bool IsDirectStream =>
PlayMethod == PlayMethod.DirectStream ||
PlayMethod == PlayMethod.DirectPlay;
- public string ToUrl(string baseUrl, string accessToken)
- {
- if (PlayMethod == PlayMethod.DirectPlay)
- {
- return MediaSource.Path;
- }
-
- if (string.IsNullOrEmpty(baseUrl))
- {
- throw new ArgumentNullException(nameof(baseUrl));
- }
-
- var list = new List<string>();
- foreach (NameValuePair pair in BuildParams(this, accessToken))
- {
- if (string.IsNullOrEmpty(pair.Value))
- {
- continue;
- }
-
- // Try to keep the url clean by omitting defaults
- if (StringHelper.EqualsIgnoreCase(pair.Name, "StartTimeTicks") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "0"))
- {
- continue;
- }
- if (StringHelper.EqualsIgnoreCase(pair.Name, "SubtitleStreamIndex") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "-1"))
- {
- continue;
- }
- if (StringHelper.EqualsIgnoreCase(pair.Name, "Static") &&
- StringHelper.EqualsIgnoreCase(pair.Value, "false"))
- {
- continue;
- }
-
- var encodedValue = pair.Value.Replace(" ", "%20");
-
- list.Add(string.Format("{0}={1}", pair.Name, encodedValue));
- }
-
- string queryString = string.Join("&", list.ToArray());
-
- return GetUrl(baseUrl, queryString);
- }
-
- private string GetUrl(string baseUrl, string queryString)
- {
- if (string.IsNullOrEmpty(baseUrl))
- {
- throw new ArgumentNullException(nameof(baseUrl));
- }
-
- string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
-
- baseUrl = baseUrl.TrimEnd('/');
-
- if (MediaType == DlnaProfileType.Audio)
- {
- if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
- {
- return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
- }
-
- return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
- }
-
- if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
- {
- return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
- }
-
- return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
- }
-
- private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken)
- {
- 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("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 = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls");
-
- if (isHls)
- {
- list.Add(new NameValuePair("StartTimeTicks", string.Empty));
- }
- else
- {
- list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture)));
- }
-
- 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)
- {
- if (item.RequireNonAnamorphic)
- {
- list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
- }
-
- list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-
- if (item.EnableSubtitlesInManifest)
- {
- list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
- }
-
- if (item.EnableMpegtsM2TsMode)
- {
- list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
- }
-
- if (item.EstimateContentLength)
- {
- list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
- }
-
- if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto)
- {
- list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant()));
- }
-
- 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(" ", "")));
- }
-
- if (!item.IsDirectStream)
- {
- list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()))));
- }
-
- return list;
- }
-
- public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
- {
- return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
- }
-
- public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
- {
- var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
- var newList = new List<SubtitleStreamInfo>();
-
- // First add the selected track
- foreach (SubtitleStreamInfo stream in list)
- {
- if (stream.DeliveryMethod == SubtitleDeliveryMethod.External)
- {
- newList.Add(stream);
- }
- }
-
- return newList;
- }
-
- public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
- {
- 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 = StringHelper.EqualsIgnoreCase(SubProtocol, "hls")
- ? 0
- : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0);
-
- // First add the selected track
- if (SubtitleStreamIndex.HasValue)
- {
- foreach (var stream in MediaSource.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
- {
- AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
- }
- }
- }
-
- if (!includeSelectedTrackOnly)
- {
- foreach (var stream in MediaSource.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
- {
- AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
- }
- }
- }
-
- 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);
- }
- }
-
- private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
- {
- var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol);
- var info = new SubtitleStreamInfo
- {
- IsForced = stream.IsForced,
- Language = stream.Language,
- Name = stream.Language ?? "Unknown",
- Format = subtitleProfile.Format,
- Index = stream.Index,
- DeliveryMethod = subtitleProfile.Method,
- DisplayTitle = stream.DisplayTitle
- };
-
- if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
- {
- if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format) || !stream.IsExternal)
- {
- info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
- baseUrl,
- ItemId,
- MediaSourceId,
- stream.Index.ToString(CultureInfo.InvariantCulture),
- startPositionTicks.ToString(CultureInfo.InvariantCulture),
- subtitleProfile.Format);
-
- if (!string.IsNullOrEmpty(accessToken))
- {
- info.Url += "?api_key=" + accessToken;
- }
-
- info.IsExternalUrl = false;
- }
- else
- {
- info.Url = stream.Path;
- info.IsExternalUrl = true;
- }
- }
-
- return info;
- }
-
/// <summary>
- /// Returns the audio stream that will be used
+ /// Gets the audio stream that will be used.
/// </summary>
- public MediaStream TargetAudioStream
- {
- get
- {
- if (MediaSource != null)
- {
- return MediaSource.GetDefaultAudioStream(AudioStreamIndex);
- }
-
- return null;
- }
- }
+ public MediaStream TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex);
/// <summary>
- /// Returns the video stream that will be used
+ /// Gets the video stream that will be used.
/// </summary>
- public MediaStream TargetVideoStream
- {
- get
- {
- if (MediaSource != null)
- {
- return MediaSource.VideoStream;
- }
-
- return null;
- }
- }
+ public MediaStream TargetVideoStream => MediaSource?.VideoStream;
/// <summary>
- /// Predicts the audio sample rate that will be in the output stream
+ /// Gets the audio sample rate that will be in the output stream.
/// </summary>
public int? TargetAudioSampleRate
{
get
{
var stream = TargetAudioStream;
- return stream == null ? null : stream.SampleRate;
+ return AudioSampleRate.HasValue && !IsDirectStream
+ ? AudioSampleRate
+ : stream?.SampleRate;
}
}
/// <summary>
- /// Predicts the audio sample rate that will be in the output stream
+ /// Gets the audio sample rate that will be in the output stream.
/// </summary>
public int? TargetAudioBitDepth
{
@@ -515,7 +146,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
+ return TargetAudioStream?.BitDepth;
}
var targetAudioCodecs = TargetAudioCodec;
@@ -525,12 +156,12 @@ namespace MediaBrowser.Model.Dlna
return GetTargetAudioBitDepth(audioCodec);
}
- return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
+ return TargetAudioStream?.BitDepth;
}
}
/// <summary>
- /// Predicts the audio sample rate that will be in the output stream
+ /// Gets the audio sample rate that will be in the output stream.
/// </summary>
public int? TargetVideoBitDepth
{
@@ -538,7 +169,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
+ return TargetVideoStream?.BitDepth;
}
var targetVideoCodecs = TargetVideoCodec;
@@ -548,7 +179,7 @@ namespace MediaBrowser.Model.Dlna
return GetTargetVideoBitDepth(videoCodec);
}
- return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
+ return TargetVideoStream?.BitDepth;
}
}
@@ -562,7 +193,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
+ return TargetVideoStream?.RefFrames;
}
var targetVideoCodecs = TargetVideoCodec;
@@ -572,12 +203,12 @@ namespace MediaBrowser.Model.Dlna
return GetTargetRefFrames(videoCodec);
}
- return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
+ return TargetVideoStream?.RefFrames;
}
}
/// <summary>
- /// Predicts the audio sample rate that will be in the output stream
+ /// Gets the audio sample rate that will be in the output stream.
/// </summary>
public float? TargetFramerate
{
@@ -591,7 +222,7 @@ namespace MediaBrowser.Model.Dlna
}
/// <summary>
- /// Predicts the audio sample rate that will be in the output stream
+ /// Gets the audio sample rate that will be in the output stream.
/// </summary>
public double? TargetVideoLevel
{
@@ -599,7 +230,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
+ return TargetVideoStream?.Level;
}
var targetVideoCodecs = TargetVideoCodec;
@@ -609,76 +240,12 @@ namespace MediaBrowser.Model.Dlna
return GetTargetVideoLevel(videoCodec);
}
- return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
+ return TargetVideoStream?.Level;
}
}
- 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? GetTargetAudioBitDepth(string codec)
- {
- var value = GetOption(codec, "audiobitdepth");
- if (string.IsNullOrEmpty(value))
- {
- return null;
- }
-
- if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
- {
- return result;
- }
-
- return null;
- }
-
- public double? GetTargetVideoLevel(string codec)
- {
- var value = GetOption(codec, "level");
- if (string.IsNullOrEmpty(value))
- {
- return null;
- }
-
- if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
- {
- return result;
- }
-
- return null;
- }
-
- public int? GetTargetRefFrames(string codec)
- {
- var value = GetOption(codec, "maxrefframes");
- if (string.IsNullOrEmpty(value))
- {
- return null;
- }
-
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
- {
- return result;
- }
-
- return null;
- }
-
/// <summary>
- /// Predicts the audio sample rate that will be in the output stream
+ /// Gets the audio sample rate that will be in the output stream.
/// </summary>
public int? TargetPacketLength
{
@@ -687,12 +254,12 @@ namespace MediaBrowser.Model.Dlna
var stream = TargetVideoStream;
return !IsDirectStream
? null
- : stream == null ? null : stream.PacketLength;
+ : stream?.PacketLength;
}
}
/// <summary>
- /// Predicts the audio sample rate that will be in the output stream
+ /// Gets the audio sample rate that will be in the output stream.
/// </summary>
public string TargetVideoProfile
{
@@ -700,7 +267,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetVideoStream == null ? null : TargetVideoStream.Profile;
+ return TargetVideoStream?.Profile;
}
var targetVideoCodecs = TargetVideoCodec;
@@ -710,7 +277,7 @@ namespace MediaBrowser.Model.Dlna
return GetOption(videoCodec, "profile");
}
- return TargetVideoStream == null ? null : TargetVideoStream.Profile;
+ return TargetVideoStream?.Profile;
}
}
@@ -725,12 +292,12 @@ namespace MediaBrowser.Model.Dlna
var stream = TargetVideoStream;
return !IsDirectStream
? null
- : stream == null ? null : stream.CodecTag;
+ : stream?.CodecTag;
}
}
/// <summary>
- /// Predicts the audio bitrate that will be in the output stream
+ /// Gets the audio bitrate that will be in the output stream.
/// </summary>
public int? TargetAudioBitrate
{
@@ -739,12 +306,12 @@ namespace MediaBrowser.Model.Dlna
var stream = TargetAudioStream;
return AudioBitrate.HasValue && !IsDirectStream
? AudioBitrate
- : stream == null ? null : stream.BitRate;
+ : stream?.BitRate;
}
}
/// <summary>
- /// Predicts the audio channels that will be in the output stream
+ /// Gets the audio channels that will be in the output stream.
/// </summary>
public int? TargetAudioChannels
{
@@ -752,7 +319,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
+ return TargetAudioStream?.Channels;
}
var targetAudioCodecs = TargetAudioCodec;
@@ -762,30 +329,12 @@ namespace MediaBrowser.Model.Dlna
return GetTargetRefFrames(codec);
}
- return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
- }
- }
-
- public int? GetTargetAudioChannels(string codec)
- {
- var defaultValue = GlobalMaxAudioChannels;
-
- var value = GetOption(codec, "audiochannels");
- if (string.IsNullOrEmpty(value))
- {
- return defaultValue;
- }
-
- if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
- {
- return Math.Min(result, defaultValue ?? result);
+ return TargetAudioStream?.Channels;
}
-
- return defaultValue;
}
/// <summary>
- /// Predicts the audio codec that will be in the output stream
+ /// Gets the audio codec that will be in the output stream.
/// </summary>
public string[] TargetAudioCodec
{
@@ -793,18 +342,18 @@ namespace MediaBrowser.Model.Dlna
{
var stream = TargetAudioStream;
- string inputCodec = stream == null ? null : stream.Codec;
+ string inputCodec = stream?.Codec;
if (IsDirectStream)
{
- return string.IsNullOrEmpty(inputCodec) ? new string[] { } : new[] { inputCodec };
+ return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec };
}
foreach (string codec in AudioCodecs)
{
- if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
+ if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
{
- return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec };
+ return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec };
}
}
@@ -818,18 +367,18 @@ namespace MediaBrowser.Model.Dlna
{
var stream = TargetVideoStream;
- string inputCodec = stream == null ? null : stream.Codec;
+ string inputCodec = stream?.Codec;
if (IsDirectStream)
{
- return string.IsNullOrEmpty(inputCodec) ? new string[] { } : new[] { inputCodec };
+ return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec };
}
foreach (string codec in VideoCodecs)
{
- if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
+ if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
{
- return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec };
+ return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec };
}
}
@@ -838,7 +387,7 @@ namespace MediaBrowser.Model.Dlna
}
/// <summary>
- /// Predicts the audio channels that will be in the output stream
+ /// Gets the audio channels that will be in the output stream.
/// </summary>
public long? TargetSize
{
@@ -876,7 +425,7 @@ namespace MediaBrowser.Model.Dlna
return VideoBitrate.HasValue && !IsDirectStream
? VideoBitrate
- : stream == null ? null : stream.BitRate;
+ : stream?.BitRate;
}
}
@@ -884,7 +433,7 @@ namespace MediaBrowser.Model.Dlna
{
get
{
- var defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts")
+ var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase)
? TransportStreamTimestamp.Valid
: TransportStreamTimestamp.None;
@@ -902,7 +451,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic;
+ return TargetVideoStream?.IsAnamorphic;
}
return false;
@@ -915,7 +464,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
+ return TargetVideoStream?.IsInterlaced;
}
var targetVideoCodecs = TargetVideoCodec;
@@ -928,7 +477,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
+ return TargetVideoStream?.IsInterlaced;
}
}
@@ -938,7 +487,7 @@ namespace MediaBrowser.Model.Dlna
{
if (IsDirectStream)
{
- return TargetVideoStream == null ? null : TargetVideoStream.IsAVC;
+ return TargetVideoStream?.IsAVC;
}
return true;
@@ -991,6 +540,7 @@ namespace MediaBrowser.Model.Dlna
{
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
}
+
return GetMediaStreamCount(MediaStreamType.Video, 1);
}
}
@@ -1003,45 +553,476 @@ namespace MediaBrowser.Model.Dlna
{
return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
}
+
return GetMediaStreamCount(MediaStreamType.Audio, 1);
}
}
- private int? GetMediaStreamCount(MediaStreamType type, int limit)
+ public void SetOption(string qualifier, string name, string value)
{
- var count = MediaSource.GetStreamCount(type);
+ if (string.IsNullOrEmpty(qualifier))
+ {
+ SetOption(name, value);
+ }
+ else
+ {
+ SetOption(qualifier + "-" + name, value);
+ }
+ }
- if (count.HasValue)
+ 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))
{
- count = Math.Min(count.Value, limit);
+ value = GetOption(name);
}
- return count;
+ return value;
}
- public List<MediaStream> GetSelectableAudioStreams()
+ public string GetOption(string name)
{
- return GetSelectableStreams(MediaStreamType.Audio);
+ if (StreamOptions.TryGetValue(name, out var value))
+ {
+ return value;
+ }
+
+ return null;
}
- public List<MediaStream> GetSelectableSubtitleStreams()
+ public string ToUrl(string baseUrl, string accessToken)
{
- return GetSelectableStreams(MediaStreamType.Subtitle);
+ if (PlayMethod == PlayMethod.DirectPlay)
+ {
+ return MediaSource.Path;
+ }
+
+ if (string.IsNullOrEmpty(baseUrl))
+ {
+ throw new ArgumentNullException(nameof(baseUrl));
+ }
+
+ var list = new List<string>();
+ foreach (NameValuePair pair in BuildParams(this, accessToken))
+ {
+ if (string.IsNullOrEmpty(pair.Value))
+ {
+ continue;
+ }
+
+ // Try to keep the url clean by omitting defaults
+ if (string.Equals(pair.Name, "StartTimeTicks", StringComparison.OrdinalIgnoreCase)
+ && string.Equals(pair.Value, "0", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase)
+ && string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase)
+ && string.Equals(pair.Value, "false", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ var encodedValue = pair.Value.Replace(" ", "%20", StringComparison.Ordinal);
+
+ list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue));
+ }
+
+ string queryString = string.Join('&', list);
+
+ return GetUrl(baseUrl, queryString);
+ }
+
+ private string GetUrl(string baseUrl, string queryString)
+ {
+ if (string.IsNullOrEmpty(baseUrl))
+ {
+ throw new ArgumentNullException(nameof(baseUrl));
+ }
+
+ string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
+
+ baseUrl = baseUrl.TrimEnd('/');
+
+ if (MediaType == DlnaProfileType.Audio)
+ {
+ if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
+ }
+
+ 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 List<MediaStream> GetSelectableStreams(MediaStreamType type)
+ private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken)
{
- var list = new List<MediaStream>();
+ 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
+ {
+ list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture)));
+ }
+
+ list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty));
+ list.Add(new NameValuePair("api_key", accessToken ?? string.Empty));
- foreach (var stream in MediaSource.MediaStreams)
+ 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)
{
- if (type == stream.Type)
+ if (item.RequireNonAnamorphic)
{
- list.Add(stream);
+ list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
}
+
+ list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+
+ if (item.EnableSubtitlesInManifest)
+ {
+ list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
+ }
+
+ if (item.EnableMpegtsM2TsMode)
+ {
+ list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
+ }
+
+ if (item.EstimateContentLength)
+ {
+ list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
+ }
+
+ if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto)
+ {
+ list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant()));
+ }
+
+ 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(" ", string.Empty)));
+ }
+
+ if (!item.IsDirectStream)
+ {
+ list.Add(new NameValuePair("TranscodeReasons", string.Join(',', item.TranscodeReasons.Distinct())));
}
return list;
}
+
+ public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+ {
+ return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
+ }
+
+ public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
+ {
+ var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
+ var newList = new List<SubtitleStreamInfo>();
+
+ // First add the selected track
+ foreach (SubtitleStreamInfo stream in list)
+ {
+ if (stream.DeliveryMethod == SubtitleDeliveryMethod.External)
+ {
+ newList.Add(stream);
+ }
+ }
+
+ return newList;
+ }
+
+ public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+ {
+ 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)
+ {
+ foreach (var stream in MediaSource.MediaStreams)
+ {
+ if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
+ {
+ AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
+ }
+ }
+ }
+
+ if (!includeSelectedTrackOnly)
+ {
+ foreach (var stream in MediaSource.MediaStreams)
+ {
+ if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
+ {
+ AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
+ {
+ var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol);
+ var info = new SubtitleStreamInfo
+ {
+ IsForced = stream.IsForced,
+ Language = stream.Language,
+ Name = stream.Language ?? "Unknown",
+ Format = subtitleProfile.Format,
+ Index = stream.Index,
+ DeliveryMethod = subtitleProfile.Method,
+ DisplayTitle = stream.DisplayTitle
+ };
+
+ if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
+ {
+ if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal)
+ {
+ 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);
+
+ if (!string.IsNullOrEmpty(accessToken))
+ {
+ info.Url += "?api_key=" + accessToken;
+ }
+
+ info.IsExternalUrl = false;
+ }
+ else
+ {
+ info.Url = stream.Path;
+ info.IsExternalUrl = true;
+ }
+ }
+
+ 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? GetTargetAudioBitDepth(string codec)
+ {
+ var value = GetOption(codec, "audiobitdepth");
+ if (string.IsNullOrEmpty(value))
+ {
+ return null;
+ }
+
+ if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+ {
+ return result;
+ }
+
+ return null;
+ }
+
+ public double? GetTargetVideoLevel(string codec)
+ {
+ var value = GetOption(codec, "level");
+ if (string.IsNullOrEmpty(value))
+ {
+ return null;
+ }
+
+ if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ {
+ return result;
+ }
+
+ return null;
+ }
+
+ public int? GetTargetRefFrames(string codec)
+ {
+ var value = GetOption(codec, "maxrefframes");
+ if (string.IsNullOrEmpty(value))
+ {
+ return null;
+ }
+
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ {
+ return result;
+ }
+
+ return null;
+ }
+
+ public int? GetTargetAudioChannels(string codec)
+ {
+ var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
+
+ var value = GetOption(codec, "audiochannels");
+ if (string.IsNullOrEmpty(value))
+ {
+ return defaultValue;
+ }
+
+ 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)
+ {
+ var count = MediaSource.GetStreamCount(type);
+
+ if (count.HasValue)
+ {
+ count = Math.Min(count.Value, limit);
+ }
+
+ return count;
+ }
}
}