public StreamInfo BuildVideoItem(VideoOptions options) { ValidateInput(options); List<MediaSourceInfo> mediaSources = options.MediaSources; // If the client wants a specific media source, filter now if (!string.IsNullOrEmpty(options.MediaSourceId)) { var newMediaSources = new List<MediaSourceInfo>(); foreach (MediaSourceInfo i in mediaSources) { if (StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId)) newMediaSources.Add(i); } mediaSources = newMediaSources; } List<StreamInfo> streams = new List<StreamInfo>(); foreach (MediaSourceInfo i in mediaSources) streams.Add(BuildVideoItem(i, options)); foreach (StreamInfo stream in streams) { stream.DeviceId = options.DeviceId; stream.DeviceProfileId = options.Profile.Id; } return GetOptimalStream(streams); }
public StreamInfo BuildVideoItem(VideoOptions options) { ValidateInput(options); List<MediaSourceInfo> mediaSources = new List<MediaSourceInfo>(); foreach (MediaSourceInfo i in options.MediaSources) { if (string.IsNullOrEmpty(options.MediaSourceId) || StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId)) { mediaSources.Add(i); } } List<StreamInfo> streams = new List<StreamInfo>(); foreach (MediaSourceInfo i in mediaSources) { StreamInfo streamInfo = BuildVideoItem(i, options); if (streamInfo != null) { streams.Add(streamInfo); } } foreach (StreamInfo stream in streams) { stream.DeviceId = options.DeviceId; stream.DeviceProfileId = options.Profile.Id; } return GetOptimalStream(streams); }
public StreamInfo BuildVideoItem(VideoOptions options) { ValidateInput(options); var mediaSources = options.MediaSources; // If the client wants a specific media soure, filter now if (!string.IsNullOrEmpty(options.MediaSourceId)) { // Avoid implicitly captured closure var mediaSourceId = options.MediaSourceId; mediaSources = mediaSources .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) .ToList(); } var streams = mediaSources.Select(i => BuildVideoItem(i, options)).ToList(); foreach (var stream in streams) { stream.DeviceId = options.DeviceId; stream.DeviceProfileId = options.Profile.Id; } return GetOptimalStream(streams); }
public Task<StreamInfo> GetVideoStreamInfo(string serverId, VideoOptions options) { var apiClient = _connectionManager.GetApiClient(serverId); options.DeviceId = _connectionManager.Device.DeviceId; return _apiPlaybackManager.GetVideoStreamInfo(serverId, options, false, apiClient); }
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { var playlistItem = new StreamInfo { ItemId = options.ItemId, MediaType = DlnaProfileType.Video, MediaSourceId = item.Id }; var audioStream = item.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); var videoStream = item.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); if (IsEligibleForDirectPlay(item, options)) { // See if it can be direct played var directPlay = options.Profile.DirectPlayProfiles .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsVideoDirectPlaySupported(i, item, videoStream, audioStream)); if (directPlay != null) { var videoCodec = videoStream == null ? null : videoStream.Codec; // Make sure video codec profiles are satisfied if (!string.IsNullOrEmpty(videoCodec) && options.Profile.CodecProfiles.Where(i => i.Type == CodecType.Video && i.ContainsCodec(videoCodec)) .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream))) { var audioCodec = audioStream == null ? null : audioStream.Codec; // Make sure audio codec profiles are satisfied if (string.IsNullOrEmpty(audioCodec) || options.Profile.CodecProfiles.Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(audioCodec)) .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream))) { playlistItem.IsDirectStream = true; playlistItem.Container = item.Container; return playlistItem; } } } } // Can't direct play, find the transcoding profile var transcodingProfile = options.Profile.TranscodingProfiles .FirstOrDefault(i => i.Type == playlistItem.MediaType); if (transcodingProfile != null) { playlistItem.IsDirectStream = false; playlistItem.Container = transcodingProfile.Container; playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault(); playlistItem.VideoCodec = transcodingProfile.VideoCodec; var videoTranscodingConditions = options.Profile.CodecProfiles .Where(i => i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec)) .Take(1) .SelectMany(i => i.Conditions); ApplyTranscodingConditions(playlistItem, videoTranscodingConditions); var audioTranscodingConditions = options.Profile.CodecProfiles .Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(transcodingProfile.AudioCodec)) .Take(1) .SelectMany(i => i.Conditions); ApplyTranscodingConditions(playlistItem, audioTranscodingConditions); // Honor requested max channels if (options.MaxAudioChannels.HasValue) { var currentValue = playlistItem.MaxAudioChannels ?? options.MaxAudioChannels.Value; playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); } // Honor requested max bitrate if (options.MaxAudioTranscodingBitrate.HasValue) { var currentValue = playlistItem.AudioBitrate ?? options.MaxAudioTranscodingBitrate.Value; playlistItem.AudioBitrate = Math.Min(options.MaxAudioTranscodingBitrate.Value, currentValue); } // Honor max rate if (options.MaxBitrate.HasValue) { var videoBitrate = options.MaxBitrate.Value; if (playlistItem.AudioBitrate.HasValue) { videoBitrate -= playlistItem.AudioBitrate.Value; } var currentValue = playlistItem.VideoBitrate ?? videoBitrate; playlistItem.VideoBitrate = Math.Min(videoBitrate, currentValue); } } return playlistItem; }
private void ValidateInput(VideoOptions options) { ValidateAudioInput(options); if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId)) { throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested"); } if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId)) { throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested"); } }
private bool IsEligibleForDirectPlay(MediaSourceInfo item, int? maxBitrate, MediaStream subtitleStream, VideoOptions options, PlayMethod playMethod) { if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context, playMethod); if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) { _logger.Debug("Not eligible for {0} due to unsupported subtitles", playMethod); return false; } } return IsAudioEligibleForDirectPlay(item, maxBitrate); }
private SubtitleDeliveryMethod GetSubtitleDeliveryMethod(MediaStream subtitleStream, VideoOptions options) { if (subtitleStream.IsTextSubtitleStream) { // See if the device can retrieve the subtitles externally bool supportsSubsExternally = options.Context == EncodingContext.Streaming && ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.External, _serverTextSubtitleOutputs); if (supportsSubsExternally) { return SubtitleDeliveryMethod.External; } // See if the device can retrieve the subtitles externally bool supportsEmbedded = ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.Embed, _serverTextSubtitleOutputs); if (supportsEmbedded) { return SubtitleDeliveryMethod.Embed; } } return SubtitleDeliveryMethod.Encode; }
/// <summary> /// Changes the video stream. /// </summary> /// <param name="currentInfo">The current information.</param> /// <param name="serverId">The server identifier.</param> /// <param name="options">The options.</param> /// <param name="apiClient">The API client.</param> /// <returns>Task<StreamInfo>.</returns> /// <exception cref="MediaBrowser.Model.Dlna.PlaybackException"></exception> public async Task<StreamInfo> ChangeVideoStream(StreamInfo currentInfo, string serverId, VideoOptions options, IApiClient apiClient) { await StopStranscoding(currentInfo, apiClient).ConfigureAwait(false); if (currentInfo.AllMediaSources != null) { options.MediaSources = currentInfo.AllMediaSources; } var streamInfo = await GetVideoStreamInfoInternal(serverId, options).ConfigureAwait(false); streamInfo.PlaySessionId = currentInfo.PlaySessionId; streamInfo.AllMediaSources = currentInfo.AllMediaSources; return streamInfo; }
private SubtitleDeliveryMethod GetDirectStreamSubtitleDeliveryMethod(MediaStream subtitleStream, VideoOptions options) { if (subtitleStream.IsTextSubtitleStream) { string subtitleFormat = NormalizeSubtitleFormat(subtitleStream.Codec); bool supportsDirect = ContainsSubtitleFormat(options.Profile.SoftSubtitleProfiles, new[] { subtitleFormat }); if (supportsDirect) { return SubtitleDeliveryMethod.Direct; } // See if the device can retrieve the subtitles externally bool supportsSubsExternally = options.Context == EncodingContext.Streaming && ContainsSubtitleFormat(options.Profile.ExternalSubtitleProfiles, _serverTextSubtitleOutputs); if (supportsSubsExternally) { return SubtitleDeliveryMethod.External; } } return SubtitleDeliveryMethod.Encode; }
private PlayMethod? GetVideoDirectPlayProfile(VideoOptions options, MediaSourceInfo mediaSource, MediaStream videoStream, MediaStream audioStream, bool isEligibleForDirectPlay, bool isEligibleForDirectStream) { DeviceProfile profile = options.Profile; if (options.ForceDirectPlay) { return PlayMethod.DirectPlay; } if (options.ForceDirectStream) { return PlayMethod.DirectStream; } if (videoStream == null) { _logger.Info("Profile: {0}, Cannot direct stream with no known video stream. Path: {1}", profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); return null; } // See if it can be direct played DirectPlayProfile directPlay = null; foreach (DirectPlayProfile i in profile.DirectPlayProfiles) { if (i.Type == DlnaProfileType.Video && IsVideoDirectPlaySupported(i, mediaSource, videoStream, audioStream)) { directPlay = i; break; } } if (directPlay == null) { _logger.Info("Profile: {0}, No direct play profiles found for Path: {1}", profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); return null; } string container = mediaSource.Container; List<ProfileCondition> conditions = new List<ProfileCondition>(); foreach (ContainerProfile i in profile.ContainerProfiles) { if (i.Type == DlnaProfileType.Video && ListHelper.ContainsIgnoreCase(i.GetContainers(), container)) { foreach (ProfileCondition c in i.Conditions) { conditions.Add(c); } } } ConditionProcessor conditionProcessor = new ConditionProcessor(); int? width = videoStream == null ? null : videoStream.Width; int? height = videoStream == null ? null : videoStream.Height; int? bitDepth = videoStream == null ? null : videoStream.BitDepth; int? videoBitrate = videoStream == null ? null : videoStream.BitRate; double? videoLevel = videoStream == null ? null : videoStream.Level; string videoProfile = videoStream == null ? null : videoStream.Profile; float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate; bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; string videoCodecTag = videoStream == null ? null : videoStream.CodecTag; int? audioBitrate = audioStream == null ? null : audioStream.BitRate; int? audioChannels = audioStream == null ? null : audioStream.Channels; string audioProfile = audioStream == null ? null : audioStream.Profile; TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : mediaSource.Timestamp; int? packetLength = videoStream == null ? null : videoStream.PacketLength; int? refFrames = videoStream == null ? null : videoStream.RefFrames; int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio); int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video); // Check container conditions foreach (ProfileCondition i in conditions) { if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) { LogConditionFailure(profile, "VideoContainerProfile", i, mediaSource); return null; } } string videoCodec = videoStream == null ? null : videoStream.Codec; if (string.IsNullOrEmpty(videoCodec)) { _logger.Info("Profile: {0}, DirectPlay=false. Reason=Unknown video codec. Path: {1}", profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); return null; } conditions = new List<ProfileCondition>(); foreach (CodecProfile i in profile.CodecProfiles) { if (i.Type == CodecType.Video && i.ContainsCodec(videoCodec, container)) { bool applyConditions = true; foreach (ProfileCondition applyCondition in i.ApplyConditions) { if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) { LogConditionFailure(profile, "VideoCodecProfile", applyCondition, mediaSource); applyConditions = false; break; } } if (applyConditions) { foreach (ProfileCondition c in i.Conditions) { conditions.Add(c); } } } } foreach (ProfileCondition i in conditions) { if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) { LogConditionFailure(profile, "VideoCodecProfile", i, mediaSource); return null; } } if (audioStream != null) { string audioCodec = audioStream.Codec; if (string.IsNullOrEmpty(audioCodec)) { _logger.Info("Profile: {0}, DirectPlay=false. Reason=Unknown audio codec. Path: {1}", profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); return null; } conditions = new List<ProfileCondition>(); bool? isSecondaryAudio = audioStream == null ? null : mediaSource.IsSecondaryAudio(audioStream); foreach (CodecProfile i in profile.CodecProfiles) { if (i.Type == CodecType.VideoAudio && i.ContainsCodec(audioCodec, container)) { bool applyConditions = true; foreach (ProfileCondition applyCondition in i.ApplyConditions) { if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioProfile, isSecondaryAudio)) { LogConditionFailure(profile, "VideoAudioCodecProfile", applyCondition, mediaSource); applyConditions = false; break; } } if (applyConditions) { foreach (ProfileCondition c in i.Conditions) { conditions.Add(c); } } } } foreach (ProfileCondition i in conditions) { if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile, isSecondaryAudio)) { LogConditionFailure(profile, "VideoAudioCodecProfile", i, mediaSource); return null; } } } if (isEligibleForDirectStream && mediaSource.SupportsDirectStream) { return PlayMethod.DirectStream; } return null; }
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 = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), subtitleStream, options, PlayMethod.DirectPlay)); bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(false), 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, 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.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest; if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)) { int transcodingMaxAudioChannels; if (IntHelper.TryParseCultureInvariant(transcodingProfile.MaxAudioChannels, out transcodingMaxAudioChannels)) { playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels; } } playlistItem.SubProtocol = transcodingProfile.Protocol; playlistItem.AudioStreamIndex = audioStreamIndex; ConditionProcessor conditionProcessor = new ConditionProcessor(); List<ProfileCondition> videoTranscodingConditions = new List<ProfileCondition>(); foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec, transcodingProfile.Container)) { bool applyConditions = true; foreach (ProfileCondition applyCondition in i.ApplyConditions) { bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream); int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate; int? audioChannels = audioStream == null ? null : audioStream.Channels; string audioProfile = audioStream == null ? null : audioStream.Profile; if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, audioProfile, isSecondaryAudio)) { LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item); applyConditions = false; break; } } if (applyConditions) { 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)) { bool applyConditions = true; foreach (ProfileCondition applyCondition in i.ApplyConditions) { int? width = videoStream == null ? null : videoStream.Width; int? height = videoStream == null ? null : videoStream.Height; int? bitDepth = videoStream == null ? null : videoStream.BitDepth; int? videoBitrate = videoStream == null ? null : videoStream.BitRate; double? videoLevel = videoStream == null ? null : videoStream.Level; string videoProfile = videoStream == null ? null : videoStream.Profile; float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate; bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; string videoCodecTag = videoStream == null ? null : videoStream.CodecTag; TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp; int? packetLength = videoStream == null ? null : videoStream.PacketLength; int? refFrames = videoStream == null ? null : videoStream.RefFrames; int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio); int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video); if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) { LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item); applyConditions = false; break; } } if (applyConditions) { 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(playlistItem.SubProtocol, options.GetMaxBitrate(false), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream); playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate); int? maxBitrateSetting = options.GetMaxBitrate(false); // 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<string> Sync(SyncJobItem jobItem, Video item, DeviceProfile profile, CancellationToken cancellationToken) { var options = new VideoOptions { Context = EncodingContext.Streaming, ItemId = item.Id.ToString("N"), DeviceId = jobItem.TargetId, Profile = profile, MediaSources = item.GetMediaSources(false).ToList() }; var streamInfo = new StreamBuilder().BuildVideoItem(options); var mediaSource = streamInfo.MediaSource; if (streamInfo.PlayMethod != PlayMethod.Transcode) { if (mediaSource.Protocol == MediaProtocol.File) { return mediaSource.Path; } if (mediaSource.Protocol == MediaProtocol.Http) { return await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false); } throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol)); } // TODO: Transcode return mediaSource.Path; }
private bool IsEligibleForDirectPlay(MediaSourceInfo item, VideoOptions options) { if (options.SubtitleStreamIndex.HasValue) { return false; } if (options.AudioStreamIndex.HasValue && item.MediaStreams.Count(i => i.Type == MediaStreamType.Audio) > 1) { return false; } return IsAudioEligibleForDirectPlay(item, options); }
private StreamInfo CreateVideoStream(string itemId, long startTimeTicks, List<MediaSourceInfo> mediaSources = null, bool useHls = false) { var profile = VideoProfileHelper.GetWindowsPhoneProfile(useHls); var streamingSettings = NavigationService.IsOnWifi ? App.SpecificSettings.WifiStreamingQuality.GetSettings() : App.SpecificSettings.StreamingQuality.GetSettings(); var options = new VideoOptions { Profile = profile, ItemId = itemId, DeviceId = ApiClient.DeviceId, MaxBitrate = streamingSettings.VideoBitrate, MediaSources = mediaSources }; var builder = new StreamBuilder(); var streamInfo = builder.BuildVideoItem(options); streamInfo.StartPositionTicks = startTimeTicks; return streamInfo; }
/// <summary> /// Gets the video stream information. /// </summary> /// <param name="serverId">The server identifier.</param> /// <param name="options">The options.</param> /// <param name="isOffline">if set to <c>true</c> [is offline].</param> /// <param name="apiClient">The API client.</param> /// <returns>Task<StreamInfo>.</returns> public async Task<StreamInfo> GetVideoStreamInfo(string serverId, VideoOptions options, bool isOffline, IApiClient apiClient) { PlaybackInfoResponse playbackInfo = null; string playSessionId = null; if (!isOffline) { playbackInfo = await apiClient.GetPlaybackInfo(new PlaybackInfoRequest { Id = options.ItemId, UserId = apiClient.CurrentUserId, MaxStreamingBitrate = options.MaxBitrate, MediaSourceId = options.MediaSourceId, AudioStreamIndex = options.AudioStreamIndex, SubtitleStreamIndex = options.SubtitleStreamIndex }).ConfigureAwait(false); if (playbackInfo.ErrorCode.HasValue) { throw new PlaybackException { ErrorCode = playbackInfo.ErrorCode.Value }; } options.MediaSources = playbackInfo.MediaSources; playSessionId = playbackInfo.PlaySessionId; } var streamInfo = await GetVideoStreamInfoInternal(serverId, options).ConfigureAwait(false); if (!isOffline) { var liveMediaSource = await GetLiveStreamInfo(playSessionId, streamInfo.MediaSource, options, apiClient).ConfigureAwait(false); if (liveMediaSource != null) { options.MediaSources = new List<MediaSourceInfo> { liveMediaSource }; streamInfo = GetStreamBuilder().BuildVideoItem(options); EnsureSuccess(streamInfo); } } if (playbackInfo != null) { streamInfo.AllMediaSources = playbackInfo.MediaSources.ToList(); streamInfo.PlaySessionId = playbackInfo.PlaySessionId; } return streamInfo; }
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 async Task<StreamInfo> GetVideoStreamInfoInternal(string serverId, VideoOptions options) { var streamBuilder = GetStreamBuilder(); var localItem = await _localAssetManager.GetLocalItem(serverId, options.ItemId); if (localItem != null) { var localMediaSource = localItem.Item.MediaSources[0]; // Use the local media source, unless a specific server media source was requested if (string.IsNullOrWhiteSpace(options.MediaSourceId) || string.Equals(localMediaSource.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)) { // Finally, check to make sure the local file is actually available at this time var fileExists = await _localAssetManager.FileExists(localMediaSource.Path).ConfigureAwait(false); if (fileExists) { options.MediaSources = localItem.Item.MediaSources; var result = streamBuilder.BuildVideoItem(options); if (result == null) { _logger.Warn("LocalItem returned no compatible streams. Will dummy up a StreamInfo to force it to direct play."); var mediaSource = localItem.Item.MediaSources.First(); result = GetForcedDirectPlayStreamInfo(DlnaProfileType.Video, options, mediaSource); } result.PlayMethod = PlayMethod.DirectPlay; return result; } } } var streamInfo = streamBuilder.BuildVideoItem(options); EnsureSuccess(streamInfo); return streamInfo; }
private bool IsEligibleForDirectPlay(MediaSourceInfo item, int? maxBitrate, MediaStream subtitleStream, VideoOptions options) { if (subtitleStream != null) { if (!subtitleStream.IsTextSubtitleStream) { return false; } SubtitleDeliveryMethod subtitleMethod = GetSubtitleDeliveryMethod(subtitleStream, options); if (subtitleMethod != SubtitleDeliveryMethod.External && subtitleMethod != SubtitleDeliveryMethod.Embed) { return false; } } return IsAudioEligibleForDirectPlay(item, maxBitrate); }
/// <summary> /// Gets the pre playback selectable subtitle streams. /// </summary> /// <param name="serverId">The server identifier.</param> /// <param name="options">The options.</param> /// <returns>Task<IEnumerable<MediaStream>>.</returns> public async Task<IEnumerable<MediaStream>> GetPrePlaybackSelectableSubtitleStreams(string serverId, VideoOptions options) { var info = await GetVideoStreamInfoInternal(serverId, options).ConfigureAwait(false); return info.GetSelectableSubtitleStreams(); }
private async Task<StreamInfo> CreateVideoStream(string itemId, long startTimeTicks, List<MediaSourceInfo> mediaSources = null, bool useHls = false) { if (ForceHls) { useHls = true; } var profile = VideoProfileHelper.GetWindowsPhoneProfile(isHls: useHls); var streamingSettings = NavigationService.IsOnWifi ? App.SpecificSettings.WifiStreamingQuality.GetSettings() : App.SpecificSettings.StreamingQuality.GetSettings(); var options = new VideoOptions { Profile = profile, ItemId = itemId, DeviceId = ApiClient.DeviceId, MaxBitrate = streamingSettings.VideoBitrate, MediaSources = mediaSources }; try { var streamInfo = await _playbackManager.GetVideoStreamInfo(App.ServerInfo.Id, options, false, ApiClient); streamInfo.StartPositionTicks = startTimeTicks; return streamInfo; } catch (PlaybackException pex) { Log.ErrorException("CreateVideoStream: " + pex.ErrorCode, pex); switch (pex.ErrorCode) { case PlaybackErrorCode.NoCompatibleStream: break; case PlaybackErrorCode.NotAllowed: break; case PlaybackErrorCode.RateLimitExceeded: break; } } catch (Exception ex) { Log.ErrorException("CreateVideoStream", ex); } return null; }
private async Task<PlayableItem> GetPlaybackInfoInternal(BaseItemDto item, long? startTimeTicks, CancellationToken cancellationToken) { var profile = new MediaBrowserTheaterProfile(); var options = new VideoOptions { Context = EncodingContext.Streaming, ItemId = item.Id, // TODO: Set to 2 if user only has stereo speakers MaxAudioChannels = 6, MaxBitrate = _config.Configuration.MaxStreamingBitrate, MediaSources = item.MediaSources, Profile = profile }; var streamInfo = item.IsAudio ? await _playbackManager.GetAudioStreamInfo(item.ServerId, options) : await _playbackManager.GetVideoStreamInfo(item.ServerId, options); streamInfo.StartPositionTicks = startTimeTicks ?? 0; var apiClient = _connectionManager.GetApiClient(item); return new PlayableItem { OriginalItem = item, PlayablePath = streamInfo.ToUrl(apiClient.ServerAddress + "/mediabrowser", apiClient.AccessToken), MediaSource = streamInfo.MediaSource, StreamInfo = streamInfo }; }
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 Sync(SyncJobItem jobItem, SyncJob job, Video item, User user, bool enableConversion, SyncOptions syncOptions, IProgress<double> progress, CancellationToken cancellationToken) { var jobOptions = _syncManager.GetVideoOptions(jobItem, job); var conversionOptions = new VideoOptions { Profile = jobOptions.DeviceProfile }; conversionOptions.DeviceId = jobItem.TargetId; conversionOptions.Context = EncodingContext.Static; conversionOptions.ItemId = item.Id.ToString("N"); conversionOptions.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, false, user).ToList(); var streamInfo = new StreamBuilder(_logger).BuildVideoItem(conversionOptions); var mediaSource = streamInfo.MediaSource; // No sense creating external subs if we're already burning one into the video var externalSubs = streamInfo.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ? new List<SubtitleStreamInfo>() : streamInfo.GetExternalSubtitles(false, true, null, null); // Mark as requiring conversion if transcoding the video, or if any subtitles need to be extracted var requiresVideoTranscoding = streamInfo.PlayMethod == PlayMethod.Transcode && jobOptions.IsConverting; var requiresConversion = requiresVideoTranscoding || externalSubs.Any(i => RequiresExtraction(i, mediaSource)); if (requiresConversion && !enableConversion) { return; } jobItem.MediaSourceId = streamInfo.MediaSourceId; jobItem.TemporaryPath = GetTemporaryPath(jobItem); if (requiresConversion) { jobItem.Status = SyncJobItemStatus.Converting; } if (requiresVideoTranscoding) { // Save the job item now since conversion could take a while await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); await UpdateJobStatus(job).ConfigureAwait(false); try { var lastJobUpdate = DateTime.MinValue; var innerProgress = new ActionableProgress<double>(); innerProgress.RegisterAction(async pct => { progress.Report(pct); if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds) { jobItem.Progress = pct / 2; await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); await UpdateJobStatus(job).ConfigureAwait(false); } }); jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, conversionOptions.Profile) { OutputDirectory = jobItem.TemporaryPath, CpuCoreLimit = syncOptions.TranscodingCpuCoreLimit, ReadInputAtNativeFramerate = !syncOptions.EnableFullSpeedTranscoding }, innerProgress, cancellationToken); } catch (OperationCanceledException) { jobItem.Status = SyncJobItemStatus.Queued; jobItem.Progress = 0; } catch (Exception ex) { jobItem.Status = SyncJobItemStatus.Failed; _logger.ErrorException("Error during sync transcoding", ex); } if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued) { await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); return; } jobItem.MediaSource = await GetEncodedMediaSource(jobItem.OutputPath, user, true).ConfigureAwait(false); } else { if (mediaSource.Protocol == MediaProtocol.File) { jobItem.OutputPath = mediaSource.Path; } else if (mediaSource.Protocol == MediaProtocol.Http) { jobItem.OutputPath = await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false); } else { throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol)); } jobItem.MediaSource = mediaSource; } jobItem.MediaSource.SupportsTranscoding = false; if (externalSubs.Count > 0) { // Save the job item now since conversion could take a while await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); await ConvertSubtitles(jobItem, externalSubs, streamInfo, cancellationToken).ConfigureAwait(false); } jobItem.Progress = 50; jobItem.Status = SyncJobItemStatus.ReadyToTransfer; await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); }
private bool IsEligibleForDirectPlay(MediaSourceInfo item, int? maxBitrate, MediaStream subtitleStream, VideoOptions options) { if (subtitleStream != null) { SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context); if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) { return false; } } return IsAudioEligibleForDirectPlay(item, maxBitrate); }
private static PlayableItem GetStreamedItem(BaseItemDto item, List<MediaSourceInfo> mediaSources, IApiClient apiClient, long? startTimeTicks, int? maxBitrate) { var profile = new MediaBrowserTheaterProfile(); StreamInfo info; if (item.IsAudio) { var options = new AudioOptions { Context = EncodingContext.Streaming, DeviceId = apiClient.DeviceId, ItemId = item.Id, // TODO: Reduce to 2 is user only has stereo speakers MaxAudioChannels = 6, MaxBitrate = maxBitrate, MediaSources = mediaSources, Profile = profile }; info = new StreamBuilder().BuildAudioItem(options); info.StartPositionTicks = startTimeTicks ?? 0; if (info.MediaSource.Protocol == MediaProtocol.File && File.Exists(info.MediaSource.Path)) { return new PlayableItem { OriginalItem = item, PlayablePath = info.MediaSource.Path, MediaSource = info.MediaSource, StreamInfo = info }; } return new PlayableItem { OriginalItem = item, PlayablePath = info.ToUrl(apiClient.ServerAddress + "/mediabrowser"), MediaSource = info.MediaSource, StreamInfo = info }; } else { var options = new VideoOptions { Context = EncodingContext.Streaming, DeviceId = apiClient.DeviceId, ItemId = item.Id, // TODO: Reduce to 2 is user only has stereo speakers MaxAudioChannels = 6, MaxBitrate = maxBitrate, MediaSources = mediaSources, Profile = profile }; info = new StreamBuilder().BuildVideoItem(options); info.StartPositionTicks = startTimeTicks ?? 0; if (info.MediaSource.Protocol == MediaProtocol.File && File.Exists(info.MediaSource.Path)) { return new PlayableItem { OriginalItem = item, PlayablePath = info.MediaSource.Path, MediaSource = info.MediaSource, StreamInfo = info }; } //info.Container = "ts"; //info.VideoCodec = "copy"; //info.AudioCodec = "copy"; //info.Protocol = "http"; //if (item.IsType("tvchannel")) //{ // info.VideoCodec = "copy"; //} var playable = new PlayableItem { OriginalItem = item, PlayablePath = info.ToUrl(apiClient.ServerAddress + "/mediabrowser"), MediaSource = info.MediaSource, StreamInfo = info }; if (!info.IsDirectStream) { playable.PlayablePath += "&EnableAdaptiveBitrateStreaming=false"; } return playable; } }
private async Task Sync(SyncJobItem jobItem, Video item, DeviceProfile profile, CancellationToken cancellationToken) { var options = new VideoOptions { Context = EncodingContext.Static, ItemId = item.Id.ToString("N"), DeviceId = jobItem.TargetId, Profile = profile, MediaSources = item.GetMediaSources(false).ToList() }; var streamInfo = new StreamBuilder().BuildVideoItem(options); var mediaSource = streamInfo.MediaSource; jobItem.MediaSourceId = streamInfo.MediaSourceId; if (streamInfo.PlayMethod == PlayMethod.Transcode) { jobItem.Status = SyncJobItemStatus.Converting; await _syncRepo.Update(jobItem).ConfigureAwait(false); jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, profile), new Progress<double>(), cancellationToken); } else { if (mediaSource.Protocol == MediaProtocol.File) { jobItem.OutputPath = mediaSource.Path; } else if (mediaSource.Protocol == MediaProtocol.Http) { jobItem.OutputPath = await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false); } else { throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol)); } } jobItem.Progress = 50; jobItem.Status = SyncJobItemStatus.Transferring; await _syncRepo.Update(jobItem).ConfigureAwait(false); }
private static string GetStreamedPath(BaseItemDto item, IApiClient apiClient, long? startTimeTicks, int? maxBitrate) { var profile = new MediaBrowserTheaterProfile(); StreamInfo info; if (item.IsAudio) { var options = new AudioOptions { Context = EncodingContext.Streaming, DeviceId = apiClient.DeviceId, ItemId = item.Id, // TODO: Reduce to 2 is user only has stereo speakers MaxAudioChannels = 6, MaxBitrate = maxBitrate, MediaSources = item.MediaSources, Profile = profile }; info = new StreamBuilder().BuildAudioItem(options); info.StartPositionTicks = startTimeTicks ?? 0; return info.ToUrl(apiClient.ServerAddress + "/mediabrowser"); } else { var options = new VideoOptions { Context = EncodingContext.Streaming, DeviceId = apiClient.DeviceId, ItemId = item.Id, // TODO: Reduce to 2 is user only has stereo speakers MaxAudioChannels = 6, MaxBitrate = maxBitrate, MediaSources = item.MediaSources, Profile = profile }; info = new StreamBuilder().BuildVideoItem(options); info.StartPositionTicks = startTimeTicks ?? 0; return info.ToUrl(apiClient.ServerAddress + "/mediabrowser") + "&EnableAdaptiveBitrateStreaming=false"; } }