using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using System; using System.IO; using System.Net; using System.Threading.Tasks; namespace MediaBrowser.Api.Streaming { /// /// Class BaseMediaHandler /// /// The type of the T base item type. public abstract class BaseProgressiveStreamingHandler : BaseStreamingHandler where TBaseItemType : BaseItem, IHasMediaStreams, new() { /// /// Gets the type of the transcoding job. /// /// The type of the transcoding job. protected override TranscodingJobType TranscodingJobType { get { return TranscodingJobType.Progressive; } } /// /// Processes the request. /// /// The CTX. /// Task. public override Task ProcessRequest(HttpListenerContext ctx) { HttpListenerContext = ctx; if (!RequiresConversion()) { return new StaticFileHandler(Kernel) { Path = LibraryItem.Path }.ProcessRequest(ctx); } var outputPath = OutputFilePath; if (File.Exists(outputPath) && !Plugin.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive)) { return new StaticFileHandler(Kernel) { Path = outputPath }.ProcessRequest(ctx); } return base.ProcessRequest(ctx); } /// /// Requireses the conversion. /// /// true if XXXX, false otherwise protected bool RequiresConversion() { return !GetBoolQueryStringParam("static"); } /// /// Writes the response to output stream. /// /// The stream. /// The response info. /// Length of the content. /// Task. protected override async Task WriteResponseToOutputStream(Stream stream, ResponseInfo responseInfo, long? contentLength) { // Use the command line args with a dummy playlist path var outputPath = OutputFilePath; if (!File.Exists(outputPath)) { await StartFFMpeg(outputPath).ConfigureAwait(false); } else { Plugin.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); } try { await StreamFile(outputPath, stream).ConfigureAwait(false); } catch (Exception ex) { Logger.ErrorException("Error streaming media", ex); } finally { Plugin.Instance.OnTranscodeEndRequest(outputPath, TranscodingJobType.Progressive); } } /// /// Streams the file. /// /// The path. /// The output stream. /// Task{System.Boolean}. private async Task StreamFile(string path, Stream outputStream) { var eofCount = 0; long position = 0; using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) { while (eofCount < 15) { await fs.CopyToAsync(outputStream).ConfigureAwait(false); var fsPosition = fs.Position; var bytesRead = fsPosition - position; //Logger.LogInfo("Streamed {0} bytes from file {1}", bytesRead, path); if (bytesRead == 0) { eofCount++; await Task.Delay(100).ConfigureAwait(false); } else { eofCount = 0; } position = fsPosition; } } } /// /// Deletes the partial stream files. /// /// The output file path. protected override void DeletePartialStreamFiles(string outputFilePath) { File.Delete(outputFilePath); } } }