diff options
Diffstat (limited to 'MediaBrowser.Api')
| -rw-r--r-- | MediaBrowser.Api/ApiEntryPoint.cs | 15 | ||||
| -rw-r--r-- | MediaBrowser.Api/ChannelService.cs | 12 | ||||
| -rw-r--r-- | MediaBrowser.Api/Images/ImageByNameService.cs | 10 | ||||
| -rw-r--r-- | MediaBrowser.Api/MediaBrowser.Api.csproj | 1 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/BaseStreamingService.cs | 179 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 24 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 10 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 9 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/AudioService.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs | 6 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Progressive/VideoService.cs | 7 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/ProgressiveStreamService.cs | 73 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/StreamState.cs | 9 |
14 files changed, 151 insertions, 209 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 9a7254c94..75c2d406d 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -117,7 +117,8 @@ namespace MediaBrowser.Api /// <param name="startTimeTicks">The start time ticks.</param> /// <param name="sourcePath">The source path.</param> /// <param name="deviceId">The device id.</param> - public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, long? startTimeTicks, string sourcePath, string deviceId) + /// <param name="cancellationTokenSource">The cancellation token source.</param> + public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, long? startTimeTicks, string sourcePath, string deviceId, CancellationTokenSource cancellationTokenSource) { lock (_activeTranscodingJobs) { @@ -129,7 +130,8 @@ namespace MediaBrowser.Api ActiveRequestCount = 1, StartTimeTicks = startTimeTicks, SourcePath = sourcePath, - DeviceId = deviceId + DeviceId = deviceId, + CancellationTokenSource = cancellationTokenSource }); } } @@ -276,6 +278,11 @@ namespace MediaBrowser.Api { _activeTranscodingJobs.Remove(job); + if (!job.CancellationTokenSource.IsCancellationRequested) + { + job.CancellationTokenSource.Cancel(); + } + if (job.KillTimer != null) { job.KillTimer.Dispose(); @@ -329,7 +336,7 @@ namespace MediaBrowser.Api private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs) { - if (retryCount >= 10) + if (retryCount >= 8) { return; } @@ -432,6 +439,8 @@ namespace MediaBrowser.Api public long? StartTimeTicks { get; set; } public string SourcePath { get; set; } public string DeviceId { get; set; } + + public CancellationTokenSource CancellationTokenSource { get; set; } } /// <summary> diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs index 2a2d316d3..a410a093e 100644 --- a/MediaBrowser.Api/ChannelService.cs +++ b/MediaBrowser.Api/ChannelService.cs @@ -43,6 +43,11 @@ namespace MediaBrowser.Api public string Id { get; set; } } + [Route("/Channels/Features", "GET", Summary = "Gets features for a channel")] + public class GetAllChannelFeatures : IReturn<List<ChannelFeatures>> + { + } + [Route("/Channels/{Id}/Items", "GET", Summary = "Gets channel items")] public class GetChannelItems : IReturn<QueryResult<BaseItemDto>> { @@ -108,6 +113,13 @@ namespace MediaBrowser.Api _channelManager = channelManager; } + public object Get(GetAllChannelFeatures request) + { + var result = _channelManager.GetAllChannelFeatures().ToList(); + + return ToOptimizedResult(result); + } + public object Get(GetChannelFeatures request) { var result = _channelManager.GetChannelFeatures(request.Id); diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index 16f3f0c1d..d40762964 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/MediaBrowser.Api/Images/ImageByNameService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Dto; using ServiceStack; using System; using System.Collections.Generic; @@ -89,15 +90,6 @@ namespace MediaBrowser.Api.Images { } - public class ImageByNameInfo - { - public string Name { get; set; } - public string Theme { get; set; } - public string Context { get; set; } - public long FileLength { get; set; } - public string Format { get; set; } - } - /// <summary> /// Class ImageByNameService /// </summary> diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 62d5a6ce2..92bbb6130 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -105,7 +105,6 @@ <Compile Include="Playback\Hls\DynamicHlsService.cs" /> <Compile Include="Playback\Hls\HlsSegmentService.cs" /> <Compile Include="Playback\Hls\VideoHlsService.cs" /> - <Compile Include="Playback\ProgressiveStreamService.cs" /> <Compile Include="Playback\Progressive\AudioService.cs" /> <Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" /> <Compile Include="Playback\BaseStreamingService.cs" /> 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); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 553f02368..a7412e3d8 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Api.Playback.Hls /// </summary> public abstract class BaseHlsService : BaseStreamingService { - protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) + protected BaseHlsService(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) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient) { } @@ -82,7 +82,9 @@ namespace MediaBrowser.Api.Playback.Hls /// </exception> private async Task<object> ProcessRequestAsync(StreamRequest request) { - var state = GetState(request, CancellationToken.None).Result; + var cancellationTokenSource = new CancellationTokenSource(); + + var state = GetState(request, cancellationTokenSource.Token).Result; var playlist = GetOutputFilePath(state); @@ -92,7 +94,7 @@ namespace MediaBrowser.Api.Playback.Hls } else { - await FfmpegStartLock.WaitAsync().ConfigureAwait(false); + await FfmpegStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { if (File.Exists(playlist)) @@ -104,16 +106,16 @@ namespace MediaBrowser.Api.Playback.Hls // If the playlist doesn't already exist, startup ffmpeg try { - await StartFfMpeg(state, playlist).ConfigureAwait(false); + await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false); } catch { state.Dispose(); throw; } - } - await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false); + await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false); + } } finally { @@ -220,10 +222,12 @@ namespace MediaBrowser.Api.Playback.Hls return builder.ToString(); } - protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount) + protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken) { while (true) { + cancellationToken.ThrowIfCancellationRequested(); + string fileText; // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written @@ -240,7 +244,7 @@ namespace MediaBrowser.Api.Playback.Hls break; } - await Task.Delay(25).ConfigureAwait(false); + await Task.Delay(25, cancellationToken).ConfigureAwait(false); } } @@ -287,7 +291,7 @@ namespace MediaBrowser.Api.Playback.Hls // If performSubtitleConversions is true we're actually starting ffmpeg var startNumberParam = performSubtitleConversions ? GetStartNumber(state).ToString(UsCulture) : "0"; - var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} \"{10}\"", + var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", itsOffset, inputModifier, GetInputArgument(state), @@ -309,7 +313,7 @@ namespace MediaBrowser.Api.Playback.Hls var bitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? 64000; - var lowBitrateParams = string.Format(" -threads {0} -vn -codec:a:0 libmp3lame -ac 2 -ab {1} -hls_time {2} -start_number {3} -hls_list_size {4} \"{5}\"", + var lowBitrateParams = string.Format(" -threads {0} -vn -codec:a:0 libmp3lame -ac 2 -ab {1} -hls_time {2} -start_number {3} -hls_list_size {4} -y \"{5}\"", threads, bitrate / 2, state.SegmentLength.ToString(UsCulture), diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index c5405a3b5..96ecfb055 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; @@ -16,6 +17,7 @@ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; +using MimeTypes = ServiceStack.MimeTypes; namespace MediaBrowser.Api.Playback.Hls { @@ -60,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls public class DynamicHlsService : BaseHlsService { - public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) + public DynamicHlsService(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) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient) { } @@ -98,9 +100,9 @@ namespace MediaBrowser.Api.Playback.Hls if (!File.Exists(playlistPath)) { - await StartFfMpeg(state, playlistPath).ConfigureAwait(false); + await StartFfMpeg(state, playlistPath, new CancellationTokenSource()).ConfigureAwait(false); - await WaitForMinimumSegmentCount(playlistPath, GetSegmentWait()).ConfigureAwait(false); + await WaitForMinimumSegmentCount(playlistPath, GetSegmentWait(), CancellationToken.None).ConfigureAwait(false); } return GetSegementResult(path); @@ -283,7 +285,7 @@ namespace MediaBrowser.Api.Playback.Hls { if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue) { - args += GetOutputSizeParam(state, codec, performSubtitleConversion); + args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None); } } diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 162ab741a..e4ee68241 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -1,4 +1,6 @@ +using System.Threading; using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; @@ -54,7 +56,7 @@ namespace MediaBrowser.Api.Playback.Hls /// </summary> public class VideoHlsService : BaseHlsService { - public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) + public VideoHlsService(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) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient) { } @@ -155,7 +157,8 @@ namespace MediaBrowser.Api.Playback.Hls /// <param name="state">The state.</param> /// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param> /// <returns>System.String.</returns> - protected override string GetVideoArguments(StreamState state, bool performSubtitleConversion) + protected override string GetVideoArguments(StreamState state, + bool performSubtitleConversion) { var codec = GetVideoCodec(state.VideoRequest); @@ -178,7 +181,7 @@ namespace MediaBrowser.Api.Playback.Hls { if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue) { - args += GetOutputSizeParam(state, codec, performSubtitleConversion); + args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None); } } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 2e002cc9b..18cfbbdf2 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -44,7 +44,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// </summary> public class AudioService : BaseProgressiveStreamingService { - public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient) + public AudioService(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, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient, imageProcessor) { } @@ -105,7 +105,7 @@ namespace MediaBrowser.Api.Playback.Progressive var inputModifier = GetInputModifier(state); - return string.Format("{0} -i {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 \"{5}\"", + return string.Format("{0} -i {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 06e8f7b2c..5e3e85140 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -25,12 +25,10 @@ namespace MediaBrowser.Api.Playback.Progressive public abstract class BaseProgressiveStreamingService : BaseStreamingService { protected readonly IImageProcessor ImageProcessor; - protected readonly IHttpClient HttpClient; - protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) + protected BaseProgressiveStreamingService(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, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient) { ImageProcessor = imageProcessor; - HttpClient = httpClient; } /// <summary> @@ -280,7 +278,7 @@ namespace MediaBrowser.Api.Playback.Progressive if (!File.Exists(outputPath)) { - await StartFfMpeg(state, outputPath).ConfigureAwait(false); + await StartFfMpeg(state, outputPath, new CancellationTokenSource()).ConfigureAwait(false); } else { diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 58f36a06c..8ba504e14 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -32,6 +32,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// </summary> /// <param name="path">The path.</param> /// <param name="logger">The logger.</param> + /// <param name="fileSystem">The file system.</param> public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem) { Path = path; diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 29bc3f897..65a62aad3 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -1,3 +1,4 @@ +using System.Threading; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Channels; @@ -60,7 +61,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// </summary> public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient) + public VideoService(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, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, httpClient, imageProcessor) { } @@ -108,7 +109,7 @@ namespace MediaBrowser.Api.Playback.Progressive var inputModifier = GetInputModifier(state); - return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} \"{8}\"", + return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"", inputModifier, GetInputArgument(state), keyFrame, @@ -156,7 +157,7 @@ namespace MediaBrowser.Api.Playback.Progressive { if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue) { - args += GetOutputSizeParam(state, codec, performSubtitleConversion); + args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None); } } diff --git a/MediaBrowser.Api/Playback/ProgressiveStreamService.cs b/MediaBrowser.Api/Playback/ProgressiveStreamService.cs deleted file mode 100644 index 531f79a22..000000000 --- a/MediaBrowser.Api/Playback/ProgressiveStreamService.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Api.Playback.Progressive; - -namespace MediaBrowser.Api.Playback -{ - //public class GetProgressiveAudioStream : StreamRequest - //{ - - //} - - //public class ProgressiveStreamService : BaseApiService - //{ - // public object Get(GetProgressiveAudioStream request) - // { - // return ProcessRequest(request, false); - // } - - // /// <summary> - // /// Gets the specified request. - // /// </summary> - // /// <param name="request">The request.</param> - // /// <returns>System.Object.</returns> - // public object Head(GetProgressiveAudioStream request) - // { - // return ProcessRequest(request, true); - // } - - // protected object ProcessRequest(StreamRequest request, bool isHeadRequest) - // { - // var state = GetState(request, CancellationToken.None).Result; - - // var responseHeaders = new Dictionary<string, string>(); - - // if (request.Static && state.IsRemote) - // { - // AddDlnaHeaders(state, responseHeaders, true); - - // return GetStaticRemoteStreamResult(state.MediaPath, responseHeaders, isHeadRequest).Result; - // } - - // var outputPath = GetOutputFilePath(state); - // var outputPathExists = File.Exists(outputPath); - - // var isStatic = request.Static || - // (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive)); - - // AddDlnaHeaders(state, responseHeaders, isStatic); - - // if (request.Static) - // { - // var contentType = state.GetMimeType(state.MediaPath); - - // return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest); - // } - - // if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive)) - // { - // var contentType = state.GetMimeType(outputPath); - - // return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest); - // } - - // return GetStreamResult(state, responseHeaders, isHeadRequest).Result; - // } - - //} -} diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index f9e861e8d..295ee44e7 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -31,8 +31,11 @@ namespace MediaBrowser.Api.Playback public StreamState() { PlayableStreamFileNames = new List<string>(); + RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } + public Dictionary<string, string> RemoteHttpHeaders { get; set; } + /// <summary> /// Gets or sets the log file stream. /// </summary> @@ -40,7 +43,7 @@ namespace MediaBrowser.Api.Playback public Stream LogFileStream { get; set; } public string InputContainer { get; set; } - + public MediaStream AudioStream { get; set; } public MediaStream VideoStream { get; set; } public MediaStream SubtitleStream { get; set; } @@ -277,8 +280,8 @@ namespace MediaBrowser.Api.Playback { get { - var defaultValue = string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ? - TransportStreamTimestamp.Valid : + var defaultValue = string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ? + TransportStreamTimestamp.Valid : TransportStreamTimestamp.None; return !Request.Static |
