private static string BuildDlnaParam(StreamInfo item) { var usCulture = new CultureInfo("en-US"); var list = new List<string> { 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 ? item.AudioStreamIndex.Value.ToString(usCulture) : string.Empty, item.SubtitleStreamIndex.HasValue ? item.SubtitleStreamIndex.Value.ToString(usCulture) : string.Empty, item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(usCulture) : string.Empty, item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(usCulture) : string.Empty, item.MaxAudioChannels.HasValue ? item.MaxAudioChannels.Value.ToString(usCulture) : string.Empty, item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(usCulture) : string.Empty, item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(usCulture) : string.Empty, item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(usCulture) : string.Empty, item.StartPositionTicks.ToString(usCulture), item.VideoLevel.HasValue ? item.VideoLevel.Value.ToString(usCulture) : string.Empty }; return string.Format("Params={0}", string.Join(";", list.ToArray())); }
public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile) { OutputContainer = info.Container; StartTimeTicks = info.StartPositionTicks; MaxWidth = info.MaxWidth; MaxHeight = info.MaxHeight; MaxFramerate = info.MaxFramerate; Profile = info.VideoProfile; Level = info.VideoLevel; ItemId = info.ItemId; MediaSourceId = info.MediaSourceId; AudioCodec = info.AudioCodec; MaxAudioChannels = info.MaxAudioChannels; AudioBitRate = info.AudioBitrate; AudioSampleRate = info.TargetAudioSampleRate; DeviceProfile = deviceProfile; VideoCodec = info.VideoCodec; VideoBitRate = info.VideoBitrate; AudioStreamIndex = info.AudioStreamIndex; MaxRefFrames = info.MaxRefFrames; MaxVideoBitDepth = info.MaxVideoBitDepth; SubtitleMethod = info.SubtitleDeliveryMethod; Cabac = info.Cabac; Context = info.Context; if (info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode || info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed) { SubtitleStreamIndex = info.SubtitleStreamIndex; } }
public XmlElement GetItemElement(XmlDocument doc, BaseItem item, string deviceId, Filter filter, StreamInfo streamInfo = null) { var element = doc.CreateElement(string.Empty, "item", NS_DIDL); element.SetAttribute("restricted", "1"); element.SetAttribute("id", item.Id.ToString("N")); if (item.Parent != null) { element.SetAttribute("parentID", item.Parent.Id.ToString("N")); } //AddBookmarkInfo(item, user, element); AddGeneralProperties(item, element, filter); // refID? // storeAttribute(itemNode, object, ClassProperties.REF_ID, false); var audio = item as Audio; if (audio != null) { AddAudioResource(element, audio, deviceId, filter, streamInfo); } var video = item as Video; if (video != null) { AddVideoResource(element, video, deviceId, filter, streamInfo); } AddCover(item, element); return element; }
public XmlElement GetItemElement(DlnaOptions options, XmlDocument doc, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null) { var clientId = GetClientId(item, null); var element = doc.CreateElement(string.Empty, "item", NS_DIDL); element.SetAttribute("restricted", "1"); element.SetAttribute("id", clientId); if (context != null) { element.SetAttribute("parentID", GetClientId(context, contextStubType)); } else { var parent = item.DisplayParentId; if (parent.HasValue) { element.SetAttribute("parentID", GetClientId(parent.Value, null)); } } //AddBookmarkInfo(item, user, element); AddGeneralProperties(item, null, context, element, filter); // refID? // storeAttribute(itemNode, object, ClassProperties.REF_ID, false); var hasMediaSources = item as IHasMediaSources; if (hasMediaSources != null) { if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { AddAudioResource(options, element, hasMediaSources, deviceId, filter, streamInfo); } else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { AddVideoResource(options, element, hasMediaSources, deviceId, filter, streamInfo); } } AddCover(item, context, null, element); return element; }
public string GetItemDidl(DlnaOptions options, BaseItem item, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo) { var result = new XmlDocument(); var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL); didl.SetAttribute("xmlns:dc", NS_DC); didl.SetAttribute("xmlns:dlna", NS_DLNA); didl.SetAttribute("xmlns:upnp", NS_UPNP); //didl.SetAttribute("xmlns:sec", NS_SEC); foreach (var att in _profile.XmlRootAttributes) { didl.SetAttribute(att.Name, att.Value); } result.AppendChild(didl); result.DocumentElement.AppendChild(GetItemElement(options, result, item, context, null, deviceId, filter, streamInfo)); return result.DocumentElement.OuterXml; }
public XmlElement GetItemElement(XmlDocument doc, BaseItem item, string deviceId, Filter filter, StreamInfo streamInfo = null) { var element = doc.CreateElement(string.Empty, "item", NS_DIDL); element.SetAttribute("restricted", "1"); element.SetAttribute("id", item.Id.ToString("N")); if (item.Parent != null) { element.SetAttribute("parentID", item.Parent.Id.ToString("N")); } //AddBookmarkInfo(item, user, element); AddGeneralProperties(item, element, filter); // refID? // storeAttribute(itemNode, object, ClassProperties.REF_ID, false); var hasMediaSources = item as IHasMediaSources; if (hasMediaSources != null) { if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { AddAudioResource(element, hasMediaSources, deviceId, filter, streamInfo); } else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { AddVideoResource(element, hasMediaSources, deviceId, filter, streamInfo); } } AddCover(item, element); return element; }
private static string BuildDlnaParam(StreamInfo item, string accessToken) { List<string> list = new List<string>(); foreach (NameValuePair pair in BuildParams(item, accessToken, true)) { list.Add(pair.Value); } return string.Format("Params={0}", string.Join(";", list.ToArray())); }
private async Task InitiatePlayback(bool isResume, int? subtitleIndex = null) { Messenger.Default.Send(new NotificationMessage(Constants.Messages.ClearNowPlayingMsg)); EndTime = TimeSpan.Zero; var streamInfo = new StreamInfo(); switch (PlayerSourceType) { case PlayerSourceType.Playlist: case PlayerSourceType.Video: if (SelectedItem.VideoType != VideoType.VideoFile) { var result = MessageBox.Show(AppResources.MessageExperimentalVideo, AppResources.MessageExperimentalTitle, MessageBoxButton.OKCancel); if (result == MessageBoxResult.Cancel) { NavigationService.GoBack(); return; } } if (SelectedItem.UserData != null && isResume) { _startPositionTicks = SelectedItem.UserData.PlaybackPositionTicks; } if (_startPositionTicks == 0) { // Although the API will return 0 items if the user doesn't have cinema mode enabled, // that's still precious time on a slow connection needlessly wasted. So let's make sure // the user actually has it enabled first. if (AuthenticationService.Current.LoggedInUser.Configuration.EnableCinemaMode) { try { var items = await ApiClient.GetIntrosAsync(SelectedItem.Id, AuthenticationService.Current.LoggedInUserId); if (items != null && !items.Items.IsNullOrEmpty()) { if (PlaylistItems == null) { PlaylistItems = new List<BaseItemDto>(items.Items) {SelectedItem}; } else { var list = items.Items.ToList(); list.AddRange(PlaylistItems); PlaylistItems = list; } var firstItem = PlaylistItems.FirstOrDefault(); if (firstItem != null) SelectedItem = firstItem; } } catch (HttpException ex) { Log.ErrorException("GetIntros (Cinema Mode)", ex); } } } streamInfo = await CreateVideoStream(SelectedItem.Id, _startPositionTicks, SelectedItem.MediaSources, SelectedItem.Type.ToLower().Equals("channelvideoitem")); if (SelectedItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(SelectedItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", SelectedItem.Type, SelectedItem.Name, SelectedItem.Id); break; case PlayerSourceType.Recording: streamInfo = await CreateVideoStream(RecordingItem.Id, _startPositionTicks); if (RecordingItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(RecordingItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", RecordingItem.Type, RecordingItem.Name, RecordingItem.Id); break; case PlayerSourceType.Programme: try { var channel = await ApiClient.GetItemAsync(ProgrammeItem.ChannelId, AuthenticationService.Current.LoggedInUserId); streamInfo = await CreateVideoStream(ProgrammeItem.ChannelId, _startPositionTicks, channel.MediaSources, useHls: true); } catch (HttpException ex) { Utils.HandleHttpException(ex, "GetVideoChannel", NavigationService, Log); NavigationService.GoBack(); return; } if (ProgrammeItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(ProgrammeItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", ProgrammeItem.Type, ProgrammeItem.Name, ProgrammeItem.Id); break; } if (streamInfo == null) { NavigationService.GoBack(); return; } if (subtitleIndex.HasValue) { streamInfo.SubtitleStreamIndex = subtitleIndex.Value; } var url = streamInfo.ToUrl(ApiClient.GetApiUrl("/"), ApiClient.AccessToken); _streamInfo = streamInfo; //Captions = GetSubtitles(SelectedItem); var isSyncedVideo = url.StartsWith("AnyTime", StringComparison.InvariantCultureIgnoreCase); if (EndTime.Ticks > 0 && !IsDirectStream) { EndTime = TimeSpan.FromTicks(EndTime.Ticks - _startPositionTicks); } StopAudioPlayback(); _streamInfo = streamInfo; if (_isResume && IsDirectStream) { StartFrom = TimeSpan.FromTicks(_startPositionTicks); } RaisePropertyChanged(() => IsDirectStream); if (isSyncedVideo) { SetVideoUrl(string.Empty); if (VideoStream == null || _storageUrl != url) { _storageUrl = url; using (var storage = IsolatedStorageFile.GetUserStoreForApplication()) { var stream = storage.OpenFile(url, FileMode.Open); VideoStream = stream; } } } else { VideoStream = null; SetVideoUrl(url); _storageUrl = string.Empty; } Debug.WriteLine(VideoUrl); Log.Debug(VideoUrl); try { Log.Info("Sending playback started message to the server."); _itemId = streamInfo.ItemId; var info = new PlaybackStartInfo { ItemId = _itemId, CanSeek = false, QueueableMediaTypes = new List<string>() }; await ApiClient.ReportPlaybackStartAsync(info); } catch (HttpException ex) { Utils.HandleHttpException("VideoPageLoaded", ex, NavigationService, Log); } }
public Task<Stream> GetFileStream(StreamInfo info) { return GetFileStream(info.ToUrl(null, null)); }
private static List <NameValuePair> BuildParams(StreamInfo item, string accessToken, bool isDlna) { List <NameValuePair> list = new List <NameValuePair>(); string audioCodecs = item.AudioCodecs.Length == 0 ? string.Empty : string.Join(",", item.AudioCodecs); string videoCodecs = item.VideoCodecs.Length == 0 ? string.Empty : string.Join(",", item.VideoCodecs); list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty)); list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty)); list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty)); list.Add(new NameValuePair("Static", item.IsDirectStream.ToString().ToLower())); list.Add(new NameValuePair("VideoCodec", videoCodecs)); list.Add(new NameValuePair("AudioCodec", audioCodecs)); list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty)); list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty)); list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty)); list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty)); list.Add(new NameValuePair("MaxAudioChannels", item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty)); list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty)); list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty)); list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); long startPositionTicks = item.StartPositionTicks; var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"); if (isHls) { list.Add(new NameValuePair("StartTimeTicks", string.Empty)); } else { list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(startPositionTicks))); } if (isDlna) { // hack alert // dlna needs to be update to support the qualified params var level = item.GetTargetVideoLevel("h264"); list.Add(new NameValuePair("Level", level.HasValue ? StringHelper.ToStringCultureInvariant(level.Value) : string.Empty)); } if (isDlna) { // hack alert // dlna needs to be update to support the qualified params var refframes = item.GetTargetRefFrames("h264"); list.Add(new NameValuePair("MaxRefFrames", refframes.HasValue ? StringHelper.ToStringCultureInvariant(refframes.Value) : string.Empty)); } list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty)); if (isDlna) { // hack alert // dlna needs to be update to support the qualified params var profile = item.GetOption("h264", "profile"); // Avoid having to encode profile = (profile ?? string.Empty).Replace(" ", ""); list.Add(new NameValuePair("Profile", profile)); } // no longer used list.Add(new NameValuePair("Cabac", string.Empty)); list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); if (isDlna) { list.Add(new NameValuePair("ItemId", item.ItemId)); } list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower())); 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 ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty)); list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString().ToLower())); list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty)); list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString().ToLower())); string subtitleCodecs = item.SubtitleCodecs.Length == 0 ? string.Empty : string.Join(",", item.SubtitleCodecs); list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty)); list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower())); if (isDlna) { // hack alert // dlna needs to be update to support the qualified params var deinterlace = string.Equals(item.GetOption("h264", "deinterlace"), "true", StringComparison.OrdinalIgnoreCase) || string.Equals(item.GetOption("mpeg2video", "deinterlace"), "true", StringComparison.OrdinalIgnoreCase); list.Add(new NameValuePair("DeInterlace", deinterlace.ToString().ToLower())); } if (!isDlna && isHls) { list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); if (item.SegmentLength.HasValue) { list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); } if (item.MinSegments.HasValue) { list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); } list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString())); } if (isDlna || !item.IsDirectStream) { list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray()))); } if (!isDlna) { foreach (var pair in item.StreamOptions) { if (string.IsNullOrWhiteSpace(pair.Value)) { continue; } // strip spaces to avoid having to encode h264 profile names list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", ""))); } } return(list); }
private void AddVideoResource(XmlElement container, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo) { var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken); res.InnerText = url; var mediaSource = streamInfo.MediaSource; if (mediaSource.RunTimeTicks.HasValue) { res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture)); } if (filter.Contains("res@size")) { if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength) { var size = streamInfo.TargetSize; if (size.HasValue) { res.SetAttribute("size", size.Value.ToString(_usCulture)); } } } var totalBitrate = streamInfo.TargetTotalBitrate; var targetSampleRate = streamInfo.TargetAudioSampleRate; var targetChannels = streamInfo.TargetAudioChannels; var targetWidth = streamInfo.TargetWidth; var targetHeight = streamInfo.TargetHeight; if (targetChannels.HasValue) { res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture)); } if (filter.Contains("res@resolution")) { if (targetWidth.HasValue && targetHeight.HasValue) { res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value)); } } if (targetSampleRate.HasValue) { res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture)); } if (totalBitrate.HasValue) { res.SetAttribute("bitrate", totalBitrate.Value.ToString(_usCulture)); } var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container, streamInfo.AudioCodec, streamInfo.VideoCodec, streamInfo.TargetAudioBitrate, targetWidth, targetHeight, streamInfo.TargetVideoBitDepth, streamInfo.TargetVideoProfile, streamInfo.TargetVideoLevel, streamInfo.TargetFramerate, streamInfo.TargetPacketLength, streamInfo.TargetTimestamp, streamInfo.IsTargetAnamorphic, streamInfo.TargetRefFrames, streamInfo.TargetVideoStreamCount, streamInfo.TargetAudioStreamCount, streamInfo.TargetVideoCodecTag); var filename = url.Substring(0, url.IndexOf('?')); var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) ? MimeTypes.GetMimeType(filename) : mediaProfile.MimeType; res.SetAttribute("protocolInfo", String.Format( "http-get:*:{0}:{1}", mimeType, contentFeatures )); container.AppendChild(res); }
/// <summary> /// Reports playback progress /// </summary> /// <param name="info">The information.</param> /// <param name="streamInfo">The stream information.</param> /// <param name="isOffline">if set to <c>true</c> [is offline].</param> /// <param name="apiClient">The current apiClient. It can be null if offline</param> /// <returns>Task.</returns> public async Task ReportPlaybackProgress(PlaybackProgressInfo info, StreamInfo streamInfo, bool isOffline, IApiClient apiClient) { if (!isOffline) { if (streamInfo != null) { info.PlaySessionId = streamInfo.PlaySessionId; if (streamInfo.MediaSource != null) { info.LiveStreamId = streamInfo.MediaSource.LiveStreamId; } } await apiClient.ReportPlaybackProgressAsync(info).ConfigureAwait(false); } }
private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options) { StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, MediaType = DlnaProfileType.Audio, MediaSource = item, RunTimeTicks = item.RunTimeTicks, Context = options.Context, DeviceProfile = options.Profile }; MediaStream audioStream = item.GetDefaultAudioStream(null); List<PlayMethod> directPlayMethods = GetAudioDirectPlayMethods(item, audioStream, options); if (directPlayMethods.Count > 0) { string audioCodec = audioStream == null ? null : audioStream.Codec; // Make sure audio codec profiles are satisfied if (!string.IsNullOrEmpty(audioCodec)) { ConditionProcessor conditionProcessor = new ConditionProcessor(); List<ProfileCondition> conditions = new List<ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Audio && i.ContainsCodec(audioCodec, item.Container)) { foreach (ProfileCondition c in i.Conditions) { conditions.Add(c); } } } int? audioChannels = audioStream.Channels; int? audioBitrate = audioStream.BitRate; bool all = true; foreach (ProfileCondition c in conditions) { if (!conditionProcessor.IsAudioConditionSatisfied(c, audioChannels, audioBitrate)) { all = false; break; } } if (all) { if (item.Protocol == MediaProtocol.File && directPlayMethods.Contains(PlayMethod.DirectPlay) && _localPlayer.CanAccessFile(item.Path)) { playlistItem.PlayMethod = PlayMethod.DirectPlay; } else if (item.Protocol == MediaProtocol.Http && directPlayMethods.Contains(PlayMethod.DirectPlay) && _localPlayer.CanAccessUrl(item.Path, item.RequiredHttpHeaders.Count > 0)) { playlistItem.PlayMethod = PlayMethod.DirectPlay; } else if (directPlayMethods.Contains(PlayMethod.DirectStream)) { playlistItem.PlayMethod = PlayMethod.DirectStream; } playlistItem.Container = item.Container; return playlistItem; } } } 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; } playlistItem.PlayMethod = PlayMethod.Transcode; playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.Container = transcodingProfile.Container; playlistItem.AudioCodec = transcodingProfile.AudioCodec; playlistItem.SubProtocol = transcodingProfile.Protocol; List<CodecProfile> audioCodecProfiles = new List<CodecProfile>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Audio && i.ContainsCodec(transcodingProfile.AudioCodec, transcodingProfile.Container)) { audioCodecProfiles.Add(i); } if (audioCodecProfiles.Count >= 1) break; } List<ProfileCondition> audioTranscodingConditions = new List<ProfileCondition>(); foreach (CodecProfile i in audioCodecProfiles) { foreach (ProfileCondition c in i.Conditions) { audioTranscodingConditions.Add(c); } } 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 configuredBitrate = options.AudioTranscodingBitrate ?? (options.Context == EncodingContext.Static ? options.Profile.MusicSyncBitrate : options.Profile.MusicStreamingTranscodingBitrate) ?? 128000; playlistItem.AudioBitrate = Math.Min(configuredBitrate, playlistItem.AudioBitrate ?? configuredBitrate); } return playlistItem; }
private void AddVideoResource(XmlElement container, Video video, string deviceId, Filter filter, StreamInfo streamInfo = null) { if (streamInfo == null) { var sources = _user == null ? video.GetMediaSources(true).ToList() : video.GetMediaSources(true, _user).ToList(); streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions { ItemId = video.Id.ToString("N"), MediaSources = sources, Profile = _profile, DeviceId = deviceId, MaxBitrate = _profile.MaxStreamingBitrate }); } var targetWidth = streamInfo.TargetWidth; var targetHeight = streamInfo.TargetHeight; var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container, streamInfo.VideoCodec, streamInfo.AudioCodec, targetWidth, targetHeight, streamInfo.TargetVideoBitDepth, streamInfo.TargetVideoBitrate, streamInfo.TargetAudioChannels, streamInfo.TargetAudioBitrate, streamInfo.TargetTimestamp, streamInfo.IsDirectStream, streamInfo.RunTimeTicks, streamInfo.TargetVideoProfile, streamInfo.TargetVideoLevel, streamInfo.TargetFramerate, streamInfo.TargetPacketLength, streamInfo.TranscodeSeekInfo, streamInfo.IsTargetAnamorphic); foreach (var contentFeature in contentFeatureList) { AddVideoResource(container, video, deviceId, filter, contentFeature, streamInfo); } foreach (var subtitle in streamInfo.GetExternalSubtitles(_serverAddress)) { AddSubtitleElement(container, subtitle); } }
private async Task ConvertSubtitles(SyncJobItem jobItem, IEnumerable<SubtitleStreamInfo> subtitles, StreamInfo streamInfo, CancellationToken cancellationToken) { var files = new List<ItemFileInfo>(); var mediaStreams = jobItem.MediaSource.MediaStreams .Where(i => i.Type != MediaStreamType.Subtitle || !i.IsExternal) .ToList(); var startingIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Select(i => i.Index).Max() + 1); foreach (var subtitle in subtitles) { var fileInfo = await ConvertSubtitles(jobItem.TemporaryPath, streamInfo, subtitle, cancellationToken).ConfigureAwait(false); // Reset this to a value that will be based on the output media fileInfo.Index = startingIndex; files.Add(fileInfo); mediaStreams.Add(new MediaStream { Index = startingIndex, Codec = subtitle.Format, IsForced = subtitle.IsForced, IsExternal = true, Language = subtitle.Language, Path = fileInfo.Path, SupportsExternalStream = true, Type = MediaStreamType.Subtitle }); startingIndex++; } jobItem.AdditionalFiles.AddRange(files); jobItem.MediaSource.MediaStreams = mediaStreams; }
private async Task PlayTrack(int index, long? startPositionTicks, StreamInfo previousStreamInfo) { var previousMedia = CurrentMedia; var previousIndex = CurrentPlaylistIndex; var endingTicks = CurrentPositionTicks; var options = CurrentPlayOptions; PlayableItem playableItem; try { playableItem = await GetPlayableItem(options.Items[index], startPositionTicks, CancellationToken.None); } catch (PlaybackException ex) { string text; switch (ex.ErrorCode) { case PlaybackErrorCode.NoCompatibleStream: text = "No compatible streams are currently available. Please try again later or contact your system administrator for details."; break; case PlaybackErrorCode.NotAllowed: text = "You do not have access to play this content at this time. Please contact your system administrator for details."; break; case PlaybackErrorCode.RateLimitExceeded: text = "Your playback limit has been exceeded. Please try again later or contact your system administrator for details."; break; default: text = "There was an error processing the request."; break; } _presentation.ShowMessage(new MessageBoxInfo { Button = MessageBoxButton.OK, Icon = MessageBoxIcon.Error, Caption = "Error", Text = text }); return; } try { var enableMadVr = EnableMadvr(options); //var enableReclock = EnableReclock(options); InvokeOnPlayerThread(() => { //create a fresh DS Player everytime we want one DisposePlayer(); var apiClient = _connectionManager.GetApiClient(playableItem.OriginalItem); _mediaPlayer = new DirectShowPlayer(_logger, _hiddenWindow, this, _presentation.WindowHandle, _sessionManager, _config, _inputManager, apiClient, _zipClient, _httpClient); _mediaPlayer.Play(playableItem, enableMadVr, false); }, true); } catch (PlaybackException ex) { string text; switch (ex.ErrorCode) { case PlaybackErrorCode.NoCompatibleStream: text = "No compatible streams are currently available. Please try again later or contact your system administrator for details."; break; case PlaybackErrorCode.NotAllowed: text = "You do not have access to play this content at this time. Please contact your system administrator for details."; break; case PlaybackErrorCode.RateLimitExceeded: text = "Your playback limit has been exceeded. Please try again later or contact your system administrator for details."; break; default: text = "There was an error processing the request."; break; } _presentation.ShowMessage(new MessageBoxInfo { Button = MessageBoxButton.OK, Icon = MessageBoxIcon.Error, Caption = "Error", Text = text }); OnPlaybackStopped(playableItem, null, TrackCompletionReason.Failure, null); throw; } catch { OnPlaybackStopped(playableItem, null, TrackCompletionReason.Failure, null); throw; } CurrentStreamInfo = playableItem.StreamInfo; CurrentPlaylistIndex = index; if (startPositionTicks.HasValue && startPositionTicks.Value > 0) { InvokeOnPlayerThread(() => _mediaPlayer.Seek(startPositionTicks.Value)); } if (playableItem.OriginalItem.IsVideo) { var audioIndex = playableItem.MediaSource.DefaultAudioStreamIndex; var subtitleIndex = playableItem.MediaSource.DefaultSubtitleStreamIndex; if (audioIndex.HasValue && audioIndex.Value != -1) { SetAudioStreamIndex(audioIndex.Value); } SetSubtitleStreamIndex(subtitleIndex ?? -1); } if (previousMedia != null && MediaChanged != null) { var args = new MediaChangeEventArgs { Player = this, NewPlaylistIndex = index, NewMedia = CurrentMedia, PreviousMedia = previousMedia, PreviousPlaylistIndex = previousIndex, EndingPositionTicks = endingTicks, PreviousStreamInfo = previousStreamInfo }; _presentation.Window.Dispatcher.Invoke ( () => MediaChanged(this, args) ); } }
/// <summary> /// Gets the in playback selectable subtitle streams. /// </summary> /// <param name="info">The information.</param> /// <returns>IEnumerable<MediaStream>.</returns> public IEnumerable<MediaStream> GetInPlaybackSelectableSubtitleStreams(StreamInfo info) { return info.GetSelectableSubtitleStreams(); }
private async Task StopStranscoding(StreamInfo streamInfo, IApiClient apiClient) { if (streamInfo.MediaType != DlnaProfileType.Video) { return; } if (streamInfo.PlayMethod != PlayMethod.Transcode) { return; } var playSessionId = streamInfo.PlaySessionId; try { await apiClient.StopTranscodingProcesses(_device.DeviceId, playSessionId).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error in StopStranscoding", ex); } }
/// <summary> /// Reports playback progress /// </summary> /// <param name="info">The information.</param> /// <param name="streamInfo">The stream information.</param> /// <param name="serverId">The server identifier.</param> /// <param name="userId">The user identifier.</param> /// <param name="isOffline">if set to <c>true</c> [is offline].</param> /// <param name="apiClient">The current apiClient. It can be null if offline</param> /// <returns>Task.</returns> public async Task ReportPlaybackStopped(PlaybackStopInfo info, StreamInfo streamInfo, string serverId, string userId, bool isOffline, IApiClient apiClient) { if (isOffline) { var action = new UserAction { Date = DateTime.UtcNow, ItemId = info.ItemId, PositionTicks = info.PositionTicks, ServerId = serverId, Type = UserActionType.PlayedItem, UserId = userId }; await _localAssetManager.RecordUserAction(action).ConfigureAwait(false); return; } if (streamInfo != null) { info.PlaySessionId = streamInfo.PlaySessionId; if (streamInfo.MediaSource != null) { info.LiveStreamId = streamInfo.MediaSource.LiveStreamId; } } // Put a try/catch here because we need to stop transcoding regardless try { await apiClient.ReportPlaybackStoppedAsync(info).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error in ReportPlaybackStoppedAsync", ex); } }
private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken, bool isDlna) { List<NameValuePair> list = new List<NameValuePair>(); string audioCodecs = item.AudioCodecs.Length == 0 ? string.Empty : string.Join(",", item.AudioCodecs); 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().ToLower())); list.Add(new NameValuePair("VideoCodec", item.VideoCodec ?? string.Empty)); list.Add(new NameValuePair("AudioCodec", audioCodecs)); list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty)); list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty)); list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty)); list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty)); list.Add(new NameValuePair("MaxAudioChannels", item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty)); list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty)); list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty)); list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); var forceStartPosition = false; long startPositionTicks = item.StartPositionTicks; //if (item.MediaSource.DateLiveStreamOpened.HasValue && startPositionTicks == 0) //{ // var elapsed = DateTime.UtcNow - item.MediaSource.DateLiveStreamOpened.Value; // elapsed -= TimeSpan.FromSeconds(20); // if (elapsed.TotalSeconds >= 0) // { // startPositionTicks = elapsed.Ticks + startPositionTicks; // forceStartPosition = true; // } //} if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls") && !forceStartPosition) { list.Add(new NameValuePair("StartTimeTicks", string.Empty)); } else { list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(startPositionTicks))); } list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty)); list.Add(new NameValuePair("MaxRefFrames", item.MaxRefFrames.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxRefFrames.Value) : string.Empty)); list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty)); list.Add(new NameValuePair("Profile", item.VideoProfile ?? string.Empty)); // no longer used list.Add(new NameValuePair("Cabac", string.Empty)); list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); if (isDlna) { list.Add(new NameValuePair("ItemId", item.ItemId)); } list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower())); 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 ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty)); list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString().ToLower())); list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty)); list.Add(new NameValuePair("EnableSplittingOnNonKeyFrames", item.EnableSplittingOnNonKeyFrames.ToString().ToLower())); return list; }
private async Task<ItemFileInfo> ConvertSubtitles(string temporaryPath, StreamInfo streamInfo, SubtitleStreamInfo subtitleStreamInfo, CancellationToken cancellationToken) { var subtitleStreamIndex = subtitleStreamInfo.Index; var filename = Guid.NewGuid() + "." + subtitleStreamInfo.Format.ToLower(); var path = Path.Combine(temporaryPath, filename); Directory.CreateDirectory(Path.GetDirectoryName(path)); using (var stream = await _subtitleEncoder.GetSubtitles(streamInfo.ItemId, streamInfo.MediaSourceId, subtitleStreamIndex, subtitleStreamInfo.Format, 0, null, cancellationToken).ConfigureAwait(false)) { using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); } } return new ItemFileInfo { Name = Path.GetFileName(path), Path = path, Type = ItemFileType.Subtitles, Index = subtitleStreamIndex }; }
private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions) { foreach (ProfileCondition condition in conditions) { string value = condition.Value; if (string.IsNullOrEmpty(value)) { continue; } // No way to express this if (condition.Condition == ProfileConditionType.GreaterThanEqual) { continue; } switch (condition.Property) { case ProfileConditionValue.AudioBitrate: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.AudioBitrate = num; } break; } case ProfileConditionValue.AudioChannels: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxAudioChannels = num; } break; } case ProfileConditionValue.IsCabac: { bool val; if (BoolHelper.TryParseCultureInvariant(value, out val)) { if (condition.Condition == ProfileConditionType.Equals) { item.Cabac = val; } else if (condition.Condition == ProfileConditionType.NotEquals) { item.Cabac = !val; } } break; } case ProfileConditionValue.IsAnamorphic: case ProfileConditionValue.AudioProfile: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.PacketLength: case ProfileConditionValue.NumAudioStreams: case ProfileConditionValue.NumVideoStreams: case ProfileConditionValue.IsSecondaryAudio: case ProfileConditionValue.VideoTimestamp: { // Not supported yet break; } case ProfileConditionValue.RefFrames: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxRefFrames = num; } break; } case ProfileConditionValue.VideoBitDepth: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxVideoBitDepth = num; } break; } case ProfileConditionValue.VideoProfile: { item.VideoProfile = (value ?? string.Empty).Split('|')[0]; break; } case ProfileConditionValue.Height: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxHeight = num; } break; } case ProfileConditionValue.VideoBitrate: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.VideoBitrate = num; } break; } case ProfileConditionValue.VideoFramerate: { float num; if (FloatHelper.TryParseCultureInvariant(value, out num)) { item.MaxFramerate = num; } break; } case ProfileConditionValue.VideoLevel: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.VideoLevel = num; } break; } case ProfileConditionValue.Width: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxWidth = num; } break; } default: throw new ArgumentException("Unrecognized ProfileConditionValue"); } } }
private static string BuildDlnaParam(StreamInfo item) { List<string> list = new List<string> { 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 }; return string.Format("Params={0}", string.Join(";", list.ToArray())); }
private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken, bool isDlna) { List<NameValuePair> list = new List<NameValuePair>(); 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().ToLower())); list.Add(new NameValuePair("VideoCodec", item.VideoCodec ?? string.Empty)); list.Add(new NameValuePair("AudioCodec", item.AudioCodec ?? string.Empty)); list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty)); list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty)); list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty)); list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty)); list.Add(new NameValuePair("MaxAudioChannels", item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty)); list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty)); list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty)); list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls")) { list.Add(new NameValuePair("StartTimeTicks", string.Empty)); } else { list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(item.StartPositionTicks))); } list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty)); if (isDlna) { // The player may see it as separate resources due to url differences // And then try to request more than one at playback list.Add(new NameValuePair("ClientTime", string.Empty)); } else { list.Add(new NameValuePair("ClientTime", item.IsDirectStream ? string.Empty : DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture))); } list.Add(new NameValuePair("MaxRefFrames", item.MaxRefFrames.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxRefFrames.Value) : string.Empty)); list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty)); list.Add(new NameValuePair("Profile", item.VideoProfile ?? string.Empty)); list.Add(new NameValuePair("Cabac", item.Cabac.HasValue ? item.Cabac.Value.ToString() : string.Empty)); list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); if (isDlna) { list.Add(new NameValuePair("ItemId", item.ItemId)); } return list; }
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); bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options); _logger.Debug("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, 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.SubtitleProfiles, 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.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(transcodingProfile.AudioCodec, 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; }
private async Task InitiatePlayback(bool isResume, int? subtitleIndex = null) { Messenger.Default.Send(new NotificationMessage(Constants.Messages.ClearNowPlayingMsg)); EndTime = TimeSpan.Zero; var streamInfo = new StreamInfo(); //var query = new VideoStreamOptions(); switch (PlayerSourceType) { case PlayerSourceType.Playlist: case PlayerSourceType.Video: if (SelectedItem.VideoType != VideoType.VideoFile) { var result = MessageBox.Show(AppResources.MessageExperimentalVideo, AppResources.MessageExperimentalTitle, MessageBoxButton.OKCancel); if (result == MessageBoxResult.Cancel) { NavigationService.GoBack(); return; } } if (SelectedItem.UserData != null && isResume) { _startPositionTicks = SelectedItem.UserData.PlaybackPositionTicks; } if (_startPositionTicks == 0) { try { var items = await ApiClient.GetIntrosAsync(SelectedItem.Id, AuthenticationService.Current.LoggedInUserId); if (items != null && !items.Items.IsNullOrEmpty()) { if (PlaylistItems == null) { PlaylistItems = new List<BaseItemDto>(items.Items) {SelectedItem}; } else { var list = items.Items.ToList(); list.AddRange(PlaylistItems); PlaylistItems = list; } var firstItem = PlaylistItems.FirstOrDefault(); if (firstItem != null) SelectedItem = firstItem; } } catch (HttpException ex) { Log.ErrorException("GetIntros (Cinema Mode)", ex); } } streamInfo = CreateVideoStream(SelectedItem.Id, _startPositionTicks, SelectedItem.MediaSources, SelectedItem.Type.ToLower().Equals("channelvideoitem")); //query = CreateVideoStreamOptions(SelectedItem.Id, _startPositionTicks, SelectedItem.Type.ToLower().Equals("channelvideoitem")); if (SelectedItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(SelectedItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", SelectedItem.Type, SelectedItem.Name, SelectedItem.Id); break; case PlayerSourceType.Recording: //query = CreateVideoStreamOptions(RecordingItem.Id, _startPositionTicks); streamInfo = CreateVideoStream(RecordingItem.Id, _startPositionTicks); if (RecordingItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(RecordingItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", RecordingItem.Type, RecordingItem.Name, RecordingItem.Id); break; case PlayerSourceType.Programme: //query = CreateVideoStreamOptions(ProgrammeItem.ChannelId, _startPositionTicks, true); try { var channel = await ApiClient.GetItemAsync(ProgrammeItem.ChannelId, AuthenticationService.Current.LoggedInUserId); streamInfo = CreateVideoStream(ProgrammeItem.ChannelId, _startPositionTicks, channel.MediaSources, useHls: true); } catch (HttpException ex) { Utils.HandleHttpException(ex, "GetVideoChannel", NavigationService, Log); NavigationService.GoBack(); return; } if (ProgrammeItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(ProgrammeItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", ProgrammeItem.Type, ProgrammeItem.Name, ProgrammeItem.Id); break; } if (subtitleIndex.HasValue) { streamInfo.SubtitleStreamIndex = subtitleIndex.Value; } var url = streamInfo.ToUrl(ApiClient.GetApiUrl("/"), ApiClient.AccessToken); _streamInfo = streamInfo; //var url = PlayerSourceType == PlayerSourceType.Programme ? ApiClient.GetHlsVideoStreamUrl(query) : ApiClient.GetVideoStreamUrl(query); //Captions = GetSubtitles(SelectedItem); if (EndTime.Ticks > 0 && !IsDirectStream) { EndTime = TimeSpan.FromTicks(EndTime.Ticks - _startPositionTicks); } StopAudioPlayback(); if (_isResume && IsDirectStream) { _startFrom = TimeSpan.FromTicks(_startPositionTicks); } RaisePropertyChanged(() => IsDirectStream); VideoUrl = url; Debug.WriteLine(VideoUrl); Log.Debug(VideoUrl); try { Log.Info("Sending playback started message to the server."); _itemId = streamInfo.ItemId; var info = new PlaybackStartInfo { ItemId = _itemId, CanSeek = false, QueueableMediaTypes = new List<string>() }; await ApiClient.ReportPlaybackStartAsync(info); } catch (HttpException ex) { Utils.HandleHttpException("VideoPageLoaded", ex, NavigationService, Log); } }
private void AddVideoResource(DlnaOptions options, XmlElement container, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null) { if (streamInfo == null) { var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList(); streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions { ItemId = GetClientId(video), MediaSources = sources, Profile = _profile, DeviceId = deviceId, MaxBitrate = _profile.MaxStreamingBitrate }); } var targetWidth = streamInfo.TargetWidth; var targetHeight = streamInfo.TargetHeight; var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container, streamInfo.VideoCodec, streamInfo.AudioCodec, targetWidth, targetHeight, streamInfo.TargetVideoBitDepth, streamInfo.TargetVideoBitrate, streamInfo.TargetTimestamp, streamInfo.IsDirectStream, streamInfo.RunTimeTicks, streamInfo.TargetVideoProfile, streamInfo.TargetVideoLevel, streamInfo.TargetFramerate, streamInfo.TargetPacketLength, streamInfo.TranscodeSeekInfo, streamInfo.IsTargetAnamorphic, streamInfo.TargetRefFrames, streamInfo.TargetVideoStreamCount, streamInfo.TargetAudioStreamCount, streamInfo.TargetVideoCodecTag); foreach (var contentFeature in contentFeatureList) { AddVideoResource(container, video, deviceId, filter, contentFeature, streamInfo); } foreach (var subtitle in streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken)) { if (subtitle.DeliveryMethod == SubtitleDeliveryMethod.External) { var subtitleAdded = AddSubtitleElement(container, subtitle); if (subtitleAdded && _profile.EnableSingleSubtitleLimit) { break; } } } }
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, MediaType = DlnaProfileType.Video, MediaSource = item, RunTimeTicks = item.RunTimeTicks }; 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 DirectPlayProfile directPlay = GetVideoDirectPlayProfile(options.Profile, item, videoStream, audioStream); if (directPlay != null) { playlistItem.PlayMethod = PlayMethod.DirectStream; playlistItem.Container = item.Container; if (subtitleStream != null) { playlistItem.SubtitleDeliveryMethod = GetSubtitleDeliveryMethod(subtitleStream, options); } 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 (subtitleStream != null) { playlistItem.SubtitleDeliveryMethod = GetSubtitleDeliveryMethod(subtitleStream, options); } 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 (var 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 (var 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 void AddAudioResource(DlnaOptions options, XmlElement container, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null) { var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); if (streamInfo == null) { var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList(); streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions { ItemId = GetClientId(audio), MediaSources = sources, Profile = _profile, DeviceId = deviceId }); } var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken); res.InnerText = url; var mediaSource = streamInfo.MediaSource; if (mediaSource.RunTimeTicks.HasValue) { res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture)); } if (filter.Contains("res@size")) { if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength) { var size = streamInfo.TargetSize; if (size.HasValue) { res.SetAttribute("size", size.Value.ToString(_usCulture)); } } } var targetAudioBitrate = streamInfo.TargetAudioBitrate; var targetSampleRate = streamInfo.TargetAudioSampleRate; var targetChannels = streamInfo.TargetAudioChannels; if (targetChannels.HasValue) { res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture)); } if (targetSampleRate.HasValue) { res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture)); } if (targetAudioBitrate.HasValue) { res.SetAttribute("bitrate", targetAudioBitrate.Value.ToString(_usCulture)); } var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container, streamInfo.AudioCodec, targetChannels, targetAudioBitrate); var filename = url.Substring(0, url.IndexOf('?')); var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) ? MimeTypes.GetMimeType(filename) : mediaProfile.MimeType; var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container, streamInfo.TargetAudioCodec, targetAudioBitrate, targetSampleRate, targetChannels, streamInfo.IsDirectStream, streamInfo.RunTimeTicks, streamInfo.TranscodeSeekInfo); res.SetAttribute("protocolInfo", String.Format( "http-get:*:{0}:{1}", mimeType, contentFeatures )); container.AppendChild(res); }
private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions) { foreach (ProfileCondition condition in conditions) { string value = condition.Value; if (string.IsNullOrEmpty(value)) { continue; } switch (condition.Property) { case ProfileConditionValue.AudioBitrate: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.AudioBitrate = num; } break; } case ProfileConditionValue.AudioChannels: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxAudioChannels = num; } break; } case ProfileConditionValue.AudioProfile: case ProfileConditionValue.IsAnamorphic: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.PacketLength: case ProfileConditionValue.VideoTimestamp: case ProfileConditionValue.VideoBitDepth: { // Not supported yet break; } case ProfileConditionValue.VideoProfile: { item.VideoProfile = value; break; } case ProfileConditionValue.Height: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxHeight = num; } break; } case ProfileConditionValue.VideoBitrate: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.VideoBitrate = num; } break; } case ProfileConditionValue.VideoFramerate: { float num; if (FloatHelper.TryParseCultureInvariant(value, out num)) { item.MaxFramerate = num; } break; } case ProfileConditionValue.VideoLevel: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.VideoLevel = num; } break; } case ProfileConditionValue.Width: { int num; if (IntHelper.TryParseCultureInvariant(value, out num)) { item.MaxWidth = num; } break; } default: throw new ArgumentException("Unrecognized ProfileConditionValue"); } } }
private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options) { StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, MediaType = DlnaProfileType.Audio, MediaSource = item, RunTimeTicks = item.RunTimeTicks }; int? maxBitrateSetting = options.GetMaxBitrate(); MediaStream audioStream = item.DefaultAudioStream; // Honor the max bitrate setting if (IsAudioEligibleForDirectPlay(item, maxBitrateSetting)) { DirectPlayProfile directPlay = null; foreach (DirectPlayProfile i in options.Profile.DirectPlayProfiles) { if (i.Type == playlistItem.MediaType && IsAudioDirectPlaySupported(i, item, audioStream)) { directPlay = i; break; } } if (directPlay != null) { string audioCodec = audioStream == null ? null : audioStream.Codec; // Make sure audio codec profiles are satisfied if (!string.IsNullOrEmpty(audioCodec)) { ConditionProcessor conditionProcessor = new ConditionProcessor(); List<ProfileCondition> conditions = new List<ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Audio && i.ContainsCodec(audioCodec)) { foreach (var c in i.Conditions) { conditions.Add(c); } } } int? audioChannels = audioStream.Channels; int? audioBitrate = audioStream.BitRate; bool all = true; foreach (ProfileCondition c in conditions) { if (!conditionProcessor.IsAudioConditionSatisfied(c, audioChannels, audioBitrate)) { all = false; break; } } if (all) { playlistItem.PlayMethod = PlayMethod.DirectStream; playlistItem.Container = item.Container; return playlistItem; } } } } 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) { playlistItem.PlayMethod = PlayMethod.Transcode; playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.Container = transcodingProfile.Container; playlistItem.AudioCodec = transcodingProfile.AudioCodec; playlistItem.Protocol = transcodingProfile.Protocol; List<CodecProfile> audioCodecProfiles = new List<CodecProfile>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Audio && i.ContainsCodec(transcodingProfile.AudioCodec)) { audioCodecProfiles.Add(i); } if (audioCodecProfiles.Count >= 1) break; } List<ProfileCondition> audioTranscodingConditions = new List<ProfileCondition>(); foreach (CodecProfile i in audioCodecProfiles) { foreach (var c in i.Conditions) { audioTranscodingConditions.Add(c); } } 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 = 128000; } } return playlistItem; }
private static List <NameValuePair> BuildParams(StreamInfo item, string accessToken) { var list = new List <NameValuePair>(); string audioCodecs = item.AudioCodecs.Length == 0 ? string.Empty : string.Join(",", item.AudioCodecs); string videoCodecs = item.VideoCodecs.Length == 0 ? string.Empty : string.Join(",", item.VideoCodecs); list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty)); list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty)); list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty)); list.Add(new NameValuePair("Static", item.IsDirectStream.ToString().ToLower())); list.Add(new NameValuePair("VideoCodec", videoCodecs)); list.Add(new NameValuePair("AudioCodec", audioCodecs)); list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); long startPositionTicks = item.StartPositionTicks; var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"); if (isHls) { list.Add(new NameValuePair("StartTimeTicks", string.Empty)); } else { list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture))); } list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); if (!item.IsDirectStream) { if (item.RequireNonAnamorphic) { list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower())); } 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().ToLower())); } if (item.EnableMpegtsM2TsMode) { list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString().ToLower())); } if (item.EstimateContentLength) { list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString().ToLower())); } if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto) { list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLower())); } if (item.CopyTimestamps) { list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower())); } list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString().ToLower())); } list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty)); string subtitleCodecs = item.SubtitleCodecs.Length == 0 ? string.Empty : string.Join(",", item.SubtitleCodecs); list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty)); if (isHls) { list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); if (item.SegmentLength.HasValue) { list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); } if (item.MinSegments.HasValue) { list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); } list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString())); } foreach (var pair in item.StreamOptions) { if (string.IsNullOrEmpty(pair.Value)) { continue; } // strip spaces to avoid having to encode h264 profile names list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", ""))); } if (!item.IsDirectStream) { list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray()))); } return(list); }