diff options
Diffstat (limited to 'MediaBrowser.Api/Playback')
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 63 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 42 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 13 |
3 files changed, 64 insertions, 54 deletions
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 57c2244c7a..fa78fa0205 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -59,10 +59,11 @@ namespace MediaBrowser.Api.Playback.Hls /// Processes the request. /// </summary> /// <param name="request">The request.</param> + /// <param name="isLive">if set to <c>true</c> [is live].</param> /// <returns>System.Object.</returns> - protected object ProcessRequest(StreamRequest request) + protected object ProcessRequest(StreamRequest request, bool isLive) { - return ProcessRequestAsync(request).Result; + return ProcessRequestAsync(request, isLive).Result; } private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1); @@ -70,13 +71,12 @@ namespace MediaBrowser.Api.Playback.Hls /// Processes the request async. /// </summary> /// <param name="request">The request.</param> + /// <param name="isLive">if set to <c>true</c> [is live].</param> /// <returns>Task{System.Object}.</returns> - /// <exception cref="ArgumentException"> - /// A video bitrate is required + /// <exception cref="ArgumentException">A video bitrate is required /// or - /// An audio bitrate is required - /// </exception> - private async Task<object> ProcessRequestAsync(StreamRequest request) + /// An audio bitrate is required</exception> + private async Task<object> ProcessRequestAsync(StreamRequest request, bool isLive) { var cancellationTokenSource = new CancellationTokenSource(); @@ -110,7 +110,8 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false); + var waitCount = isLive ? 1 : GetSegmentWait(); + await WaitForMinimumSegmentCount(playlist, waitCount, cancellationTokenSource.Token).ConfigureAwait(false); } } finally @@ -119,6 +120,22 @@ namespace MediaBrowser.Api.Playback.Hls } } + if (isLive) + { + //var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); + + //file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file); + + try + { + return ResultFactory.GetStaticFileResult(Request, playlist, FileShare.ReadWrite); + } + finally + { + ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls); + } + } + var audioBitrate = state.OutputAudioBitrate ?? 0; var videoBitrate = state.OutputVideoBitrate ?? 0; @@ -188,16 +205,18 @@ namespace MediaBrowser.Api.Playback.Hls protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken) { - var count = 0; + Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist); - // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written - using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + while (true) { - using (var reader = new StreamReader(fileStream)) + // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written + using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { - while (true) + using (var reader = new StreamReader(fileStream)) { - if (!reader.EndOfStream) + var count = 0; + + while (!reader.EndOfStream) { var line = await reader.ReadLineAsync().ConfigureAwait(false); @@ -206,11 +225,12 @@ namespace MediaBrowser.Api.Playback.Hls count++; if (count >= segmentCount) { + Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist); return; } } } - await Task.Delay(25, cancellationToken).ConfigureAwait(false); + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } } } @@ -229,7 +249,7 @@ namespace MediaBrowser.Api.Playback.Hls var itsOffsetMs = hlsVideoRequest == null ? 0 - : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs; + : hlsVideoRequest.TimeStampOffsetMs; var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture)); @@ -240,7 +260,15 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? 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} -y \"{10}\"", + var baseUrlParam = string.Empty; + + if (state.Request is GetLiveHlsStream) + { + baseUrlParam = string.Format(" -hls_base_url \"{0}/\"", + "hls/" + Path.GetFileNameWithoutExtension(outputPath)); + } + + 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} -y \"{11}\"", itsOffset, inputModifier, GetInputArgument(state), @@ -251,6 +279,7 @@ namespace MediaBrowser.Api.Playback.Hls state.SegmentLength.ToString(UsCulture), startNumberParam, state.HlsListSize.ToString(UsCulture), + baseUrlParam, outputPath ).Trim(); diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index bde2c56943..5bb6106860 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -22,11 +22,6 @@ namespace MediaBrowser.Api.Playback.Hls [Api(Description = "Gets a video stream using HTTP live streaming.")] public class GetMasterHlsVideoStream : VideoStreamRequest { - [ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? BaselineStreamAudioBitRate { get; set; } - - [ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool AppendBaselineStream { get; set; } } [Route("/Videos/{Id}/main.m3u8", "GET")] @@ -35,12 +30,6 @@ namespace MediaBrowser.Api.Playback.Hls { } - [Route("/Videos/{Id}/baseline.m3u8", "GET")] - [Api(Description = "Gets a video stream using HTTP live streaming.")] - public class GetBaselineHlsVideoStream : VideoStreamRequest - { - } - /// <summary> /// Class GetHlsVideoSegment /// </summary> @@ -73,16 +62,11 @@ namespace MediaBrowser.Api.Playback.Hls public object Get(GetDynamicHlsVideoSegment request) { - if (string.Equals("baseline", request.PlaylistId, StringComparison.OrdinalIgnoreCase)) - { - return GetDynamicSegment(request, false).Result; - } - - return GetDynamicSegment(request, true).Result; + return GetDynamicSegment(request).Result; } private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1); - private async Task<object> GetDynamicSegment(GetDynamicHlsVideoSegment request, bool isMain) + private async Task<object> GetDynamicSegment(GetDynamicHlsVideoSegment request) { if ((request.StartTimeTicks ?? 0) > 0) { @@ -322,7 +306,9 @@ namespace MediaBrowser.Api.Playback.Hls var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); // Main stream - var playlistUrl = "main.m3u8" + queryString; + var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8"; + playlistUrl += queryString; + AppendPlaylist(builder, playlistUrl, totalBitrate); if (state.VideoRequest.VideoBitRate.HasValue) @@ -385,13 +371,6 @@ namespace MediaBrowser.Api.Playback.Hls return result; } - public object Get(GetBaselineHlsVideoStream request) - { - var result = GetPlaylistAsync(request, "baseline").Result; - - return result; - } - private async Task<object> GetPlaylistAsync(VideoStreamRequest request, string name) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); @@ -506,14 +485,6 @@ namespace MediaBrowser.Api.Playback.Hls /// <returns>System.String.</returns> protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { - var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; - - var itsOffsetMs = hlsVideoRequest == null - ? 0 - : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs; - - var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture)); - var threads = GetNumberOfThreads(state, false); var inputModifier = GetInputModifier(state); @@ -521,8 +492,7 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -flags -global_header {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", - itsOffset, + var args = string.Format("{0} -i {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 1a925378bb..28c0219fce 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -32,6 +32,12 @@ namespace MediaBrowser.Api.Playback.Hls public int TimeStampOffsetMs { get; set; } } + [Route("/Videos/{Id}/live.m3u8", "GET")] + [Api(Description = "Gets a video stream using HTTP live streaming.")] + public class GetLiveHlsStream : VideoStreamRequest + { + } + /// <summary> /// Class GetHlsVideoSegment /// </summary> @@ -105,7 +111,12 @@ namespace MediaBrowser.Api.Playback.Hls /// <returns>System.Object.</returns> public object Get(GetHlsVideoStream request) { - return ProcessRequest(request); + return ProcessRequest(request, false); + } + + public object Get(GetLiveHlsStream request) + { + return ProcessRequest(request, true); } /// <summary> |
