diff options
Diffstat (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs')
| -rw-r--r-- | MediaBrowser.Api/Playback/BaseStreamingService.cs | 179 |
1 files changed, 85 insertions, 94 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 5999a2b55..9f7c1a6c4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; @@ -13,6 +14,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Library; @@ -72,20 +74,14 @@ namespace MediaBrowser.Api.Playback protected ILiveTvManager LiveTvManager { get; private set; } protected IDlnaManager DlnaManager { get; private set; } protected IChannelManager ChannelManager { get; private set; } + protected IHttpClient HttpClient { get; private set; } /// <summary> /// Initializes a new instance of the <see cref="BaseStreamingService" /> class. /// </summary> - /// <param name="serverConfig">The server configuration.</param> - /// <param name="userManager">The user manager.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="isoManager">The iso manager.</param> - /// <param name="mediaEncoder">The media encoder.</param> - /// <param name="dtoService">The dto service.</param> - /// <param name="fileSystem">The file system.</param> - /// <param name="itemRepository">The item repository.</param> - protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) + protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IHttpClient httpClient) { + HttpClient = httpClient; ChannelManager = channelManager; DlnaManager = dlnaManager; EncodingManager = encodingManager; @@ -483,8 +479,12 @@ namespace MediaBrowser.Api.Playback /// <param name="state">The state.</param> /// <param name="outputVideoCodec">The output video codec.</param> /// <param name="performTextSubtitleConversion">if set to <c>true</c> [perform text subtitle conversion].</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>System.String.</returns> - protected string GetOutputSizeParam(StreamState state, string outputVideoCodec, bool performTextSubtitleConversion) + protected string GetOutputSizeParam(StreamState state, + string outputVideoCodec, + bool performTextSubtitleConversion, + CancellationToken cancellationToken) { // http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/ @@ -496,7 +496,7 @@ namespace MediaBrowser.Api.Playback if (state.SubtitleStream != null && !state.SubtitleStream.IsGraphicalSubtitleStream) { - assSubtitleParam = GetTextSubtitleParam(state, performTextSubtitleConversion); + assSubtitleParam = GetTextSubtitleParam(state, performTextSubtitleConversion, cancellationToken); copyTsParam = " -copyts"; } @@ -592,11 +592,15 @@ namespace MediaBrowser.Api.Playback /// </summary> /// <param name="state">The state.</param> /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>System.String.</returns> - protected string GetTextSubtitleParam(StreamState state, bool performConversion) + protected string GetTextSubtitleParam(StreamState state, + bool performConversion, + CancellationToken cancellationToken) { - var path = state.SubtitleStream.IsExternal ? GetConvertedAssPath(state.MediaPath, state.SubtitleStream, performConversion) : - GetExtractedAssPath(state, performConversion); + var path = state.SubtitleStream.IsExternal ? + GetConvertedAssPath(state.SubtitleStream, performConversion, cancellationToken) : + GetExtractedAssPath(state, performConversion, cancellationToken); if (string.IsNullOrEmpty(path)) { @@ -615,8 +619,11 @@ namespace MediaBrowser.Api.Playback /// </summary> /// <param name="state">The state.</param> /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>System.String.</returns> - private string GetExtractedAssPath(StreamState state, bool performConversion) + private string GetExtractedAssPath(StreamState state, + bool performConversion, + CancellationToken cancellationToken) { var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass"); @@ -636,7 +643,7 @@ namespace MediaBrowser.Api.Playback // See https://lists.ffmpeg.org/pipermail/ffmpeg-cvslog/2013-April/063616.html var isAssSubtitle = string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase); - var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, isAssSubtitle, path, CancellationToken.None); + var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, isAssSubtitle, path, cancellationToken); Task.WaitAll(task); } @@ -652,11 +659,13 @@ namespace MediaBrowser.Api.Playback /// <summary> /// Gets the converted ass path. /// </summary> - /// <param name="mediaPath">The media path.</param> /// <param name="subtitleStream">The subtitle stream.</param> /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>System.String.</returns> - private string GetConvertedAssPath(string mediaPath, MediaStream subtitleStream, bool performConversion) + private string GetConvertedAssPath(MediaStream subtitleStream, + bool performConversion, + CancellationToken cancellationToken) { var path = EncodingManager.GetSubtitleCachePath(subtitleStream.Path, ".ass"); @@ -668,7 +677,7 @@ namespace MediaBrowser.Api.Playback Directory.CreateDirectory(parentPath); - var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, CancellationToken.None); + var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, cancellationToken); Task.WaitAll(task); } @@ -696,7 +705,7 @@ namespace MediaBrowser.Api.Playback // Add resolution params, if specified if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) { - outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, false).TrimEnd('"'); + outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, false, CancellationToken.None).TrimEnd('"'); outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase)); } @@ -842,7 +851,7 @@ namespace MediaBrowser.Api.Playback /// <returns>System.String.</returns> protected string GetInputArgument(StreamState state) { - var type = InputType.File; + var type = state.IsRemote ? InputType.Url : InputType.File; var inputPath = new[] { state.MediaPath }; @@ -862,8 +871,10 @@ namespace MediaBrowser.Api.Playback /// </summary> /// <param name="state">The state.</param> /// <param name="outputPath">The output path.</param> + /// <param name="cancellationTokenSource">The cancellation token source.</param> /// <returns>Task.</returns> - protected async Task StartFfMpeg(StreamState state, string outputPath) + /// <exception cref="System.InvalidOperationException">ffmpeg was not found at + MediaEncoder.EncoderPath</exception> + protected async Task StartFfMpeg(StreamState state, string outputPath, CancellationTokenSource cancellationTokenSource) { if (!File.Exists(MediaEncoder.EncoderPath)) { @@ -874,7 +885,7 @@ namespace MediaBrowser.Api.Playback if (state.IsInputVideo && state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath)) { - state.IsoMount = await IsoManager.Mount(state.MediaPath, CancellationToken.None).ConfigureAwait(false); + state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false); } var commandLineArgs = GetCommandLineArguments(outputPath, state, true); @@ -906,7 +917,7 @@ namespace MediaBrowser.Api.Playback EnableRaisingEvents = true }; - ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId); + ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId, cancellationTokenSource); var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments; Logger.Info(commandLineLogMessage); @@ -918,7 +929,7 @@ namespace MediaBrowser.Api.Playback state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine); - await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length).ConfigureAwait(false); + await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); process.Exited += (sender, args) => OnFfMpegProcessExited(process, state); @@ -946,19 +957,19 @@ namespace MediaBrowser.Api.Playback // Wait for the file to exist before proceeeding while (!File.Exists(outputPath)) { - await Task.Delay(100).ConfigureAwait(false); + await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); } // Allow a small amount of time to buffer a little if (state.IsInputVideo) { - await Task.Delay(500).ConfigureAwait(false); + await Task.Delay(500, cancellationTokenSource.Token).ConfigureAwait(false); } // This is arbitrary, but add a little buffer time when internet streaming if (state.IsRemote) { - await Task.Delay(3000).ConfigureAwait(false); + await Task.Delay(3000, cancellationTokenSource.Token).ConfigureAwait(false); } } @@ -1050,13 +1061,19 @@ namespace MediaBrowser.Api.Playback /// <summary> /// Gets the user agent param. /// </summary> - /// <param name="path">The path.</param> + /// <param name="state">The state.</param> /// <returns>System.String.</returns> - private string GetUserAgentParam(string path) + private string GetUserAgentParam(StreamState state) { - var useragent = GetUserAgent(path); + string useragent; + state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent); - if (!string.IsNullOrEmpty(useragent)) + if (string.IsNullOrWhiteSpace(useragent)) + { + useragent = GetUserAgent(state.MediaPath); + } + + if (!string.IsNullOrWhiteSpace(useragent)) { return "-user-agent \"" + useragent + "\""; } @@ -1337,9 +1354,7 @@ namespace MediaBrowser.Api.Playback state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(); } - var item = string.IsNullOrEmpty(request.MediaSourceId) ? - LibraryManager.GetItemById(request.Id) : - LibraryManager.GetItemById(request.MediaSourceId); + var item = LibraryManager.GetItemById(request.Id); if (user != null && item.GetPlayAccess(user) != PlayAccess.Full) { @@ -1427,19 +1442,24 @@ namespace MediaBrowser.Api.Playback } else if (item is IChannelMediaItem) { - var source = await GetChannelMediaInfo(request.Id, CancellationToken.None).ConfigureAwait(false); + var source = await GetChannelMediaInfo(request.Id, request.MediaSourceId, cancellationToken).ConfigureAwait(false); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - state.IsRemote = source.IsRemote; + state.IsRemote = source.LocationType == LocationType.Remote; state.MediaPath = source.Path; state.RunTimeTicks = item.RunTimeTicks; - mediaStreams = GetMediaStreams(source).ToList(); + state.RemoteHttpHeaders = source.RequiredHttpHeaders; + mediaStreams = source.MediaStreams; } else { - state.MediaPath = item.Path; - state.IsRemote = item.LocationType == LocationType.Remote; + var mediaSource = string.IsNullOrWhiteSpace(request.MediaSourceId) + ? item + : LibraryManager.GetItemById(request.MediaSourceId); + + state.MediaPath = mediaSource.Path; + state.IsRemote = mediaSource.LocationType == LocationType.Remote; - var video = item as Video; + var video = mediaSource as Video; if (video != null) { @@ -1461,20 +1481,20 @@ namespace MediaBrowser.Api.Playback state.InputContainer = video.Container; } - var audio = item as Audio; + var audio = mediaSource as Audio; if (audio != null) { state.InputContainer = audio.Container; } - state.RunTimeTicks = item.RunTimeTicks; + state.RunTimeTicks = mediaSource.RunTimeTicks; } var videoRequest = request as VideoStreamRequest; mediaStreams = mediaStreams ?? ItemRepository.GetMediaStreams(new MediaStreamQuery { - ItemId = item.Id + ItemId = new Guid(string.IsNullOrWhiteSpace(request.MediaSourceId) ? request.Id : request.MediaSourceId) }).ToList(); @@ -1545,65 +1565,32 @@ namespace MediaBrowser.Api.Playback } } - private IEnumerable<MediaStream> GetMediaStreams(ChannelMediaInfo info) - { - var list = new List<MediaStream>(); - - if (!string.IsNullOrWhiteSpace(info.VideoCodec) && - !string.IsNullOrWhiteSpace(info.AudioCodec)) - { - list.Add(new MediaStream - { - Type = MediaStreamType.Video, - Width = info.Width, - RealFrameRate = info.Framerate, - Profile = info.VideoProfile, - Level = info.VideoLevel, - Index = -1, - Height = info.Height, - Codec = info.VideoCodec, - BitRate = info.VideoBitrate, - AverageFrameRate = info.Framerate - }); - - list.Add(new MediaStream - { - Type = MediaStreamType.Audio, - Index = -1, - Codec = info.AudioCodec, - BitRate = info.AudioBitrate, - Channels = info.AudioChannels, - SampleRate = info.AudioSampleRate - }); - } - - return list; - } - - private async Task<ChannelMediaInfo> GetChannelMediaInfo(string id, CancellationToken cancellationToken) + private async Task<MediaSourceInfo> GetChannelMediaInfo(string id, + string mediaSourceId, + CancellationToken cancellationToken) { var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(id, cancellationToken) .ConfigureAwait(false); var list = channelMediaSources.ToList(); - var preferredWidth = ServerConfigurationManager.Configuration.ChannelOptions.PreferredStreamingWidth; - - if (preferredWidth.HasValue) + if (!string.IsNullOrWhiteSpace(mediaSourceId)) { - var val = preferredWidth.Value; + var source = list + .FirstOrDefault(i => string.Equals(mediaSourceId, i.Id)); - return list - .OrderBy(i => Math.Abs(i.Width ?? 0 - val)) - .ThenByDescending(i => i.Width ?? 0) - .ThenBy(list.IndexOf) - .First(); + if (source != null) + { + return source; + } + + Logger.Warn("Invalid channel MediaSourceId requested, defaulting to first. Item: {0}. Requested MediaSourceId: {1}.", + id, + mediaSourceId + ); } - return list - .OrderByDescending(i => i.Width ?? 0) - .ThenBy(list.IndexOf) - .First(); + return list.First(); } private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) @@ -1932,7 +1919,11 @@ namespace MediaBrowser.Api.Playback inputModifier += " " + probeSize; inputModifier = inputModifier.Trim(); - inputModifier += " " + GetUserAgentParam(state.MediaPath); + if (state.IsRemote) + { + inputModifier += " " + GetUserAgentParam(state); + } + inputModifier = inputModifier.Trim(); inputModifier += " " + GetFastSeekCommandLineParameter(state.Request); |
