/// <summary> /// Gets the stream result. /// </summary> /// <param name="state">The state.</param> /// <param name="responseHeaders">The response headers.</param> /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <returns>Task{System.Object}.</returns> private async Task <object> GetStreamResult(StreamState state, IDictionary <string, string> responseHeaders, bool isHeadRequest) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; responseHeaders["Accept-Ranges"] = "none"; var contentType = state.GetMimeType(outputPath); var contentLength = state.EstimateContentLength ? GetEstimatedContentLength(state) : null; if (contentLength.HasValue) { responseHeaders["Content-Length"] = contentLength.Value.ToString(UsCulture); } // Headers only if (isHeadRequest) { var streamResult = ResultFactory.GetResult(new byte[] { }, contentType, responseHeaders); if (!contentLength.HasValue) { var hasOptions = streamResult as IHasOptions; if (hasOptions != null) { if (hasOptions.Options.ContainsKey("Content-Length")) { hasOptions.Options.Remove("Content-Length"); } } } return(streamResult); } if (!File.Exists(outputPath)) { await StartFfMpeg(state, outputPath, new CancellationTokenSource()).ConfigureAwait(false); } else { ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive); var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job); result.Options["Content-Type"] = contentType; // Add the response headers to the result object foreach (var item in responseHeaders) { result.Options[item.Key] = item.Value; } return(result); }
/// <summary> /// Gets the stream result. /// </summary> /// <param name="state">The state.</param> /// <param name="responseHeaders">The response headers.</param> /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <param name="cancellationTokenSource">The cancellation token source.</param> /// <returns>Task{System.Object}.</returns> private async Task <object> GetStreamResult(StreamRequest request, StreamState state, IDictionary <string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; responseHeaders[HeaderNames.AcceptRanges] = "none"; var contentType = state.GetMimeType(outputPath); // TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response // Headers only if (isHeadRequest) { return(ResultFactory.GetResult(null, Array.Empty <byte>(), contentType, responseHeaders)); } var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath); await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { TranscodingJob job; if (!File.Exists(outputPath)) { job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false); } else { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } var outputHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { [HeaderNames.ContentType] = contentType }; // Add the response headers to the result object foreach (var item in responseHeaders) { outputHeaders[item.Key] = item.Value; } return(new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, CancellationToken.None)); } finally { transcodingLock.Release(); } }
/// <summary> /// Returns a transcoded file from the server. /// </summary> /// <param name="state">The current <see cref="StreamState"/>.</param> /// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param> /// <param name="controller">The <see cref="ControllerBase"/> managing the response.</param> /// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper"/> singleton.</param> /// <param name="ffmpegCommandLineArguments">The command line arguments to start ffmpeg.</param> /// <param name="request">The <see cref="HttpRequest"/> starting the transcoding.</param> /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param> /// <param name="cancellationTokenSource">The <see cref="CancellationTokenSource"/>.</param> /// <returns>A <see cref="Task{ActionResult}"/> containing the transcoded file.</returns> public static async Task <ActionResult> GetTranscodedFile( StreamState state, bool isHeadRequest, ControllerBase controller, TranscodingJobHelper transcodingJobHelper, string ffmpegCommandLineArguments, HttpRequest request, TranscodingJobType transcodingJobType, CancellationTokenSource cancellationTokenSource) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; controller.Response.Headers[HeaderNames.AcceptRanges] = "none"; var contentType = state.GetMimeType(outputPath); // Headers only if (isHeadRequest) { return(controller.File(Array.Empty <byte>(), contentType)); } var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath); await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { TranscodingJobDto?job; if (!File.Exists(outputPath)) { job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); } else { job = transcodingJobHelper.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } var memoryStream = new MemoryStream(); await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false); memoryStream.Position = 0; return(controller.File(memoryStream, contentType)); } finally { transcodingLock.Release(); } }
/// <summary> /// Returns a transcoded file from the server. /// </summary> /// <param name="state">The current <see cref="StreamState"/>.</param> /// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param> /// <param name="httpContext">The current http context.</param> /// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper"/> singleton.</param> /// <param name="ffmpegCommandLineArguments">The command line arguments to start ffmpeg.</param> /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param> /// <param name="cancellationTokenSource">The <see cref="CancellationTokenSource"/>.</param> /// <returns>A <see cref="Task{ActionResult}"/> containing the transcoded file.</returns> public static async Task <ActionResult> GetTranscodedFile( StreamState state, bool isHeadRequest, HttpContext httpContext, TranscodingJobHelper transcodingJobHelper, string ffmpegCommandLineArguments, TranscodingJobType transcodingJobType, CancellationTokenSource cancellationTokenSource) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none"; var contentType = state.GetMimeType(outputPath); // Headers only if (isHeadRequest) { httpContext.Response.Headers[HeaderNames.ContentType] = contentType; return(new OkResult()); } var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath); await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { TranscodingJobDto?job; if (!File.Exists(outputPath)) { job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); } else { job = transcodingJobHelper.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } var stream = new ProgressiveFileStream(outputPath, job, transcodingJobHelper); return(new FileStreamResult(stream, contentType)); } finally { transcodingLock.Release(); } }
/// <summary> /// Gets the stream result. /// </summary> /// <param name="state">The state.</param> /// <param name="responseHeaders">The response headers.</param> /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <param name="cancellationTokenSource">The cancellation token source.</param> /// <returns>Task{System.Object}.</returns> private async Task<object> GetStreamResult(StreamState state, IDictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; responseHeaders["Accept-Ranges"] = "none"; var contentType = state.GetMimeType(outputPath); // TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response // What we really want to do is hunt that down and remove that var contentLength = state.EstimateContentLength || isHeadRequest ? GetEstimatedContentLength(state) : null; if (contentLength.HasValue) { responseHeaders["Content-Length"] = contentLength.Value.ToString(UsCulture); } // Headers only if (isHeadRequest) { var streamResult = ResultFactory.GetResult(new byte[] { }, contentType, responseHeaders); var hasOptions = streamResult as IHasOptions; if (hasOptions != null) { if (contentLength.HasValue) { hasOptions.Options["Content-Length"] = contentLength.Value.ToString(CultureInfo.InvariantCulture); } else { if (hasOptions.Options.ContainsKey("Content-Length")) { hasOptions.Options.Remove("Content-Length"); } } } return streamResult; } await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { TranscodingJob job; if (!File.Exists(outputPath)) { job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false); } else { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job); result.Options["Content-Type"] = contentType; // Add the response headers to the result object foreach (var item in responseHeaders) { result.Options[item.Key] = item.Value; } return result; } finally { ApiEntryPoint.Instance.TranscodingStartLock.Release(); } }
/// <summary> /// Gets the stream result. /// </summary> /// <param name="state">The state.</param> /// <param name="responseHeaders">The response headers.</param> /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <param name="cancellationTokenSource">The cancellation token source.</param> /// <returns>Task{System.Object}.</returns> private async Task <object> GetStreamResult(StreamState state, IDictionary <string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; responseHeaders["Accept-Ranges"] = "none"; var contentType = state.GetMimeType(outputPath); // TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response // What we really want to do is hunt that down and remove that var contentLength = state.EstimateContentLength || isHeadRequest?GetEstimatedContentLength(state) : null; if (contentLength.HasValue) { responseHeaders["Content-Length"] = contentLength.Value.ToString(UsCulture); } // Headers only if (isHeadRequest) { var streamResult = ResultFactory.GetResult(new byte[] { }, contentType, responseHeaders); var hasOptions = streamResult as IHasOptions; if (hasOptions != null) { if (contentLength.HasValue) { hasOptions.Options["Content-Length"] = contentLength.Value.ToString(CultureInfo.InvariantCulture); } else { if (hasOptions.Options.ContainsKey("Content-Length")) { hasOptions.Options.Remove("Content-Length"); } } } return(streamResult); } await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { TranscodingJob job; if (!FileSystem.FileExists(outputPath)) { job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false); } else { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job); result.Options["Content-Type"] = contentType; // Add the response headers to the result object foreach (var item in responseHeaders) { result.Options[item.Key] = item.Value; } return(result); } finally { ApiEntryPoint.Instance.TranscodingStartLock.Release(); } }
/// <summary> /// Gets the stream result. /// </summary> /// <param name="state">The state.</param> /// <param name="responseHeaders">The response headers.</param> /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <returns>Task{System.Object}.</returns> private async Task<object> GetStreamResult(StreamState state, IDictionary<string, string> responseHeaders, bool isHeadRequest) { // Use the command line args with a dummy playlist path var outputPath = state.OutputFilePath; responseHeaders["Accept-Ranges"] = "none"; var contentType = state.GetMimeType(outputPath); var contentLength = state.EstimateContentLength ? GetEstimatedContentLength(state) : null; if (contentLength.HasValue) { responseHeaders["Content-Length"] = contentLength.Value.ToString(UsCulture); } // Headers only if (isHeadRequest) { var streamResult = ResultFactory.GetResult(new byte[] { }, contentType, responseHeaders); if (!contentLength.HasValue) { var hasOptions = streamResult as IHasOptions; if (hasOptions != null) { if (hasOptions.Options.ContainsKey("Content-Length")) { hasOptions.Options.Remove("Content-Length"); } } } return streamResult; } if (!File.Exists(outputPath)) { await StartFfMpeg(state, outputPath, new CancellationTokenSource()).ConfigureAwait(false); } else { ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive); var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job); result.Options["Content-Type"] = contentType; // Add the response headers to the result object foreach (var item in responseHeaders) { result.Options[item.Key] = item.Value; } return result; }