diff options
Diffstat (limited to 'MediaBrowser.Model/Dlna/StreamInfo.cs')
| -rw-r--r-- | MediaBrowser.Model/Dlna/StreamInfo.cs | 1675 |
1 files changed, 984 insertions, 691 deletions
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 8232ee3fe..3be686088 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -1,9 +1,6 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; using Jellyfin.Data.Enums; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; @@ -11,1007 +8,1303 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Session; -namespace MediaBrowser.Model.Dlna +namespace MediaBrowser.Model.Dlna; + +/// <summary> +/// Class holding information on a stream. +/// </summary> +public class StreamInfo { /// <summary> - /// Class StreamInfo. + /// Initializes a new instance of the <see cref="StreamInfo"/> class. /// </summary> - public class StreamInfo + public StreamInfo() { - public StreamInfo() - { - AudioCodecs = Array.Empty<string>(); - VideoCodecs = Array.Empty<string>(); - SubtitleCodecs = Array.Empty<string>(); - StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - } + AudioCodecs = []; + VideoCodecs = []; + SubtitleCodecs = []; + StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + } - public Guid ItemId { get; set; } + /// <summary> + /// Gets or sets the item id. + /// </summary> + /// <value>The item id.</value> + public Guid ItemId { get; set; } - public PlayMethod PlayMethod { get; set; } + /// <summary> + /// Gets or sets the play method. + /// </summary> + /// <value>The play method.</value> + public PlayMethod PlayMethod { get; set; } - public EncodingContext Context { get; set; } + /// <summary> + /// Gets or sets the encoding context. + /// </summary> + /// <value>The encoding context.</value> + public EncodingContext Context { get; set; } - public DlnaProfileType MediaType { get; set; } + /// <summary> + /// Gets or sets the media type. + /// </summary> + /// <value>The media type.</value> + public DlnaProfileType MediaType { get; set; } - public string? Container { get; set; } + /// <summary> + /// Gets or sets the container. + /// </summary> + /// <value>The container.</value> + public string? Container { get; set; } - public MediaStreamProtocol SubProtocol { get; set; } + /// <summary> + /// Gets or sets the sub protocol. + /// </summary> + /// <value>The sub protocol.</value> + public MediaStreamProtocol SubProtocol { get; set; } - public long StartPositionTicks { get; set; } + /// <summary> + /// Gets or sets the start position ticks. + /// </summary> + /// <value>The start position ticks.</value> + public long StartPositionTicks { get; set; } - public int? SegmentLength { get; set; } + /// <summary> + /// Gets or sets the segment length. + /// </summary> + /// <value>The segment length.</value> + public int? SegmentLength { get; set; } - public int? MinSegments { get; set; } + /// <summary> + /// Gets or sets the minimum segments count. + /// </summary> + /// <value>The minimum segments count.</value> + public int? MinSegments { get; set; } - public bool BreakOnNonKeyFrames { get; set; } + /// <summary> + /// Gets or sets a value indicating whether the stream can be broken on non-keyframes. + /// </summary> + public bool BreakOnNonKeyFrames { get; set; } - public bool RequireAvc { get; set; } + /// <summary> + /// Gets or sets a value indicating whether the stream requires AVC. + /// </summary> + public bool RequireAvc { get; set; } - public bool RequireNonAnamorphic { get; set; } + /// <summary> + /// Gets or sets a value indicating whether the stream requires AVC. + /// </summary> + public bool RequireNonAnamorphic { get; set; } - public bool CopyTimestamps { get; set; } + /// <summary> + /// Gets or sets a value indicating whether timestamps should be copied. + /// </summary> + public bool CopyTimestamps { get; set; } - public bool EnableMpegtsM2TsMode { get; set; } + /// <summary> + /// Gets or sets a value indicating whether timestamps should be copied. + /// </summary> + public bool EnableMpegtsM2TsMode { get; set; } - public bool EnableSubtitlesInManifest { get; set; } + /// <summary> + /// Gets or sets a value indicating whether the subtitle manifest is enabled. + /// </summary> + public bool EnableSubtitlesInManifest { get; set; } - public string[] AudioCodecs { get; set; } + /// <summary> + /// Gets or sets the audio codecs. + /// </summary> + /// <value>The audio codecs.</value> + public IReadOnlyList<string> AudioCodecs { get; set; } - public string[] VideoCodecs { get; set; } + /// <summary> + /// Gets or sets the video codecs. + /// </summary> + /// <value>The video codecs.</value> + public IReadOnlyList<string> VideoCodecs { get; set; } - public int? AudioStreamIndex { get; set; } + /// <summary> + /// Gets or sets the audio stream index. + /// </summary> + /// <value>The audio stream index.</value> + public int? AudioStreamIndex { get; set; } - public int? SubtitleStreamIndex { get; set; } + /// <summary> + /// Gets or sets the video stream index. + /// </summary> + /// <value>The subtitle stream index.</value> + public int? SubtitleStreamIndex { get; set; } - public int? TranscodingMaxAudioChannels { get; set; } + /// <summary> + /// Gets or sets the maximum transcoding audio channels. + /// </summary> + /// <value>The maximum transcoding audio channels.</value> + public int? TranscodingMaxAudioChannels { get; set; } - public int? GlobalMaxAudioChannels { get; set; } + /// <summary> + /// Gets or sets the global maximum audio channels. + /// </summary> + /// <value>The global maximum audio channels.</value> + public int? GlobalMaxAudioChannels { get; set; } - public int? AudioBitrate { get; set; } + /// <summary> + /// Gets or sets the audio bitrate. + /// </summary> + /// <value>The audio bitrate.</value> + public int? AudioBitrate { get; set; } - public int? AudioSampleRate { get; set; } + /// <summary> + /// Gets or sets the audio sample rate. + /// </summary> + /// <value>The audio sample rate.</value> + public int? AudioSampleRate { get; set; } - public int? VideoBitrate { get; set; } + /// <summary> + /// Gets or sets the video bitrate. + /// </summary> + /// <value>The video bitrate.</value> + public int? VideoBitrate { get; set; } - public int? MaxWidth { get; set; } + /// <summary> + /// Gets or sets the maximum output width. + /// </summary> + /// <value>The output width.</value> + public int? MaxWidth { get; set; } - public int? MaxHeight { get; set; } + /// <summary> + /// Gets or sets the maximum output height. + /// </summary> + /// <value>The maximum output height.</value> + public int? MaxHeight { get; set; } - public float? MaxFramerate { get; set; } + /// <summary> + /// Gets or sets the maximum framerate. + /// </summary> + /// <value>The maximum framerate.</value> + public float? MaxFramerate { get; set; } - public required DeviceProfile DeviceProfile { get; set; } + /// <summary> + /// Gets or sets the device profile. + /// </summary> + /// <value>The device profile.</value> + public required DeviceProfile DeviceProfile { get; set; } - public string? DeviceProfileId { get; set; } + /// <summary> + /// Gets or sets the device profile id. + /// </summary> + /// <value>The device profile id.</value> + public string? DeviceProfileId { get; set; } - public string? DeviceId { get; set; } + /// <summary> + /// Gets or sets the device id. + /// </summary> + /// <value>The device id.</value> + public string? DeviceId { get; set; } - public long? RunTimeTicks { get; set; } + /// <summary> + /// Gets or sets the runtime ticks. + /// </summary> + /// <value>The runtime ticks.</value> + public long? RunTimeTicks { get; set; } - public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + /// <summary> + /// Gets or sets the transcode seek info. + /// </summary> + /// <value>The transcode seek info.</value> + public TranscodeSeekInfo TranscodeSeekInfo { get; set; } - public bool EstimateContentLength { get; set; } + /// <summary> + /// Gets or sets a value indicating whether content length should be estimated. + /// </summary> + public bool EstimateContentLength { get; set; } - public MediaSourceInfo? MediaSource { get; set; } + /// <summary> + /// Gets or sets the media source info. + /// </summary> + /// <value>The media source info.</value> + public MediaSourceInfo? MediaSource { get; set; } - public string[] SubtitleCodecs { get; set; } + /// <summary> + /// Gets or sets the subtitle codecs. + /// </summary> + /// <value>The subtitle codecs.</value> + public IReadOnlyList<string> SubtitleCodecs { get; set; } - public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; } + /// <summary> + /// Gets or sets the subtitle delivery method. + /// </summary> + /// <value>The subtitle delivery method.</value> + public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; } - public string? SubtitleFormat { get; set; } + /// <summary> + /// Gets or sets the subtitle format. + /// </summary> + /// <value>The subtitle format.</value> + public string? SubtitleFormat { get; set; } - public string? PlaySessionId { get; set; } + /// <summary> + /// Gets or sets the play session id. + /// </summary> + /// <value>The play session id.</value> + public string? PlaySessionId { get; set; } - public TranscodeReason TranscodeReasons { get; set; } + /// <summary> + /// Gets or sets the transcode reasons. + /// </summary> + /// <value>The transcode reasons.</value> + public TranscodeReason TranscodeReasons { get; set; } - public Dictionary<string, string> StreamOptions { get; private set; } + /// <summary> + /// Gets the stream options. + /// </summary> + /// <value>The stream options.</value> + public Dictionary<string, string> StreamOptions { get; private set; } - public string? MediaSourceId => MediaSource?.Id; + /// <summary> + /// Gets the media source id. + /// </summary> + /// <value>The media source id.</value> + public string? MediaSourceId => MediaSource?.Id; - public bool EnableAudioVbrEncoding { get; set; } + /// <summary> + /// Gets or sets a value indicating whether audio VBR encoding is enabled. + /// </summary> + public bool EnableAudioVbrEncoding { get; set; } - public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay) - && PlayMethod is PlayMethod.DirectStream or PlayMethod.DirectPlay; + /// <summary> + /// Gets a value indicating whether the stream is direct. + /// </summary> + public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay) + && PlayMethod is PlayMethod.DirectStream or PlayMethod.DirectPlay; - /// <summary> - /// Gets the audio stream that will be used. - /// </summary> - public MediaStream? TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex); + /// <summary> + /// Gets the audio stream that will be used in the output stream. + /// </summary> + /// <value>The audio stream.</value> + public MediaStream? TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex); - /// <summary> - /// Gets the video stream that will be used. - /// </summary> - public MediaStream? TargetVideoStream => MediaSource?.VideoStream; + /// <summary> + /// Gets the video stream that will be used in the output stream. + /// </summary> + /// <value>The video stream.</value> + public MediaStream? TargetVideoStream => MediaSource?.VideoStream; - /// <summary> - /// Gets the audio sample rate that will be in the output stream. - /// </summary> - public int? TargetAudioSampleRate + /// <summary> + /// Gets the audio sample rate that will be in the output stream. + /// </summary> + /// <value>The target audio sample rate.</value> + public int? TargetAudioSampleRate + { + get { - get - { - var stream = TargetAudioStream; - return AudioSampleRate.HasValue && !IsDirectStream - ? AudioSampleRate - : stream?.SampleRate; - } + var stream = TargetAudioStream; + return AudioSampleRate.HasValue && !IsDirectStream + ? AudioSampleRate + : stream?.SampleRate; } + } - /// <summary> - /// Gets the audio sample rate that will be in the output stream. - /// </summary> - public int? TargetAudioBitDepth + /// <summary> + /// Gets the audio bit depth that will be in the output stream. + /// </summary> + /// <value>The target bit depth.</value> + public int? TargetAudioBitDepth + { + get { - get + if (IsDirectStream) { - if (IsDirectStream) - { - return TargetAudioStream?.BitDepth; - } - - var targetAudioCodecs = TargetAudioCodec; - var audioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; - if (!string.IsNullOrEmpty(audioCodec)) - { - return GetTargetAudioBitDepth(audioCodec); - } - return TargetAudioStream?.BitDepth; } - } - /// <summary> - /// Gets the audio sample rate that will be in the output stream. - /// </summary> - public int? TargetVideoBitDepth - { - get + var targetAudioCodecs = TargetAudioCodec; + var audioCodec = targetAudioCodecs.Count == 0 ? null : targetAudioCodecs[0]; + if (!string.IsNullOrEmpty(audioCodec)) { - if (IsDirectStream) - { - return TargetVideoStream?.BitDepth; - } - - var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; - if (!string.IsNullOrEmpty(videoCodec)) - { - return GetTargetVideoBitDepth(videoCodec); - } - - return TargetVideoStream?.BitDepth; + return GetTargetAudioBitDepth(audioCodec); } + + return TargetAudioStream?.BitDepth; } + } - /// <summary> - /// Gets the target reference frames. - /// </summary> - /// <value>The target reference frames.</value> - public int? TargetRefFrames + /// <summary> + /// Gets the video bit depth that will be in the output stream. + /// </summary> + /// <value>The target video bit depth.</value> + public int? TargetVideoBitDepth + { + get { - get + if (IsDirectStream) { - if (IsDirectStream) - { - return TargetVideoStream?.RefFrames; - } - - var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; - if (!string.IsNullOrEmpty(videoCodec)) - { - return GetTargetRefFrames(videoCodec); - } + return TargetVideoStream?.BitDepth; + } - return TargetVideoStream?.RefFrames; + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Count == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + return GetTargetVideoBitDepth(videoCodec); } + + return TargetVideoStream?.BitDepth; } + } - /// <summary> - /// Gets the audio sample rate that will be in the output stream. - /// </summary> - public float? TargetFramerate + /// <summary> + /// Gets the target reference frames that will be in the output stream. + /// </summary> + /// <value>The target reference frames.</value> + public int? TargetRefFrames + { + get { - get + if (IsDirectStream) { - var stream = TargetVideoStream; - return MaxFramerate.HasValue && !IsDirectStream - ? MaxFramerate - : stream?.ReferenceFrameRate; + return TargetVideoStream?.RefFrames; } - } - /// <summary> - /// Gets the audio sample rate that will be in the output stream. - /// </summary> - public double? TargetVideoLevel - { - get + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Count == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) { - if (IsDirectStream) - { - return TargetVideoStream?.Level; - } + return GetTargetRefFrames(videoCodec); + } - var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; - if (!string.IsNullOrEmpty(videoCodec)) - { - return GetTargetVideoLevel(videoCodec); - } + return TargetVideoStream?.RefFrames; + } + } - return TargetVideoStream?.Level; - } + /// <summary> + /// Gets the target framerate that will be in the output stream. + /// </summary> + /// <value>The target framerate.</value> + public float? TargetFramerate + { + get + { + var stream = TargetVideoStream; + return MaxFramerate.HasValue && !IsDirectStream + ? MaxFramerate + : stream?.ReferenceFrameRate; } + } - /// <summary> - /// Gets the audio sample rate that will be in the output stream. - /// </summary> - public int? TargetPacketLength + /// <summary> + /// Gets the target video level that will be in the output stream. + /// </summary> + /// <value>The target video level.</value> + public double? TargetVideoLevel + { + get { - get + if (IsDirectStream) { - var stream = TargetVideoStream; - return !IsDirectStream - ? null - : stream?.PacketLength; + return TargetVideoStream?.Level; } - } - /// <summary> - /// Gets the audio sample rate that will be in the output stream. - /// </summary> - public string? TargetVideoProfile - { - get + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Count == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) { - if (IsDirectStream) - { - return TargetVideoStream?.Profile; - } + return GetTargetVideoLevel(videoCodec); + } - var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; - if (!string.IsNullOrEmpty(videoCodec)) - { - return GetOption(videoCodec, "profile"); - } + return TargetVideoStream?.Level; + } + } - return TargetVideoStream?.Profile; - } + /// <summary> + /// Gets the target packet length that will be in the output stream. + /// </summary> + /// <value>The target packet length.</value> + public int? TargetPacketLength + { + get + { + var stream = TargetVideoStream; + return !IsDirectStream + ? null + : stream?.PacketLength; } + } - /// <summary> - /// Gets the target video range type that will be in the output stream. - /// </summary> - public VideoRangeType TargetVideoRangeType + /// <summary> + /// Gets the target video profile that will be in the output stream. + /// </summary> + /// <value>The target video profile.</value> + public string? TargetVideoProfile + { + get { - get + if (IsDirectStream) { - if (IsDirectStream) - { - return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown; - } - - var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; - if (!string.IsNullOrEmpty(videoCodec) - && Enum.TryParse(GetOption(videoCodec, "rangetype"), true, out VideoRangeType videoRangeType)) - { - return videoRangeType; - } + return TargetVideoStream?.Profile; + } - return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown; + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Count == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + return GetOption(videoCodec, "profile"); } + + return TargetVideoStream?.Profile; } + } - /// <summary> - /// Gets the target video codec tag. - /// </summary> - /// <value>The target video codec tag.</value> - public string? TargetVideoCodecTag + /// <summary> + /// Gets the target video range type that will be in the output stream. + /// </summary> + /// <value>The video range type.</value> + public VideoRangeType TargetVideoRangeType + { + get { - get + if (IsDirectStream) { - var stream = TargetVideoStream; - return !IsDirectStream - ? null - : stream?.CodecTag; + return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown; } - } - /// <summary> - /// Gets the audio bitrate that will be in the output stream. - /// </summary> - public int? TargetAudioBitrate - { - get + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Count == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec) + && Enum.TryParse(GetOption(videoCodec, "rangetype"), true, out VideoRangeType videoRangeType)) { - var stream = TargetAudioStream; - return AudioBitrate.HasValue && !IsDirectStream - ? AudioBitrate - : stream?.BitRate; + return videoRangeType; } + + return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown; } + } - /// <summary> - /// Gets the audio channels that will be in the output stream. - /// </summary> - public int? TargetAudioChannels + /// <summary> + /// Gets the target video codec tag. + /// </summary> + /// <value>The video codec tag.</value> + public string? TargetVideoCodecTag + { + get { - get - { - if (IsDirectStream) - { - return TargetAudioStream?.Channels; - } + var stream = TargetVideoStream; + return !IsDirectStream + ? null + : stream?.CodecTag; + } + } - var targetAudioCodecs = TargetAudioCodec; - var codec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; - if (!string.IsNullOrEmpty(codec)) - { - return GetTargetRefFrames(codec); - } + /// <summary> + /// Gets the audio bitrate that will be in the output stream. + /// </summary> + /// <value>The audio bitrate.</value> + public int? TargetAudioBitrate + { + get + { + var stream = TargetAudioStream; + return AudioBitrate.HasValue && !IsDirectStream + ? AudioBitrate + : stream?.BitRate; + } + } + /// <summary> + /// Gets the amount of audio channels that will be in the output stream. + /// </summary> + /// <value>The target audio channels.</value> + public int? TargetAudioChannels + { + get + { + if (IsDirectStream) + { return TargetAudioStream?.Channels; } + + var targetAudioCodecs = TargetAudioCodec; + var codec = targetAudioCodecs.Count == 0 ? null : targetAudioCodecs[0]; + if (!string.IsNullOrEmpty(codec)) + { + return GetTargetRefFrames(codec); + } + + return TargetAudioStream?.Channels; } + } - /// <summary> - /// Gets the audio codec that will be in the output stream. - /// </summary> - public string[] TargetAudioCodec + /// <summary> + /// Gets the audio codec that will be in the output stream. + /// </summary> + /// <value>The audio codec.</value> + public IReadOnlyList<string> TargetAudioCodec + { + get { - get - { - var stream = TargetAudioStream; + var stream = TargetAudioStream; - string? inputCodec = stream?.Codec; + string? inputCodec = stream?.Codec; - if (IsDirectStream) - { - return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec }; - } + if (IsDirectStream) + { + return string.IsNullOrEmpty(inputCodec) ? [] : [inputCodec]; + } - foreach (string codec in AudioCodecs) + foreach (string codec in AudioCodecs) + { + if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) - { - return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec }; - } + return string.IsNullOrEmpty(codec) ? [] : [codec]; } - - return AudioCodecs; } + + return AudioCodecs; } + } - public string[] TargetVideoCodec + /// <summary> + /// Gets the video codec that will be in the output stream. + /// </summary> + /// <value>The target video codec.</value> + public IReadOnlyList<string> TargetVideoCodec + { + get { - get - { - var stream = TargetVideoStream; + var stream = TargetVideoStream; - string? inputCodec = stream?.Codec; + string? inputCodec = stream?.Codec; - if (IsDirectStream) - { - return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec }; - } + if (IsDirectStream) + { + return string.IsNullOrEmpty(inputCodec) ? [] : [inputCodec]; + } - foreach (string codec in VideoCodecs) + foreach (string codec in VideoCodecs) + { + if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) - { - return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec }; - } + return string.IsNullOrEmpty(codec) ? [] : [codec]; } - - return VideoCodecs; } + + return VideoCodecs; } + } - /// <summary> - /// Gets the audio channels that will be in the output stream. - /// </summary> - public long? TargetSize + /// <summary> + /// Gets the target size of the output stream. + /// </summary> + /// <value>The target size.</value> + public long? TargetSize + { + get { - get + if (IsDirectStream) { - if (IsDirectStream) - { - return MediaSource?.Size; - } - - if (RunTimeTicks.HasValue) - { - int? totalBitrate = TargetTotalBitrate; + return MediaSource?.Size; + } - double totalSeconds = RunTimeTicks.Value; - // Convert to ms - totalSeconds /= 10000; - // Convert to seconds - totalSeconds /= 1000; + if (RunTimeTicks.HasValue) + { + int? totalBitrate = TargetTotalBitrate; - return totalBitrate.HasValue ? - Convert.ToInt64(totalBitrate.Value * totalSeconds) : - null; - } + double totalSeconds = RunTimeTicks.Value; + // Convert to ms + totalSeconds /= 10000; + // Convert to seconds + totalSeconds /= 1000; - return null; + return totalBitrate.HasValue ? + Convert.ToInt64(totalBitrate.Value * totalSeconds) : + null; } + + return null; } + } - public int? TargetVideoBitrate + /// <summary> + /// Gets the target video bitrate of the output stream. + /// </summary> + /// <value>The video bitrate.</value> + public int? TargetVideoBitrate + { + get { - get - { - var stream = TargetVideoStream; + var stream = TargetVideoStream; - return VideoBitrate.HasValue && !IsDirectStream - ? VideoBitrate - : stream?.BitRate; - } + return VideoBitrate.HasValue && !IsDirectStream + ? VideoBitrate + : stream?.BitRate; } + } - public TransportStreamTimestamp TargetTimestamp + /// <summary> + /// Gets the target timestamp of the output stream. + /// </summary> + /// <value>The target timestamp.</value> + public TransportStreamTimestamp TargetTimestamp + { + get { - get - { - var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase) - ? TransportStreamTimestamp.Valid - : TransportStreamTimestamp.None; + var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase) + ? TransportStreamTimestamp.Valid + : TransportStreamTimestamp.None; - return !IsDirectStream - ? defaultValue - : MediaSource is null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None; - } + return !IsDirectStream + ? defaultValue + : MediaSource is null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None; } + } - public int? TargetTotalBitrate => (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0); + /// <summary> + /// Gets the target total bitrate of the output stream. + /// </summary> + /// <value>The target total bitrate.</value> + public int? TargetTotalBitrate => (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0); - public bool? IsTargetAnamorphic + /// <summary> + /// Gets a value indicating whether the output stream is anamorphic. + /// </summary> + public bool? IsTargetAnamorphic + { + get { - get + if (IsDirectStream) { - if (IsDirectStream) - { - return TargetVideoStream?.IsAnamorphic; - } - - return false; + return TargetVideoStream?.IsAnamorphic; } + + return false; } + } - public bool? IsTargetInterlaced + /// <summary> + /// Gets a value indicating whether the output stream is interlaced. + /// </summary> + public bool? IsTargetInterlaced + { + get { - get + if (IsDirectStream) { - if (IsDirectStream) - { - return TargetVideoStream?.IsInterlaced; - } - - var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; - if (!string.IsNullOrEmpty(videoCodec)) - { - if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - return TargetVideoStream?.IsInterlaced; } - } - public bool? IsTargetAVC - { - get + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Count == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) { - if (IsDirectStream) + if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase)) { - return TargetVideoStream?.IsAVC; + return false; } - - return true; } + + return TargetVideoStream?.IsInterlaced; } + } - public int? TargetWidth + /// <summary> + /// Gets a value indicating whether the output stream is AVC. + /// </summary> + public bool? IsTargetAVC + { + get { - get + if (IsDirectStream) { - var videoStream = TargetVideoStream; - - if (videoStream is not 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.Width; - } - - return MaxWidth; + return TargetVideoStream?.IsAVC; } + + return true; } + } - public int? TargetHeight + /// <summary> + /// Gets the target width of the output stream. + /// </summary> + /// <value>The target width.</value> + public int? TargetWidth + { + get { - get - { - var videoStream = TargetVideoStream; - - if (videoStream is not null && videoStream.Width.HasValue && videoStream.Height.HasValue) - { - ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value); + var videoStream = TargetVideoStream; - size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0); + if (videoStream is not null && videoStream.Width.HasValue && videoStream.Height.HasValue) + { + ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value); - return size.Height; - } + size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0); - return MaxHeight; + return size.Width; } + + return MaxWidth; } + } - public int? TargetVideoStreamCount + /// <summary> + /// Gets the target height of the output stream. + /// </summary> + /// <value>The target height.</value> + public int? TargetHeight + { + get { - get + var videoStream = TargetVideoStream; + + if (videoStream is not null && videoStream.Width.HasValue && videoStream.Height.HasValue) { - if (IsDirectStream) - { - return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); - } + ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value); - return GetMediaStreamCount(MediaStreamType.Video, 1); + size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0); + + return size.Height; } + + return MaxHeight; } + } - public int? TargetAudioStreamCount + /// <summary> + /// Gets the target video stream count of the output stream. + /// </summary> + /// <value>The target video stream count.</value> + public int? TargetVideoStreamCount + { + get { - get + if (IsDirectStream) { - if (IsDirectStream) - { - return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); - } - - return GetMediaStreamCount(MediaStreamType.Audio, 1); + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); } + + return GetMediaStreamCount(MediaStreamType.Video, 1); } + } - public void SetOption(string? qualifier, string name, string value) + /// <summary> + /// Gets the target audio stream count of the output stream. + /// </summary> + /// <value>The target audio stream count.</value> + public int? TargetAudioStreamCount + { + get { - if (string.IsNullOrEmpty(qualifier)) - { - SetOption(name, value); - } - else + if (IsDirectStream) { - SetOption(qualifier + "-" + name, value); + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); } + + return GetMediaStreamCount(MediaStreamType.Audio, 1); } + } - public void SetOption(string name, string value) + /// <summary> + /// Sets a stream option. + /// </summary> + /// <param name="qualifier">The qualifier.</param> + /// <param name="name">The name.</param> + /// <param name="value">The value.</param> + public void SetOption(string? qualifier, string name, string value) + { + if (string.IsNullOrEmpty(qualifier)) { - StreamOptions[name] = value; + SetOption(name, value); } - - public string? GetOption(string? qualifier, string name) + else { - var value = GetOption(qualifier + "-" + name); + SetOption(qualifier + "-" + name, value); + } + } - if (string.IsNullOrEmpty(value)) - { - value = GetOption(name); - } + /// <summary> + /// Sets a stream option. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="value">The value.</param> + public void SetOption(string name, string value) + { + StreamOptions[name] = value; + } - return value; - } + /// <summary> + /// Gets a stream option. + /// </summary> + /// <param name="qualifier">The qualifier.</param> + /// <param name="name">The name.</param> + /// <returns>The value.</returns> + public string? GetOption(string? qualifier, string name) + { + var value = GetOption(qualifier + "-" + name); - public string? GetOption(string name) + if (string.IsNullOrEmpty(value)) { - if (StreamOptions.TryGetValue(name, out var value)) - { - return value; - } - - return null; + value = GetOption(name); } - public string ToUrl(string baseUrl, string? accessToken) + return value; + } + + /// <summary> + /// Gets a stream option. + /// </summary> + /// <param name="name">The name.</param> + /// <returns>The value.</returns> + public string? GetOption(string name) + { + if (StreamOptions.TryGetValue(name, out var value)) { - ArgumentException.ThrowIfNullOrEmpty(baseUrl); + return value; + } - var list = new List<string>(); - foreach (NameValuePair pair in BuildParams(this, accessToken)) - { - if (string.IsNullOrEmpty(pair.Value)) - { - continue; - } + return null; + } - // 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; - } + /// <summary> + /// Returns this output stream URL for this class. + /// </summary> + /// <param name="baseUrl">The base Url.</param> + /// <param name="accessToken">The access Token.</param> + /// <returns>A querystring representation of this object.</returns> + public string ToUrl(string baseUrl, string? accessToken) + { + ArgumentException.ThrowIfNullOrEmpty(baseUrl); - if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) - && string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase)) - { - continue; - } + List<string> list = []; + foreach (NameValuePair pair in BuildParams(this, accessToken)) + { + if (string.IsNullOrEmpty(pair.Value)) + { + continue; + } - if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) - && string.Equals(pair.Value, "false", StringComparison.OrdinalIgnoreCase)) - { - 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; + } - var encodedValue = pair.Value.Replace(" ", "%20", StringComparison.Ordinal); + if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) + && string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase)) + { + continue; + } - list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue)); + if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) + && string.Equals(pair.Value, "false", StringComparison.OrdinalIgnoreCase)) + { + continue; } - string queryString = string.Join('&', list); + var encodedValue = pair.Value.Replace(" ", "%20", StringComparison.Ordinal); - return GetUrl(baseUrl, queryString); + list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue)); } - private string GetUrl(string baseUrl, string queryString) - { - ArgumentException.ThrowIfNullOrEmpty(baseUrl); + string queryString = string.Join('&', list); - string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; + return GetUrl(baseUrl, queryString); + } - baseUrl = baseUrl.TrimEnd('/'); + private string GetUrl(string baseUrl, string queryString) + { + ArgumentException.ThrowIfNullOrEmpty(baseUrl); - if (MediaType == DlnaProfileType.Audio) - { - if (SubProtocol == MediaStreamProtocol.hls) - { - return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); - } + string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; - return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); - } + baseUrl = baseUrl.TrimEnd('/'); + if (MediaType == DlnaProfileType.Audio) + { if (SubProtocol == MediaStreamProtocol.hls) { - return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); } - return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); + return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } - private static IEnumerable<NameValuePair> BuildParams(StreamInfo item, string? accessToken) + if (SubProtocol == MediaStreamProtocol.hls) { - var list = new List<NameValuePair>(); + return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + } - string audioCodecs = item.AudioCodecs.Length == 0 ? - string.Empty : - string.Join(',', item.AudioCodecs); + return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); + } - string videoCodecs = item.VideoCodecs.Length == 0 ? - string.Empty : - string.Join(',', item.VideoCodecs); + private static List<NameValuePair> BuildParams(StreamInfo item, string? accessToken) + { + List<NameValuePair> list = []; + + string audioCodecs = item.AudioCodecs.Count == 0 ? + string.Empty : + string.Join(',', item.AudioCodecs); + + string videoCodecs = item.VideoCodecs.Count == 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; + + if (item.SubProtocol == MediaStreamProtocol.hls) + { + list.Add(new NameValuePair("StartTimeTicks", string.Empty)); + } + else + { + list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture))); + } - 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("PlaySessionId", item.PlaySessionId ?? string.Empty)); + list.Add(new NameValuePair("api_key", accessToken ?? 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)); + string? liveStreamId = item.MediaSource?.LiveStreamId; + list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); - long startPositionTicks = item.StartPositionTicks; + list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); - if (item.SubProtocol == MediaStreamProtocol.hls) - { - list.Add(new NameValuePair("StartTimeTicks", string.Empty)); - } - else + if (!item.IsDirectStream) + { + if (item.RequireNonAnamorphic) { - list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture))); + list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); } - 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)); + list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); - if (!item.IsDirectStream) + if (item.EnableSubtitlesInManifest) { - 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())); - } + list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); + } - if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto) - { - list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant())); - } + if (item.EnableMpegtsM2TsMode) + { + list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); + } - if (item.CopyTimestamps) - { - list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); - } + if (item.EstimateContentLength) + { + list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); + } - list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); + if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto) + { + list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant())); + } - list.Add(new NameValuePair("EnableAudioVbrEncoding", item.EnableAudioVbrEncoding.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); + if (item.CopyTimestamps) + { + list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); } - list.Add(new NameValuePair("Tag", item.MediaSource?.ETag ?? string.Empty)); + list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); - string subtitleCodecs = item.SubtitleCodecs.Length == 0 ? - string.Empty : - string.Join(",", item.SubtitleCodecs); + list.Add(new NameValuePair("EnableAudioVbrEncoding", item.EnableAudioVbrEncoding.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); + } - list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty)); + list.Add(new NameValuePair("Tag", item.MediaSource?.ETag ?? string.Empty)); - if (item.SubProtocol == MediaStreamProtocol.hls) - { - list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); + string subtitleCodecs = item.SubtitleCodecs.Count == 0 ? + string.Empty : + string.Join(",", item.SubtitleCodecs); - if (item.SegmentLength.HasValue) - { - list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); - } + list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty)); - if (item.MinSegments.HasValue) - { - list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); - } + if (item.SubProtocol == MediaStreamProtocol.hls) + { + list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); - list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture))); + if (item.SegmentLength.HasValue) + { + list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); } - foreach (var pair in item.StreamOptions) + if (item.MinSegments.HasValue) { - 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, StringComparison.Ordinal))); + list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); } - if (!item.IsDirectStream) + list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture))); + } + + foreach (var pair in item.StreamOptions) + { + if (string.IsNullOrEmpty(pair.Value)) { - list.Add(new NameValuePair("TranscodeReasons", item.TranscodeReasons.ToString())); + continue; } - return list; + // strip spaces to avoid having to encode h264 profile names + list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", string.Empty, StringComparison.Ordinal))); } - public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string? accessToken) + if (!item.IsDirectStream) { - return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); + list.Add(new NameValuePair("TranscodeReasons", item.TranscodeReasons.ToString())); } - public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string? accessToken) + return list; + } + + /// <summary> + /// Gets the subtitle profiles. + /// </summary> + /// <param name="transcoderSupport">The transcoder support.</param> + /// <param name="includeSelectedTrackOnly">If only the selected track should be included.</param> + /// <param name="baseUrl">The base URL.</param> + /// <param name="accessToken">The access token.</param> + /// <returns>The <see cref="SubtitleStreamInfo"/> of the profiles.</returns> + public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string? accessToken) + { + return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); + } + + /// <summary> + /// Gets the subtitle profiles. + /// </summary> + /// <param name="transcoderSupport">The transcoder support.</param> + /// <param name="includeSelectedTrackOnly">If only the selected track should be included.</param> + /// <param name="enableAllProfiles">If all profiles are enabled.</param> + /// <param name="baseUrl">The base URL.</param> + /// <param name="accessToken">The access token.</param> + /// <returns>The <see cref="SubtitleStreamInfo"/> of the profiles.</returns> + public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string? accessToken) + { + if (MediaSource is null) { - if (MediaSource is null) - { - return Enumerable.Empty<SubtitleStreamInfo>(); - } + return []; + } - var list = new List<SubtitleStreamInfo>(); + List<SubtitleStreamInfo> list = []; - // HLS will preserve timestamps so we can just grab the full subtitle stream - long startPositionTicks = SubProtocol == MediaStreamProtocol.hls - ? 0 - : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0); + // HLS will preserve timestamps so we can just grab the full subtitle stream + long startPositionTicks = SubProtocol == MediaStreamProtocol.hls + ? 0 + : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0); - // First add the selected track - if (SubtitleStreamIndex.HasValue) + // First add the selected track + if (SubtitleStreamIndex.HasValue) + { + foreach (var stream in MediaSource.MediaStreams) { - foreach (var stream in MediaSource.MediaStreams) + if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) { - if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) - { - AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); - } + AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); } } + } - if (!includeSelectedTrackOnly) + if (!includeSelectedTrackOnly) + { + foreach (var stream in MediaSource.MediaStreams) { - foreach (var stream in MediaSource.MediaStreams) + if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) { - if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) - { - AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); - } + 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) + return list; + } + + private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string? accessToken, long startPositionTicks) + { + if (enableAllProfiles) { - if (enableAllProfiles) + foreach (var profile in DeviceProfile.SubtitleProfiles) { - foreach (var profile in DeviceProfile.SubtitleProfiles) - { - var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport); - if (info is not null) - { - list.Add(info); - } - } - } - else - { - var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport); + var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport); if (info is not null) { list.Add(info); } } } - - private SubtitleStreamInfo? GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string? accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) + else { - if (MediaSource is null) + var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport); + if (info is not null) { - return null; + list.Add(info); } + } + } - 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 - }; + private SubtitleStreamInfo? GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string? accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) + { + if (MediaSource is null) + { + return null; + } - if (info.DeliveryMethod == SubtitleDeliveryMethod.External) + 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) { - 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 = 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; + info.Url += "?api_key=" + accessToken; } - else - { - info.Url = stream.Path; - info.IsExternalUrl = true; - } - } - - return info; - } - public int? GetTargetVideoBitDepth(string? codec) - { - var value = GetOption(codec, "videobitdepth"); - - if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) + info.IsExternalUrl = false; + } + else { - return result; + info.Url = stream.Path; + info.IsExternalUrl = true; } - - return null; } - public int? GetTargetAudioBitDepth(string? codec) - { - var value = GetOption(codec, "audiobitdepth"); + return info; + } - if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) - { - return result; - } + /// <summary> + /// Gets the target video bit depth. + /// </summary> + /// <param name="codec">The codec.</param> + /// <returns>The target video bit depth.</returns> + public int? GetTargetVideoBitDepth(string? codec) + { + var value = GetOption(codec, "videobitdepth"); - return null; + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) + { + return result; } - public double? GetTargetVideoLevel(string? codec) - { - var value = GetOption(codec, "level"); + return null; + } - if (double.TryParse(value, CultureInfo.InvariantCulture, out var result)) - { - return result; - } + /// <summary> + /// Gets the target audio bit depth. + /// </summary> + /// <param name="codec">The codec.</param> + /// <returns>The target audio bit depth.</returns> + public int? GetTargetAudioBitDepth(string? codec) + { + var value = GetOption(codec, "audiobitdepth"); - return null; + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) + { + return result; } - public int? GetTargetRefFrames(string? codec) - { - var value = GetOption(codec, "maxrefframes"); + return null; + } - if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) - { - return result; - } + /// <summary> + /// Gets the target video level. + /// </summary> + /// <param name="codec">The codec.</param> + /// <returns>The target video level.</returns> + public double? GetTargetVideoLevel(string? codec) + { + var value = GetOption(codec, "level"); - return null; + if (double.TryParse(value, CultureInfo.InvariantCulture, out var result)) + { + return result; } - public int? GetTargetAudioChannels(string? codec) + return null; + } + + /// <summary> + /// Gets the target reference frames. + /// </summary> + /// <param name="codec">The codec.</param> + /// <returns>The target reference frames.</returns> + public int? GetTargetRefFrames(string? codec) + { + var value = GetOption(codec, "maxrefframes"); + + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { - var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels; + return result; + } - var value = GetOption(codec, "audiochannels"); - if (string.IsNullOrEmpty(value)) - { - return defaultValue; - } + return null; + } - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) - { - return Math.Min(result, defaultValue ?? result); - } + /// <summary> + /// Gets the target audio channels. + /// </summary> + /// <param name="codec">The codec.</param> + /// <returns>The target audio channels.</returns> + public int? GetTargetAudioChannels(string? codec) + { + var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels; + var value = GetOption(codec, "audiochannels"); + if (string.IsNullOrEmpty(value)) + { return defaultValue; } - private int? GetMediaStreamCount(MediaStreamType type, int limit) + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { - var count = MediaSource?.GetStreamCount(type); + return Math.Min(result, defaultValue ?? result); + } - if (count.HasValue) - { - count = Math.Min(count.Value, limit); - } + return defaultValue; + } + + /// <summary> + /// Gets the media stream count. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="limit">The limit.</param> + /// <returns>The media stream count.</returns> + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource?.GetStreamCount(type); - return count; + if (count.HasValue) + { + count = Math.Min(count.Value, limit); } + + return count; } } |
