using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Session; using System; using System.Collections.Generic; using System.Globalization; namespace MediaBrowser.Model.Dlna { /// /// Class StreamInfo. /// public class StreamInfo { public string ItemId { get; set; } public PlayMethod PlayMethod { get; set; } public EncodingContext Context { get; set; } public DlnaProfileType MediaType { get; set; } public string Container { get; set; } public string SubProtocol { get; set; } public long StartPositionTicks { get; set; } public string VideoCodec { get; set; } public string VideoProfile { get; set; } public bool? Cabac { get; set; } public string AudioCodec { get; set; } public int? AudioStreamIndex { get; set; } public int? SubtitleStreamIndex { get; set; } public int? MaxAudioChannels { get; set; } public int? AudioBitrate { get; set; } public int? VideoBitrate { get; set; } public int? VideoLevel { get; set; } public int? MaxWidth { get; set; } public int? MaxHeight { get; set; } public int? MaxVideoBitDepth { get; set; } public int? MaxRefFrames { 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; } public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public bool EstimateContentLength { get; set; } public MediaSourceInfo MediaSource { get; set; } public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; } public string SubtitleFormat { get; set; } public PlaybackInfoResponse PlaybackInfo { get; set; } public string MediaSourceId { get { return MediaSource == null ? null : MediaSource.Id; } } public bool IsDirectStream { get { return PlayMethod == PlayMethod.DirectStream || PlayMethod == PlayMethod.DirectPlay; } } public string ToUrl(string baseUrl, string accessToken) { return ToDlnaUrl(baseUrl, accessToken); } public string ToDlnaUrl(string baseUrl, string accessToken) { if (PlayMethod == PlayMethod.DirectPlay) { return MediaSource.Path; } if (string.IsNullOrEmpty(baseUrl)) { throw new ArgumentNullException(baseUrl); } string dlnaCommand = BuildDlnaParam(this); string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; baseUrl = baseUrl.TrimEnd('/'); if (MediaType == DlnaProfileType.Audio) { return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand); } if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) { return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, dlnaCommand); } return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand); } private static string BuildDlnaParam(StreamInfo item) { List list = new List { item.DeviceProfileId ?? string.Empty, item.DeviceId ?? string.Empty, item.MediaSourceId ?? string.Empty, (item.IsDirectStream).ToString().ToLower(), item.VideoCodec ?? string.Empty, item.AudioCodec ?? string.Empty, item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty, item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty, item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty, item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty, item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty, item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty, item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty, item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty, StringHelper.ToStringCultureInvariant(item.StartPositionTicks), item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty }; list.Add(item.IsDirectStream ? string.Empty : DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture)); list.Add(item.MaxRefFrames.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxRefFrames.Value) : string.Empty); list.Add(item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty); list.Add(item.VideoProfile ?? string.Empty); list.Add(item.Cabac.HasValue ? item.Cabac.Value.ToString() : string.Empty); string streamId = item.PlaybackInfo == null ? null : item.PlaybackInfo.StreamId; list.Add(streamId ?? string.Empty); return string.Format("Params={0}", string.Join(";", list.ToArray())); } public List GetExternalSubtitles(bool includeSelectedTrackOnly) { List list = new List(); // First add the selected track if (SubtitleStreamIndex.HasValue) { foreach (MediaStream stream in MediaSource.MediaStreams) { if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); if (info != null) { list.Add(info); } } } } if (!includeSelectedTrackOnly) { foreach (MediaStream stream in MediaSource.MediaStreams) { if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); if (info != null) { list.Add(info); } } } } return list; } public List GetExternalSubtitles(string baseUrl, string accessToken, bool includeSelectedTrackOnly) { if (string.IsNullOrEmpty(baseUrl)) { throw new ArgumentNullException(baseUrl); } List list = new List(); // HLS will preserve timestamps so we can just grab the full subtitle stream long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls") ? 0 : StartPositionTicks; // First add the selected track if (SubtitleStreamIndex.HasValue) { foreach (MediaStream stream in MediaSource.MediaStreams) { if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); if (info != null) { list.Add(info); } } } } if (!includeSelectedTrackOnly) { foreach (MediaStream stream in MediaSource.MediaStreams) { if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); if (info != null) { list.Add(info); } } } } return list; } private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks) { SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); if (info != null) { info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", baseUrl, ItemId, MediaSourceId, StringHelper.ToStringCultureInvariant(stream.Index), StringHelper.ToStringCultureInvariant(startPositionTicks), SubtitleFormat); } return info; } private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream) { SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile.SubtitleProfiles, Context); if (subtitleProfile.Method != SubtitleDeliveryMethod.External) { return null; } return new SubtitleStreamInfo { IsForced = stream.IsForced, Language = stream.Language, Name = stream.Language ?? "Unknown", Format = SubtitleFormat, Index = stream.Index }; } /// /// Returns the audio stream that will be used /// public MediaStream TargetAudioStream { get { if (MediaSource != null) { return MediaSource.GetDefaultAudioStream(AudioStreamIndex); } return null; } } /// /// Returns the video stream that will be used /// public MediaStream TargetVideoStream { get { if (MediaSource != null) { return MediaSource.VideoStream; } return null; } } /// /// Predicts the audio sample rate that will be in the output stream /// public int? TargetAudioSampleRate { get { MediaStream stream = TargetAudioStream; return stream == null ? null : stream.SampleRate; } } /// /// Predicts the audio sample rate that will be in the output stream /// public int? TargetVideoBitDepth { get { MediaStream stream = TargetVideoStream; return stream == null || !IsDirectStream ? null : stream.BitDepth; } } /// /// Gets the target reference frames. /// /// The target reference frames. public int? TargetRefFrames { get { MediaStream stream = TargetVideoStream; return stream == null || !IsDirectStream ? null : stream.RefFrames; } } /// /// Predicts the audio sample rate that will be in the output stream /// public float? TargetFramerate { get { MediaStream stream = TargetVideoStream; return MaxFramerate.HasValue && !IsDirectStream ? MaxFramerate : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate; } } /// /// Predicts the audio sample rate that will be in the output stream /// public double? TargetVideoLevel { get { MediaStream stream = TargetVideoStream; return VideoLevel.HasValue && !IsDirectStream ? VideoLevel : stream == null ? null : stream.Level; } } /// /// Predicts the audio sample rate that will be in the output stream /// public int? TargetPacketLength { get { MediaStream stream = TargetVideoStream; return !IsDirectStream ? null : stream == null ? null : stream.PacketLength; } } /// /// Predicts the audio sample rate that will be in the output stream /// public string TargetVideoProfile { get { MediaStream stream = TargetVideoStream; return !string.IsNullOrEmpty(VideoProfile) && !IsDirectStream ? VideoProfile : stream == null ? null : stream.Profile; } } /// /// Predicts the audio bitrate that will be in the output stream /// public int? TargetAudioBitrate { get { MediaStream stream = TargetAudioStream; return AudioBitrate.HasValue && !IsDirectStream ? AudioBitrate : stream == null ? null : stream.BitRate; } } /// /// Predicts the audio channels that will be in the output stream /// public int? TargetAudioChannels { get { MediaStream stream = TargetAudioStream; int? streamChannels = stream == null ? null : stream.Channels; if (MaxAudioChannels.HasValue && !IsDirectStream) { if (streamChannels.HasValue) { return Math.Min(MaxAudioChannels.Value, streamChannels.Value); } return MaxAudioChannels.Value; } return streamChannels; } } /// /// Predicts the audio codec that will be in the output stream /// public string TargetAudioCodec { get { MediaStream stream = TargetAudioStream; return IsDirectStream ? (stream == null ? null : stream.Codec) : AudioCodec; } } /// /// Predicts the audio channels that will be in the output stream /// public long? TargetSize { get { if (IsDirectStream) { return MediaSource.Size; } if (RunTimeTicks.HasValue) { int? totalBitrate = TargetTotalBitrate; double totalSeconds = RunTimeTicks.Value; // Convert to ms totalSeconds /= 10000; // Convert to seconds totalSeconds /= 1000; return totalBitrate.HasValue ? Convert.ToInt64(totalBitrate.Value * totalSeconds) : (long?)null; } return null; } } public int? TargetVideoBitrate { get { MediaStream stream = TargetVideoStream; return VideoBitrate.HasValue && !IsDirectStream ? VideoBitrate : stream == null ? null : stream.BitRate; } } public TransportStreamTimestamp TargetTimestamp { get { TransportStreamTimestamp defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts") ? TransportStreamTimestamp.Valid : TransportStreamTimestamp.None; return !IsDirectStream ? defaultValue : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None; } } public int? TargetTotalBitrate { get { return (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0); } } public bool? IsTargetAnamorphic { get { if (IsDirectStream) { return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic; } return false; } } public bool? IsTargetCabac { get { if (IsDirectStream) { return TargetVideoStream == null ? null : TargetVideoStream.IsCabac; } return true; } } public int? TargetWidth { get { MediaStream videoStream = TargetVideoStream; if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) { ImageSize size = new ImageSize { Width = videoStream.Width.Value, Height = videoStream.Height.Value }; double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null; double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null; ImageSize newSize = DrawingUtils.Resize(size, null, null, maxWidth, maxHeight); return Convert.ToInt32(newSize.Width); } return MaxWidth; } } public int? TargetHeight { get { MediaStream videoStream = TargetVideoStream; if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) { ImageSize size = new ImageSize { Width = videoStream.Width.Value, Height = videoStream.Height.Value }; double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null; double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null; ImageSize newSize = DrawingUtils.Resize(size, null, null, maxWidth, maxHeight); return Convert.ToInt32(newSize.Height); } return MaxHeight; } } public List GetSelectableAudioStreams() { return GetSelectableStreams(MediaStreamType.Audio); } public List GetSelectableSubtitleStreams() { return GetSelectableStreams(MediaStreamType.Subtitle); } public List GetSelectableStreams(MediaStreamType type) { List list = new List(); foreach (MediaStream stream in MediaSource.MediaStreams) { if (type == stream.Type) { list.Add(stream); } } return list; } } }