public ActionResult <StreamResponse> PostStream(StreamRequest streamRequest)
 {
     _logger.LogTrace($"{Request.HttpContext.Connection.RemoteIpAddress}: POST {Request.Host}{Request.Path}");
     _authHandler.CheckToken(streamRequest.Token);
     return(_serverManager.GetStream(streamRequest.StreamId, streamRequest.Settings.VideoPresetId,
                                     streamRequest.Settings.AudioPresetId));
 }
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
        /// <returns>Task.</returns>
        protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
        {
            var cancellationTokenSource = new CancellationTokenSource();

            var state = GetState(request, cancellationTokenSource.Token).Result;

            var responseHeaders = new Dictionary<string, string>();

            // Static remote stream
            if (request.Static && state.InputProtocol == MediaProtocol.Http)
            {
                AddDlnaHeaders(state, responseHeaders, true);

                using (state)
                {
                    return GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).Result;
                }
            }

            if (request.Static && state.InputProtocol != MediaProtocol.File)
            {
                throw new ArgumentException(string.Format("Input protocol {0} cannot be streamed statically.", state.InputProtocol));
            }

            var outputPath = state.OutputFilePath;
            var outputPathExists = File.Exists(outputPath);

            var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);

            AddDlnaHeaders(state, responseHeaders, request.Static || isTranscodeCached);

            // Static stream
            if (request.Static)
            {
                var contentType = state.GetMimeType(state.MediaPath);

                using (state)
                {
                    var job = string.IsNullOrEmpty(request.TranscodingJobId) ?
                        null :
                        ApiEntryPoint.Instance.GetTranscodingJob(request.TranscodingJobId);

                    var limits = new List<long>();
                    if (state.InputBitrate.HasValue)
                    {
                        // Bytes per second
                        limits.Add((state.InputBitrate.Value / 8));
                    }
                    if (state.InputFileSize.HasValue && state.RunTimeTicks.HasValue)
                    {
                        var totalSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds;

                        if (totalSeconds > 1)
                        {
                            var timeBasedLimit = state.InputFileSize.Value / totalSeconds;
                            limits.Add(Convert.ToInt64(timeBasedLimit));
                        }
                    }

                    // Take the greater of the above to methods, just to be safe
                    var throttleLimit = limits.Count > 0 ? limits.First() : 0;

                    // Pad to play it safe
                    var bytesPerSecond = Convert.ToInt64(1.05 * throttleLimit);

                    // Don't even start evaluating this until at least two minutes have content have been consumed
                    var targetGap = throttleLimit * 120;

                    return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
                    {
                        ResponseHeaders = responseHeaders,
                        ContentType = contentType,
                        IsHeadRequest = isHeadRequest,
                        Path = state.MediaPath,
                        Throttle = request.Throttle,

                        ThrottleLimit = bytesPerSecond,

                        MinThrottlePosition = targetGap,

                        ThrottleCallback = (l1, l2) => ThrottleCallack(l1, l2, bytesPerSecond, job)
                    });
                }
            }

            // Not static but transcode cache file exists
            if (isTranscodeCached)
            {
                var contentType = state.GetMimeType(outputPath);

                try
                {
                    return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
                    {
                        ResponseHeaders = responseHeaders,
                        ContentType = contentType,
                        IsHeadRequest = isHeadRequest,
                        Path = outputPath
                    });
                }
                finally
                {
                    state.Dispose();
                }
            }

            // Need to start ffmpeg
            try
            {
                return GetStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).Result;
            }
            catch
            {
                state.Dispose();

                throw;
            }
        }
Example #3
0
        /// <summary>
        /// Processes the request async.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Task{System.Object}.</returns>
        /// <exception cref="ArgumentException">
        /// A video bitrate is required
        /// or
        /// An audio bitrate is required
        /// </exception>
        private async Task<object> ProcessRequestAsync(StreamRequest request)
        {
            var state = GetState(request, CancellationToken.None).Result;

            if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy))
            {
                throw new ArgumentException("A video bitrate is required");
            }
            if (!state.Request.AudioBitRate.HasValue && (!state.Request.AudioCodec.HasValue || state.Request.AudioCodec.Value != AudioCodecs.Copy))
            {
                throw new ArgumentException("An audio bitrate is required");
            }

            var playlist = GetOutputFilePath(state);
            var isPlaylistNewlyCreated = false;

            // If the playlist doesn't already exist, startup ffmpeg
            if (!File.Exists(playlist))
            {
                isPlaylistNewlyCreated = true;
                await StartFfMpeg(state, playlist).ConfigureAwait(false);
            }
            else
            {
                ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
            }

            if (isPlaylistNewlyCreated)
            {
                await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false);
            }

            int audioBitrate;
            int videoBitrate;
            GetPlaylistBitrates(state, out audioBitrate, out videoBitrate);

            var appendBaselineStream = false;
            var baselineStreamBitrate = 64000;

            var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
            if (hlsVideoRequest != null)
            {
                appendBaselineStream = hlsVideoRequest.AppendBaselineStream;
                baselineStreamBitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? baselineStreamBitrate;
            }

            var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);

            try
            {
                return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
            }
            finally
            {
                ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
            }
        }
Example #4
0
 /// <summary>
 /// Processes the request.
 /// </summary>
 /// <param name="request">The request.</param>
 /// <returns>System.Object.</returns>
 protected object ProcessRequest(StreamRequest request)
 {
     return ProcessRequestAsync(request).Result;
 }
Example #5
0
 /// <summary>
 /// 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 async Task<object> ProcessRequest(StreamRequest request, bool isLive)
 {
     return await ProcessRequestAsync(request, isLive).ConfigureAwait(false);
 }
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
        /// <returns>Task.</returns>
        protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
        {
            var cancellationTokenSource = new CancellationTokenSource();

            var state = GetState(request, cancellationTokenSource.Token).Result;

            var responseHeaders = new Dictionary<string, string>();

            // Static remote stream
            if (request.Static && state.InputProtocol == MediaProtocol.Http)
            {
                AddDlnaHeaders(state, responseHeaders, true);

                using (state)
                {
                    return GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).Result;
                }
            }

            if (request.Static && state.InputProtocol != MediaProtocol.File)
            {
                throw new ArgumentException(string.Format("Input protocol {0} cannot be streamed statically.", state.InputProtocol));
            }

            var outputPath = state.OutputFilePath;
            var outputPathExists = File.Exists(outputPath);

            var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);

            AddDlnaHeaders(state, responseHeaders, request.Static || isTranscodeCached);

            // Static stream
            if (request.Static)
            {
                var contentType = state.GetMimeType(state.MediaPath);

                using (state)
                {
                    return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
                    {
                        ResponseHeaders = responseHeaders,
                        ContentType = contentType,
                        IsHeadRequest = isHeadRequest,
                        Path = state.MediaPath
                    });
                }
            }

            // Not static but transcode cache file exists
            if (isTranscodeCached)
            {
                var contentType = state.GetMimeType(outputPath);

                try
                {
                    return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
                    {
                        ResponseHeaders = responseHeaders,
                        ContentType = contentType,
                        IsHeadRequest = isHeadRequest,
                        Path = outputPath
                    });
                }
                finally
                {
                    state.Dispose();
                }
            }

            // Need to start ffmpeg
            try
            {
                return GetStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).Result;
            }
            catch
            {
                state.Dispose();

                throw;
            }
        }
Example #7
0
        private async Task<object> GetMasterPlaylistInternal(StreamRequest request, string method)
        {
            var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);

            if (string.IsNullOrEmpty(request.MediaSourceId))
            {
                throw new ArgumentException("MediaSourceId is required");
            }

            var playlistText = string.Empty;

            if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
            {
                var audioBitrate = state.OutputAudioBitrate ?? 0;
                var videoBitrate = state.OutputVideoBitrate ?? 0;

                playlistText = GetMasterPlaylistFileText(state, videoBitrate + audioBitrate);
            }

            return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
        }
        /// <summary>
        /// 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
        /// or
        /// An audio bitrate is required</exception>
        private async Task<object> ProcessRequestAsync(StreamRequest request, bool isLive)
        {
            var cancellationTokenSource = new CancellationTokenSource();

            var state = await GetState(request, cancellationTokenSource.Token).ConfigureAwait(false);

            if (isLive)
            {
                state.Request.StartTimeTicks = null;
            }

            var playlist = state.OutputFilePath;

            if (File.Exists(playlist))
            {
                ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
            }
            else
            {
                await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
                try
                {
                    if (File.Exists(playlist))
                    {
                        ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
                    }
                    else
                    {
                        // If the playlist doesn't already exist, startup ffmpeg
                        try
                        {
                            await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
                        }
                        catch
                        {
                            state.Dispose();
                            throw;
                        }

                        var waitCount = isLive ? 1 : GetSegmentWait();
                        await WaitForMinimumSegmentCount(playlist, waitCount, cancellationTokenSource.Token).ConfigureAwait(false);
                    }
                }
                finally
                {
                    ApiEntryPoint.Instance.TranscodingStartLock.Release();
                }
            }

            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;

            var appendBaselineStream = false;
            var baselineStreamBitrate = 64000;

            var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
            if (hlsVideoRequest != null)
            {
                appendBaselineStream = hlsVideoRequest.AppendBaselineStream;
                baselineStreamBitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? baselineStreamBitrate;
            }

            var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);

            try
            {
                return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
            }
            finally
            {
                ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
            }
        }
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
        /// <returns>Task.</returns>
        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)
            {
                return ResultFactory.GetStaticFileResult(Request, state.MediaPath, FileShare.Read, responseHeaders, isHeadRequest);
            }

            if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
            {
                return ResultFactory.GetStaticFileResult(Request, outputPath, FileShare.Read, responseHeaders, isHeadRequest);
            }

            return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
        }
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
        /// <returns>Task.</returns>
        protected async Task<object> ProcessRequest(StreamRequest request, bool isHeadRequest)
        {
            var cancellationTokenSource = new CancellationTokenSource();

            var state = await GetState(request, cancellationTokenSource.Token).ConfigureAwait(false);

            var responseHeaders = new Dictionary<string, string>();

            if (request.Static && state.DirectStreamProvider != null)
            {
                AddDlnaHeaders(state, responseHeaders, true);

                using (state)
                {
                    var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

                    // TODO: Don't hardcode this
                    outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts");

                    var streamSource = new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
                    {
                        AllowEndOfFile = false
                    };
                    return ResultFactory.GetAsyncStreamWriter(streamSource);
                }
            }

            // Static remote stream
            if (request.Static && state.InputProtocol == MediaProtocol.Http)
            {
                AddDlnaHeaders(state, responseHeaders, true);

                using (state)
                {
                    return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
                }
            }

            if (request.Static && state.InputProtocol != MediaProtocol.File)
            {
                throw new ArgumentException(string.Format("Input protocol {0} cannot be streamed statically.", state.InputProtocol));
            }

            var outputPath = state.OutputFilePath;
            var outputPathExists = FileSystem.FileExists(outputPath);

            var transcodingJob = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
            var isTranscodeCached = outputPathExists && transcodingJob != null;

            AddDlnaHeaders(state, responseHeaders, request.Static || isTranscodeCached);

            // Static stream
            if (request.Static)
            {
                var contentType = state.GetMimeType(state.MediaPath);

                using (state)
                {
                    if (state.MediaSource.IsInfiniteStream)
                    {
                        var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

                        outputHeaders["Content-Type"] = contentType;

                        var streamSource = new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None)
                        {
                            AllowEndOfFile = false
                        };
                        return ResultFactory.GetAsyncStreamWriter(streamSource);
                    }

                    TimeSpan? cacheDuration = null;

                    if (!string.IsNullOrEmpty(request.Tag))
                    {
                        cacheDuration = TimeSpan.FromDays(365);
                    }

                    return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
                    {
                        ResponseHeaders = responseHeaders,
                        ContentType = contentType,
                        IsHeadRequest = isHeadRequest,
                        Path = state.MediaPath,
                        CacheDuration = cacheDuration

                    }).ConfigureAwait(false);
                }
            }

            //// Not static but transcode cache file exists
            //if (isTranscodeCached && state.VideoRequest == null)
            //{
            //    var contentType = state.GetMimeType(outputPath);

            //    try
            //    {
            //        if (transcodingJob != null)
            //        {
            //            ApiEntryPoint.Instance.OnTranscodeBeginRequest(transcodingJob);
            //        }

            //        return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
            //        {
            //            ResponseHeaders = responseHeaders,
            //            ContentType = contentType,
            //            IsHeadRequest = isHeadRequest,
            //            Path = outputPath,
            //            FileShare = FileShare.ReadWrite,
            //            OnComplete = () =>
            //            {
            //                if (transcodingJob != null)
            //                {
            //                    ApiEntryPoint.Instance.OnTranscodeEndRequest(transcodingJob);
            //                }
            //            }

            //        }).ConfigureAwait(false);
            //    }
            //    finally
            //    {
            //        state.Dispose();
            //    }
            //}

            // Need to start ffmpeg
            try
            {
                return await GetStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
            }
            catch
            {
                state.Dispose();

                throw;
            }
        }
 public StreamRequestHandler(IContainerManager containerManager, IJobManager jobManager, Request request)
     : base(containerManager, jobManager, request)
 {
     this.request = (StreamRequest)request;
 }
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
        /// <returns>Task.</returns>
        protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
        {
            var state = GetState(request);

            if (request.AlbumArt)
            {
                return GetAlbumArtResponse(state);
            }

            var responseHeaders = new Dictionary<string, string>();

            if (request.Static && state.Item.LocationType == LocationType.Remote)
            {
                return GetStaticRemoteStreamResult(state.Item, 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)
            {
                return ResultFactory.GetStaticFileResult(RequestContext, state.Item.Path, FileShare.Read, responseHeaders, isHeadRequest);
            }

            if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
            {
                return ResultFactory.GetStaticFileResult(RequestContext, outputPath, FileShare.Read, responseHeaders, isHeadRequest);
            }

            return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
        }
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>System.Object.</returns>
        protected object ProcessRequest(StreamRequest request)
        {
            var state = GetState(request, CancellationToken.None).Result;

            return ProcessRequestAsync(state).Result;
        }
Example #14
0
        /// <summary>
        /// Add a stream to the server
        /// </summary>
        internal uint AddStream(IClient client, Request request)
        {
            var streamClient = streamServer.Clients.Single (c => c.Guid == client.Guid);

            // Check for an existing stream for the request
            var procedure = KRPC.Service.Services.Instance.GetProcedureSignature (request);
            var arguments = KRPC.Service.Services.Instance.DecodeArguments (procedure, request);
            foreach (var streamRequest in streamRequests[streamClient]) {
                if (streamRequest.Procedure == procedure && streamRequest.Arguments.SequenceEqual (arguments))
                    return streamRequest.Identifier;
            }

            // Create a new stream
            {
                var streamRequest = new StreamRequest (request);
                streamRequests [streamClient].Add (streamRequest);
                return streamRequest.Identifier;
            }
        }
Example #15
0
 /// <summary>
 /// 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, bool isLive)
 {
     return ProcessRequestAsync(request, isLive).Result;
 }
Example #16
0
        private async Task<object> GetVariantPlaylistInternal(StreamRequest request, bool isOutputVideo, string name)
        {
            var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);

            var builder = new StringBuilder();

            builder.AppendLine("#EXTM3U");
            builder.AppendLine("#EXT-X-VERSION:3");
            builder.AppendLine("#EXT-X-TARGETDURATION:" + (state.SegmentLength).ToString(UsCulture));
            builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");

            var queryStringIndex = Request.RawUrl.IndexOf('?');
            var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);

            var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;

            var index = 0;

            while (seconds > 0)
            {
                var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds;

                builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ",");

                builder.AppendLine(string.Format("hlsdynamic/{0}/{1}{2}{3}",

                    name,
                    index.ToString(UsCulture),
                    GetSegmentFileExtension(isOutputVideo),
                    queryString));

                seconds -= state.SegmentLength;
                index++;
            }

            builder.AppendLine("#EXT-X-ENDLIST");

            var playlistText = builder.ToString();

            return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
        }
Example #17
0
        /// <summary>
        /// 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
        /// or
        /// An audio bitrate is required</exception>
        private async Task<object> ProcessRequestAsync(StreamRequest request, bool isLive)
        {
            var cancellationTokenSource = new CancellationTokenSource();

            var state = await GetState(request, cancellationTokenSource.Token).ConfigureAwait(false);

            if (isLive)
            {
                state.Request.StartTimeTicks = null;
            }

            TranscodingJob job = null;
            var playlist = state.OutputFilePath;

            if (!FileSystem.FileExists(playlist))
            {
                await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
                try
                {
                    if (!FileSystem.FileExists(playlist))
                    {
                        // If the playlist doesn't already exist, startup ffmpeg
                        try
                        {
                            job = await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
                            job.IsLiveOutput = isLive;
                        }
                        catch
                        {
                            state.Dispose();
                            throw;
                        }

                        await WaitForMinimumSegmentCount(playlist, 3, cancellationTokenSource.Token).ConfigureAwait(false);
                    }
                }
                finally
                {
                    ApiEntryPoint.Instance.TranscodingStartLock.Release();
                }
            }

            if (isLive)
            {
                job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);

                if (job != null)
                {
                    ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
                }
                return ResultFactory.GetResult(GetLivePlaylistText(playlist, state.SegmentLength), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
            }

            var audioBitrate = state.OutputAudioBitrate ?? 0;
            var videoBitrate = state.OutputVideoBitrate ?? 0;

            var appendBaselineStream = false;
            var baselineStreamBitrate = 64000;

            var hlsVideoRequest = state.VideoRequest as GetHlsVideoStreamLegacy;
            if (hlsVideoRequest != null)
            {
                appendBaselineStream = hlsVideoRequest.AppendBaselineStream;
                baselineStreamBitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? baselineStreamBitrate;
            }

            var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);

            job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);

            if (job != null)
            {
                ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
            }

            return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
        }
Example #18
0
 /// <summary>
 /// Processes the request.
 /// </summary>
 /// <param name="request">The request.</param>
 /// <returns>System.Object.</returns>
 protected object ProcessRequest(StreamRequest request)
 {
     var state = GetState(request);
     
     return ProcessRequestAsync(state).Result;
 }
Example #19
0
        private async Task<object> GetDynamicSegment(StreamRequest request, string segmentId)
        {
            if ((request.StartTimeTicks ?? 0) > 0)
            {
                throw new ArgumentException("StartTimeTicks is not allowed.");
            }

            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken = cancellationTokenSource.Token;

            var requestedIndex = int.Parse(segmentId, NumberStyles.Integer, UsCulture);

            var state = await GetState(request, cancellationToken).ConfigureAwait(false);

            var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");

            var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex);

            var segmentExtension = GetSegmentFileExtension(state);

            TranscodingJob job = null;

            if (FileSystem.FileExists(segmentPath))
            {
                job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
                return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
            }

            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
            var released = false;
            try
            {
                if (FileSystem.FileExists(segmentPath))
                {
                    job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
                    ApiEntryPoint.Instance.TranscodingStartLock.Release();
                    released = true;
                    return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    var startTranscoding = false;

                    var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
                    var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;

                    if (currentTranscodingIndex == null)
                    {
                        Logger.Debug("Starting transcoding because currentTranscodingIndex=null");
                        startTranscoding = true;
                    }
                    else if (requestedIndex < currentTranscodingIndex.Value)
                    {
                        Logger.Debug("Starting transcoding because requestedIndex={0} and currentTranscodingIndex={1}", requestedIndex, currentTranscodingIndex);
                        startTranscoding = true;
                    }
                    else if ((requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
                    {
                        Logger.Debug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", (requestedIndex - currentTranscodingIndex.Value), segmentGapRequiringTranscodingChange, requestedIndex);
                        startTranscoding = true;
                    }
                    if (startTranscoding)
                    {
                        // If the playlist doesn't already exist, startup ffmpeg
                        try
                        {
                            ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false);

                            if (currentTranscodingIndex.HasValue)
                            {
                                DeleteLastFile(playlistPath, segmentExtension, 0);
                            }

                            request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex);

                            job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
                        }
                        catch
                        {
                            state.Dispose();
                            throw;
                        }

                        //await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
                    }
                    else
                    {
                        job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
                        if (job.TranscodingThrottler != null)
                        {
                            job.TranscodingThrottler.UnpauseTranscoding();
                        }
                    }
                }
            }
            finally
            {
                if (!released)
                {
                    ApiEntryPoint.Instance.TranscodingStartLock.Release();
                }
            }

            //Logger.Info("waiting for {0}", segmentPath);
            //while (!File.Exists(segmentPath))
            //{
            //    await Task.Delay(50, cancellationToken).ConfigureAwait(false);
            //}

            Logger.Info("returning {0}", segmentPath);
            job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
            return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
        }
Example #20
0
        /// <summary>
        /// Processes the request async.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Task{System.Object}.</returns>
        /// <exception cref="ArgumentException">
        /// A video bitrate is required
        /// or
        /// An audio bitrate is required
        /// </exception>
        private async Task<object> ProcessRequestAsync(StreamRequest request)
        {
            var state = GetState(request, CancellationToken.None).Result;

            if (!state.VideoRequest.VideoBitRate.HasValue && (string.IsNullOrEmpty(state.VideoRequest.VideoCodec) || !string.Equals(state.VideoRequest.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase)))
            {
                state.Dispose();
                throw new ArgumentException("A video bitrate is required");
            }
            if (!state.Request.AudioBitRate.HasValue && (string.IsNullOrEmpty(state.Request.AudioCodec) || !string.Equals(state.Request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase)))
            {
                state.Dispose();
                throw new ArgumentException("An audio bitrate is required");
            }

            var playlist = GetOutputFilePath(state);

            if (File.Exists(playlist))
            {
                ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
            }
            else
            {
                await FfmpegStartLock.WaitAsync().ConfigureAwait(false);
                try
                {
                    if (File.Exists(playlist))
                    {
                        ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
                    }
                    else
                    {
                        // If the playlist doesn't already exist, startup ffmpeg
                        try
                        {
                            await StartFfMpeg(state, playlist).ConfigureAwait(false);
                        }
                        catch
                        {
                            state.Dispose();
                            throw;
                        }
                    }

                    await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false);
                }
                finally
                {
                    FfmpegStartLock.Release();
                }
            }

            int audioBitrate;
            int videoBitrate;
            GetPlaylistBitrates(state, out audioBitrate, out videoBitrate);

            var appendBaselineStream = false;
            var baselineStreamBitrate = 64000;

            var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
            if (hlsVideoRequest != null)
            {
                appendBaselineStream = hlsVideoRequest.AppendBaselineStream;
                baselineStreamBitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? baselineStreamBitrate;
            }

            var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);

            try
            {
                return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
            }
            finally
            {
                ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
            }
        }
Example #21
0
        private async Task<object> GetVariantPlaylistInternal(StreamRequest request, bool isOutputVideo, string name)
        {
            var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);

            var segmentLengths = GetSegmentLengths(state);

            var builder = new StringBuilder();

            builder.AppendLine("#EXTM3U");
            builder.AppendLine("#EXT-X-VERSION:3");
            builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling((segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)).ToString(UsCulture));
            builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");

            var queryStringIndex = Request.RawUrl.IndexOf('?');
            var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);

            var index = 0;

            foreach (var length in segmentLengths)
            {
                builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ",");

                builder.AppendLine(string.Format("hls1/{0}/{1}{2}{3}",

                    name,
                    index.ToString(UsCulture),
                    GetSegmentFileExtension(isOutputVideo),
                    queryString));

                index++;
            }

            builder.AppendLine("#EXT-X-ENDLIST");

            var playlistText = builder.ToString();

            return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
        }
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
        /// <returns>Task.</returns>
        protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
        {
            var state = GetState(request, CancellationToken.None).Result;

            var responseHeaders = new Dictionary<string, string>();

            // Static remote stream
            if (request.Static && state.IsRemote)
            {
                AddDlnaHeaders(state, responseHeaders, true);

                try
                {
                    return GetStaticRemoteStreamResult(state.MediaPath, responseHeaders, isHeadRequest).Result;
                }
                finally
                {
                    state.Dispose();
                }
            }

            var outputPath = GetOutputFilePath(state);
            var outputPathExists = File.Exists(outputPath);

            var isStatic = request.Static ||
                           (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive));

            AddDlnaHeaders(state, responseHeaders, isStatic);

            // Static stream
            if (request.Static)
            {
                var contentType = state.GetMimeType(state.MediaPath);

                try
                {
                    return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
                }
                finally
                {
                    state.Dispose();
                }
            }

            // Not static but transcode cache file exists
            if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
            {
                var contentType = state.GetMimeType(outputPath);

                try
                {
                    return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
                }
                finally
                {
                    state.Dispose();
                }
            }

            // Need to start ffmpeg
            try
            {
                return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
            }
            catch
            {
                state.Dispose();

                throw;
            }
        }