private bool IsEligibleForDirectPlay(MediaSourceInfo item, int?maxBitrate, MediaStream subtitleStream, VideoOptions options) { if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile, options.Context); if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) { return(false); } } return(IsAudioEligibleForDirectPlay(item, maxBitrate)); }
private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream) { SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile, 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 }); }
private bool IsEligibleForDirectPlay(MediaSourceInfo item, int?maxBitrate, MediaStream subtitleStream, VideoOptions options, PlayMethod playMethod) { if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, playMethod); if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) { _logger.Info("Not eligible for {0} due to unsupported subtitles", playMethod); return(false); } } return(IsAudioEligibleForDirectPlay(item, maxBitrate)); }
private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) { SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, SubProtocol, Container); SubtitleStreamInfo 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); }
private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles) { SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, Context, PlayMethod); SubtitleStreamInfo info = new SubtitleStreamInfo { IsForced = stream.IsForced, Language = stream.Language, Name = stream.Language ?? "Unknown", Format = subtitleProfile.Format, Index = stream.Index, DeliveryMethod = subtitleProfile.Method }; if (info.DeliveryMethod == SubtitleDeliveryMethod.External) { if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format)) { info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", baseUrl, ItemId, MediaSourceId, StringHelper.ToStringCultureInvariant(stream.Index), StringHelper.ToStringCultureInvariant(startPositionTicks), subtitleProfile.Format); info.IsExternalUrl = false; } else { info.Url = stream.Path; info.IsExternalUrl = true; } } return(info); }
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, MediaType = DlnaProfileType.Video, MediaSource = item, RunTimeTicks = item.RunTimeTicks, Context = options.Context, DeviceProfile = options.Profile }; playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles); MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; MediaStream audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex); int? audioStreamIndex = null; if (audioStream != null) { audioStreamIndex = audioStream.Index; } MediaStream videoStream = item.VideoStream; // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay); bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream); _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", options.Profile.Name ?? "Unknown Profile", item.Path ?? "Unknown path", isEligibleForDirectPlay, isEligibleForDirectStream); if (isEligibleForDirectPlay || isEligibleForDirectStream) { // See if it can be direct played PlayMethod?directPlay = GetVideoDirectPlayProfile(options.Profile, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream); if (directPlay != null) { playlistItem.PlayMethod = directPlay.Value; playlistItem.Container = item.Container; if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; } return(playlistItem); } } // Can't direct play, find the transcoding profile TranscodingProfile transcodingProfile = null; foreach (TranscodingProfile i in options.Profile.TranscodingProfiles) { if (i.Type == playlistItem.MediaType && i.Context == options.Context) { transcodingProfile = i; break; } } if (transcodingProfile != null) { if (!item.SupportsTranscoding) { return(null); } if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; } playlistItem.PlayMethod = PlayMethod.Transcode; playlistItem.Container = transcodingProfile.Container; playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(','); playlistItem.VideoCodec = transcodingProfile.VideoCodec; playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps; playlistItem.ForceLiveStream = transcodingProfile.ForceLiveStream; if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)) { int transcodingMaxAudioChannels; if (IntHelper.TryParseCultureInvariant(transcodingProfile.MaxAudioChannels, out transcodingMaxAudioChannels)) { playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels; } } playlistItem.SubProtocol = transcodingProfile.Protocol; playlistItem.AudioStreamIndex = audioStreamIndex; List <ProfileCondition> videoTranscodingConditions = new List <ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec, transcodingProfile.Container)) { foreach (ProfileCondition c in i.Conditions) { videoTranscodingConditions.Add(c); } break; } } ApplyTranscodingConditions(playlistItem, videoTranscodingConditions); List <ProfileCondition> audioTranscodingConditions = new List <ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.VideoAudio && i.ContainsCodec(playlistItem.TargetAudioCodec, transcodingProfile.Container)) { foreach (ProfileCondition c in i.Conditions) { audioTranscodingConditions.Add(c); } break; } } ApplyTranscodingConditions(playlistItem, audioTranscodingConditions); // Honor requested max channels if (options.MaxAudioChannels.HasValue) { int currentValue = playlistItem.MaxAudioChannels ?? options.MaxAudioChannels.Value; playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); } int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream); playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate); int?maxBitrateSetting = options.GetMaxBitrate(); // Honor max rate if (maxBitrateSetting.HasValue) { int videoBitrate = maxBitrateSetting.Value; if (playlistItem.AudioBitrate.HasValue) { videoBitrate -= playlistItem.AudioBitrate.Value; } // Make sure the video bitrate is lower than bitrate settings but at least 64k int currentValue = playlistItem.VideoBitrate ?? videoBitrate; playlistItem.VideoBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000); } } return(playlistItem); }
public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, EncodingContext context) { // Look for an external profile that matches the stream type (text/graphical) foreach (SubtitleProfile profile in subtitleProfiles) { bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); if (!profile.SupportsLanguage(subtitleStream.Language)) { continue; } if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { if (!requiresConversion) { return profile; } if (subtitleStream.SupportsExternalStream) { return profile; } // For sync we can handle the longer extraction times if (context == EncodingContext.Static && subtitleStream.IsTextSubtitleStream) { return profile; } } } foreach (SubtitleProfile profile in subtitleProfiles) { bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); if (!profile.SupportsLanguage(subtitleStream.Language)) { continue; } if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { if (!requiresConversion) { return profile; } return profile; } } return new SubtitleProfile { Method = SubtitleDeliveryMethod.Encode, Format = subtitleStream.Codec }; }
private int? GetDefaultSubtitleStreamIndex(MediaSourceInfo item, SubtitleProfile[] subtitleProfiles) { int highestScore = -1; foreach (MediaStream stream in item.MediaStreams) { if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue) { if (stream.Score.Value > highestScore) { highestScore = stream.Score.Value; } } } List<MediaStream> topStreams = new List<MediaStream>(); foreach (MediaStream stream in item.MediaStreams) { if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue && stream.Score.Value == highestScore) { topStreams.Add(stream); } } // If multiple streams have an equal score, try to pick the most efficient one if (topStreams.Count > 1) { foreach (MediaStream stream in topStreams) { foreach (SubtitleProfile profile in subtitleProfiles) { if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec)) { return stream.Index; } } } } // If no optimization panned out, just use the original default return item.DefaultSubtitleStreamIndex; }
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, MediaType = DlnaProfileType.Video, MediaSource = item, RunTimeTicks = item.RunTimeTicks, Context = options.Context, DeviceProfile = options.Profile }; int?audioStreamIndex = options.AudioStreamIndex ?? item.DefaultAudioStreamIndex; playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? item.DefaultSubtitleStreamIndex; MediaStream audioStream = audioStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Audio, audioStreamIndex.Value) : null; MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; MediaStream videoStream = item.VideoStream; int?maxBitrateSetting = options.GetMaxBitrate(); if (IsEligibleForDirectPlay(item, maxBitrateSetting, subtitleStream, options)) { // See if it can be direct played var directPlay = GetVideoDirectPlayProfile(options, options.Profile, item, videoStream, audioStream); if (directPlay != null) { playlistItem.PlayMethod = directPlay.Value; playlistItem.Container = item.Container; if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile, options.Context); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; } return(playlistItem); } } // Can't direct play, find the transcoding profile TranscodingProfile transcodingProfile = null; foreach (TranscodingProfile i in options.Profile.TranscodingProfiles) { if (i.Type == playlistItem.MediaType && i.Context == options.Context) { transcodingProfile = i; break; } } if (transcodingProfile != null) { if (!item.SupportsTranscoding) { return(null); } if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile, options.Context); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; } playlistItem.PlayMethod = PlayMethod.Transcode; playlistItem.Container = transcodingProfile.Container; playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',')[0]; playlistItem.VideoCodec = transcodingProfile.VideoCodec; playlistItem.Protocol = transcodingProfile.Protocol; playlistItem.AudioStreamIndex = audioStreamIndex; List <ProfileCondition> videoTranscodingConditions = new List <ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec)) { foreach (ProfileCondition c in i.Conditions) { videoTranscodingConditions.Add(c); } break; } } ApplyTranscodingConditions(playlistItem, videoTranscodingConditions); List <ProfileCondition> audioTranscodingConditions = new List <ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.VideoAudio && i.ContainsCodec(transcodingProfile.AudioCodec)) { foreach (ProfileCondition c in i.Conditions) { audioTranscodingConditions.Add(c); } break; } } ApplyTranscodingConditions(playlistItem, audioTranscodingConditions); // Honor requested max channels if (options.MaxAudioChannels.HasValue) { int currentValue = playlistItem.MaxAudioChannels ?? options.MaxAudioChannels.Value; playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); } if (!playlistItem.AudioBitrate.HasValue) { playlistItem.AudioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec); } // Honor max rate if (maxBitrateSetting.HasValue) { int videoBitrate = maxBitrateSetting.Value; if (playlistItem.AudioBitrate.HasValue) { videoBitrate -= playlistItem.AudioBitrate.Value; } int currentValue = playlistItem.VideoBitrate ?? videoBitrate; playlistItem.VideoBitrate = Math.Min(videoBitrate, currentValue); } } return(playlistItem); }
private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles) { SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, PlayMethod); SubtitleStreamInfo 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)) { info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", baseUrl, ItemId, MediaSourceId, StringHelper.ToStringCultureInvariant(stream.Index), StringHelper.ToStringCultureInvariant(startPositionTicks), subtitleProfile.Format); if (!string.IsNullOrEmpty(accessToken)) { info.Url += "?api_key=" + accessToken; } info.IsExternalUrl = false; } else { info.Url = stream.Path; info.IsExternalUrl = true; } } return info; }
private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, SubtitleDeliveryMethod method, string[] formats) { foreach (SubtitleProfile profile in profiles) { if (method == profile.Method && ListHelper.ContainsIgnoreCase(formats, profile.Format)) { return true; } } return false; }
public DeviceProfile() { DirectPlayProfiles = new DirectPlayProfile[] { }; TranscodingProfiles = new TranscodingProfile[] { }; ResponseProfiles = new ResponseProfile[] { }; CodecProfiles = new CodecProfile[] { }; ContainerProfiles = new ContainerProfile[] { }; SoftSubtitleProfiles = new SubtitleProfile[] { }; ExternalSubtitleProfiles = new SubtitleProfile[] { }; XmlRootAttributes = new XmlAttribute[] { }; SupportedMediaTypes = "Audio,Photo,Video"; }
private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, string[] formats) { foreach (SubtitleProfile profile in profiles) { if (ListHelper.ContainsIgnoreCase(formats, profile.Format)) { return true; } } return false; }
private static SubtitleProfile GetExternalSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, bool allowConversion) { foreach (SubtitleProfile profile in subtitleProfiles) { if (profile.Method != SubtitleDeliveryMethod.External && profile.Method != SubtitleDeliveryMethod.Hls) { continue; } if (profile.Method == SubtitleDeliveryMethod.Hls && playMethod != PlayMethod.Transcode) { continue; } if (!profile.SupportsLanguage(subtitleStream.Language)) { continue; } if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) || (profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream)) { bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); if (!requiresConversion) { return profile; } if (!allowConversion) { continue; } if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsExternalStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format)) { return profile; } } } return null; }
public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod) { if (playMethod != PlayMethod.Transcode && !subtitleStream.IsExternal) { // Look for supported embedded subs foreach (SubtitleProfile profile in subtitleProfiles) { if (!profile.SupportsLanguage(subtitleStream.Language)) { continue; } if (profile.Method != SubtitleDeliveryMethod.Embed) { continue; } if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format) && StringHelper.EqualsIgnoreCase(profile.Format, subtitleStream.Codec)) { return profile; } } } // Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, false) ?? GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, true) ?? new SubtitleProfile { Method = SubtitleDeliveryMethod.Encode, Format = subtitleStream.Codec }; }