/// <inheritdoc /> public async Task <(MediaAttachment attachment, Stream stream)> GetAttachment(BaseItem item, string mediaSourceId, int attachmentStreamIndex, CancellationToken cancellationToken) { if (item == null) { throw new ArgumentNullException(nameof(item)); } if (string.IsNullOrWhiteSpace(mediaSourceId)) { throw new ArgumentNullException(nameof(mediaSourceId)); } var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false); var mediaSource = mediaSources .FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); if (mediaSource == null) { throw new ResourceNotFoundException($"MediaSource {mediaSourceId} not found"); } var mediaAttachment = mediaSource.MediaAttachments .FirstOrDefault(i => i.Index == attachmentStreamIndex); if (mediaAttachment == null) { throw new ResourceNotFoundException($"MediaSource {mediaSourceId} has no attachment with stream index {attachmentStreamIndex}"); } var attachmentStream = await GetAttachmentStream(mediaSource, mediaAttachment, cancellationToken) .ConfigureAwait(false); return(mediaAttachment, attachmentStream); }
private async Task <Tuple <Stream, string> > GetSubtitleStream(string itemId, string mediaSourceId, int subtitleStreamIndex, CancellationToken cancellationToken) { var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId)); var subtitleStream = mediaSource.MediaStreams .First(i => i.Type == MediaStreamType.Subtitle && i.Index == subtitleStreamIndex); var inputFiles = new[] { mediaSource.Path }; if (mediaSource.VideoType.HasValue) { if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd) { var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSourceId)); inputFiles = mediaSourceItem.GetPlayableStreamFiles().ToArray(); } } var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false); var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item2, fileInfo.Item4, cancellationToken).ConfigureAwait(false); return(new Tuple <Stream, string>(stream, fileInfo.Item3)); }
private async Task <PlaybackInfoResponse> GetPlaybackInfo(Guid id, Guid userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) { var user = _userManager.GetUserById(userId); var item = _libraryManager.GetItemById(id); var result = new PlaybackInfoResponse(); if (string.IsNullOrWhiteSpace(liveStreamId)) { IEnumerable <MediaSourceInfo> mediaSources; try { // TODO handle supportedLiveMediaTypes ? mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { mediaSources = new List <MediaSourceInfo>(); _logger.LogError(ex, "Could not find media sources for item id {id}", id); // TODO PlaybackException ?? //result.ErrorCode = ex.ErrorCode; } result.MediaSources = mediaSources.ToArray(); if (!string.IsNullOrWhiteSpace(mediaSourceId)) { result.MediaSources = result.MediaSources .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) .ToArray(); } } else { var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); result.MediaSources = new MediaSourceInfo[] { mediaSource }; } if (result.MediaSources.Length == 0) { if (!result.ErrorCode.HasValue) { result.ErrorCode = PlaybackErrorCode.NoCompatibleStream; } } else { result.MediaSources = Clone(result.MediaSources); result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } return(result); }
private async Task <PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) { var result = new PlaybackInfoResponse(); if (string.IsNullOrWhiteSpace(liveStreamId)) { IEnumerable <MediaSourceInfo> mediaSources; try { mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, supportedLiveMediaTypes, CancellationToken.None).ConfigureAwait(false); } catch (PlaybackException ex) { mediaSources = new List <MediaSourceInfo>(); result.ErrorCode = ex.ErrorCode; } result.MediaSources = mediaSources.ToList(); if (!string.IsNullOrWhiteSpace(mediaSourceId)) { result.MediaSources = result.MediaSources .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) .ToList(); } } else { var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); result.MediaSources = new List <MediaSourceInfo> { mediaSource }; } if (result.MediaSources.Count == 0) { if (!result.ErrorCode.HasValue) { result.ErrorCode = PlaybackErrorCode.NoCompatibleStream; } } else { result.MediaSources = Clone(result.MediaSources); result.PlaySessionId = Guid.NewGuid().ToString("N"); } return(result); }
public async Task <Stream> GetSubtitles(string itemId, string mediaSourceId, int subtitleStreamIndex, string outputFormat, long startTimeTicks, long?endTimeTicks, bool preserveOriginalTimestamps, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(itemId)) { throw new ArgumentNullException("itemId"); } if (string.IsNullOrWhiteSpace(mediaSourceId)) { throw new ArgumentNullException("mediaSourceId"); } var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); var subtitleStream = mediaSource.MediaStreams .First(i => i.Type == MediaStreamType.Subtitle && i.Index == subtitleStreamIndex); var subtitle = await GetSubtitleStream(mediaSource, subtitleStream, cancellationToken) .ConfigureAwait(false); var inputFormat = subtitle.Item2; var writer = TryGetWriter(outputFormat); // Return the original if we don't have any way of converting it if (writer == null) { return(subtitle.Item1); } // Return the original if the same format is being requested // Character encoding was already handled in GetSubtitleStream if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase)) { return(subtitle.Item1); } using (var stream = subtitle.Item1) { return(ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, preserveOriginalTimestamps, cancellationToken)); } }
async Task <Stream> ISubtitleEncoder.GetSubtitles(BaseItem item, string mediaSourceId, int subtitleStreamIndex, string outputFormat, long startTimeTicks, long endTimeTicks, bool preserveOriginalTimestamps, CancellationToken cancellationToken) { if (item == null) { throw new ArgumentNullException(nameof(item)); } if (string.IsNullOrWhiteSpace(mediaSourceId)) { throw new ArgumentNullException(nameof(mediaSourceId)); } // TODO network path substition useful ? var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, true, cancellationToken).ConfigureAwait(false); var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); var subtitleStream = mediaSource.MediaStreams .First(i => i.Type == MediaStreamType.Subtitle && i.Index == subtitleStreamIndex); var subtitle = await GetSubtitleStream(mediaSource, subtitleStream, cancellationToken) .ConfigureAwait(false); var inputFormat = subtitle.format; var writer = TryGetWriter(outputFormat); // Return the original if we don't have any way of converting it if (writer == null) { return(subtitle.stream); } // Return the original if the same format is being requested // Character encoding was already handled in GetSubtitleStream if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase)) { return(subtitle.stream); } using (var stream = subtitle.stream) { return(ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, preserveOriginalTimestamps, cancellationToken)); } }
private async Task <object> GetPlaybackInfo(string id, string userId) { IEnumerable <MediaSourceInfo> mediaSources; var result = new LiveMediaInfoResult(); try { mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false); } catch (PlaybackException ex) { mediaSources = new List <MediaSourceInfo>(); result.ErrorCode = ex.ErrorCode; } result.MediaSources = mediaSources.ToList(); return(ToOptimizedResult(result)); }
public async Task <EncodingJob> CreateJob(EncodingJobOptions options, bool isVideoRequest, IProgress <double> progress, CancellationToken cancellationToken) { var request = options; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.OutputContainer); } var state = new EncodingJob(_logger, _mediaSourceManager) { Options = options, IsVideoRequest = isVideoRequest, Progress = progress }; if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } var item = _libraryManager.GetItemById(request.ItemId); state.ItemType = item.GetType().Name; state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); var videoRequest = state.Options; AttachMediaSourceInfo(state, mediaSource, videoRequest); //var container = Path.GetExtension(state.RequestedUrl); //if (string.IsNullOrEmpty(container)) //{ // container = request.Static ? // state.InputContainer : // (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.'); //} //state.OutputContainer = (container ?? string.Empty).TrimStart('.'); state.OutputAudioBitrate = GetAudioBitrateParam(state.Options, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioCodec = state.Options.AudioCodec; state.OutputAudioChannels = GetNumAudioChannelsParam(state.Options, state.AudioStream, state.OutputAudioCodec); if (videoRequest != null) { state.OutputVideoCodec = state.Options.VideoCodec; state.OutputVideoBitrate = GetVideoBitrateParamValue(state.Options, state.VideoStream); if (state.OutputVideoBitrate.HasValue) { var resolution = ResolutionNormalizer.Normalize( state.VideoStream == null ? (int?)null : state.VideoStream.BitRate, state.OutputVideoBitrate.Value, state.VideoStream == null ? null : state.VideoStream.Codec, state.OutputVideoCodec, videoRequest.MaxWidth, videoRequest.MaxHeight); videoRequest.MaxWidth = resolution.MaxWidth; videoRequest.MaxHeight = resolution.MaxHeight; } } ApplyDeviceProfileSettings(state); if (videoRequest != null) { TryStreamCopy(state, videoRequest); } //state.OutputFilePath = GetOutputFilePath(state); return(state); }
public async Task <EncodingJob> CreateJob(EncodingJobOptions options, EncodingHelper encodingHelper, bool isVideoRequest, IProgress <double> progress, CancellationToken cancellationToken) { var request = options; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.Container); } var state = new EncodingJob(_logger, _mediaSourceManager) { Options = options, IsVideoRequest = isVideoRequest, Progress = progress }; if (!string.IsNullOrWhiteSpace(request.VideoCodec)) { state.SupportedVideoCodecs = request.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); request.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(request.SubtitleCodec)) { state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(); request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToSubtitleCodec(i)) ?? state.SupportedSubtitleCodecs.FirstOrDefault(); } var item = _libraryManager.GetItemById(request.Id); state.ItemType = item.GetType().Name; state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); // TODO // var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ?? // item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null); // if (primaryImage != null) // { // state.AlbumCoverPath = primaryImage.Path; // } // TODO network path substition useful ? var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, true, cancellationToken).ConfigureAwait(false); var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); var videoRequest = state.Options; encodingHelper.AttachMediaSourceInfo(state, mediaSource, null); //var container = Path.GetExtension(state.RequestedUrl); //if (string.IsNullOrEmpty(container)) //{ // container = request.Static ? // state.InputContainer : // (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.'); //} //state.OutputContainer = (container ?? string.Empty).TrimStart('.'); state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.Options, state.AudioStream); state.OutputAudioCodec = state.Options.AudioCodec; state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec); if (videoRequest != null) { state.OutputVideoCodec = state.Options.VideoCodec; state.OutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.Options, state.VideoStream, state.OutputVideoCodec); if (state.OutputVideoBitrate.HasValue) { var resolution = ResolutionNormalizer.Normalize( state.VideoStream == null ? (int?)null : state.VideoStream.BitRate, state.VideoStream == null ? (int?)null : state.VideoStream.Width, state.VideoStream == null ? (int?)null : state.VideoStream.Height, state.OutputVideoBitrate.Value, state.VideoStream == null ? null : state.VideoStream.Codec, state.OutputVideoCodec, videoRequest.MaxWidth, videoRequest.MaxHeight); videoRequest.MaxWidth = resolution.MaxWidth; videoRequest.MaxHeight = resolution.MaxHeight; } } ApplyDeviceProfileSettings(state); if (videoRequest != null) { encodingHelper.TryStreamCopy(state); } //state.OutputFilePath = GetOutputFilePath(state); return(state); }
public async Task <EncodingJob> CreateJob(EncodingJobOptions options, bool isVideoRequest, IProgress <double> progress, CancellationToken cancellationToken) { var request = options; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.OutputContainer); } var state = new EncodingJob(_logger, _mediaSourceManager) { Options = options, IsVideoRequest = isVideoRequest, Progress = progress }; if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } var item = _libraryManager.GetItemById(request.ItemId); state.ItemType = item.GetType().Name; state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false); var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); AttachMediaStreamInfo(state, mediaSource, options); state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioCodec = GetAudioCodec(request); state.OutputAudioChannels = GetNumAudioChannelsParam(request, state.AudioStream, state.OutputAudioCodec); if (isVideoRequest) { state.OutputVideoCodec = GetVideoCodec(request); state.OutputVideoBitrate = GetVideoBitrateParamValue(request, state.VideoStream); if (state.OutputVideoBitrate.HasValue) { var resolution = ResolutionNormalizer.Normalize(state.OutputVideoBitrate.Value, state.OutputVideoCodec, request.MaxWidth, request.MaxHeight); request.MaxWidth = resolution.MaxWidth; request.MaxHeight = resolution.MaxHeight; } } ApplyDeviceProfileSettings(state); TryStreamCopy(state, request); state.Quality = options.Context == EncodingContext.Static ? EncodingQuality.MaxQuality : GetQualitySetting(); return(state); }
public async Task <EncodingJob> CreateJob(EncodingJobOptions options, bool isVideoRequest, IProgress <double> progress, CancellationToken cancellationToken) { var request = options; if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.OutputContainer); } var state = new EncodingJob(_logger, _liveTvManager) { Options = options, IsVideoRequest = isVideoRequest, Progress = progress }; if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } var item = _libraryManager.GetItemById(request.ItemId); List <MediaStream> mediaStreams = null; state.ItemType = item.GetType().Name; if (item is ILiveTvRecording) { var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); var path = recording.RecordingInfo.Path; var mediaUrl = recording.RecordingInfo.Url; var source = string.IsNullOrEmpty(request.MediaSourceId) ? recording.GetMediaSources(false).First() : _mediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false); mediaStreams = source.MediaStreams; // Just to prevent this from being null and causing other methods to fail state.MediaPath = string.Empty; if (!string.IsNullOrEmpty(path)) { state.MediaPath = path; state.InputProtocol = MediaProtocol.File; } else if (!string.IsNullOrEmpty(mediaUrl)) { state.MediaPath = mediaUrl; state.InputProtocol = MediaProtocol.Http; } state.RunTimeTicks = recording.RunTimeTicks; state.DeInterlace = true; state.OutputAudioSync = "1000"; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; state.InputContainer = recording.Container; state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate; } else if (item is LiveTvChannel) { var channel = _liveTvManager.GetInternalChannel(request.ItemId); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); mediaStreams = new List <MediaStream>(); state.DeInterlace = true; // Just to prevent this from being null and causing other methods to fail state.MediaPath = string.Empty; } else { var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources.First() : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); mediaStreams = mediaSource.MediaStreams; state.MediaPath = mediaSource.Path; state.InputProtocol = mediaSource.Protocol; state.InputContainer = mediaSource.Container; state.InputFileSize = mediaSource.Size; state.InputBitrate = mediaSource.Bitrate; state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; state.RunTimeTicks = mediaSource.RunTimeTicks; state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; var video = item as Video; if (video != null) { state.IsInputVideo = true; if (mediaSource.VideoType.HasValue) { state.VideoType = mediaSource.VideoType.Value; } state.IsoType = mediaSource.IsoType; state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); if (mediaSource.Timestamp.HasValue) { state.InputTimestamp = mediaSource.Timestamp.Value; } } state.RunTimeTicks = mediaSource.RunTimeTicks; } AttachMediaStreamInfo(state, mediaStreams, request); state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; state.OutputAudioCodec = GetAudioCodec(request); state.OutputAudioChannels = GetNumAudioChannelsParam(request, state.AudioStream, state.OutputAudioCodec); if (isVideoRequest) { state.OutputVideoCodec = GetVideoCodec(request); state.OutputVideoBitrate = GetVideoBitrateParamValue(request, state.VideoStream); if (state.OutputVideoBitrate.HasValue) { var resolution = ResolutionNormalizer.Normalize(state.OutputVideoBitrate.Value, state.OutputVideoCodec, request.MaxWidth, request.MaxHeight); request.MaxWidth = resolution.MaxWidth; request.MaxHeight = resolution.MaxHeight; } } ApplyDeviceProfileSettings(state); if (isVideoRequest) { if (state.VideoStream != null && CanStreamCopyVideo(request, state.VideoStream)) { state.OutputVideoCodec = "copy"; } if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs)) { state.OutputAudioCodec = "copy"; } } return(state); }