2
0
Эх сурвалжийг харах

Reworked audio transcoding to output directly to response

LukePulverenti Luke Pulverenti luke pulverenti 13 жил өмнө
parent
commit
bda2c81dab

+ 29 - 27
MediaBrowser.Api/ApiService.cs

@@ -1,7 +1,8 @@
 using System;
 using System;
-using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Linq;
-using MediaBrowser.Api.Transcoding;
+using System.Reflection;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
@@ -12,41 +13,42 @@ namespace MediaBrowser.Api
     /// </summary>
     /// </summary>
     public static class ApiService
     public static class ApiService
     {
     {
+        private static string _FFMpegDirectory = null;
         /// <summary>
         /// <summary>
-        /// Holds the list of active transcoding jobs
+        /// Gets the folder path to ffmpeg
         /// </summary>
         /// </summary>
-        private static List<TranscodingJob> CurrentTranscodingJobs = new List<TranscodingJob>();
-
-        /// <summary>
-        /// Finds an active transcoding job
-        /// </summary>
-        public static TranscodingJob GetTranscodingJob(string outputPath)
+        public static string FFMpegDirectory
         {
         {
-            lock (CurrentTranscodingJobs)
+            get
             {
             {
-                return CurrentTranscodingJobs.FirstOrDefault(j => j.OutputFile.Equals(outputPath, StringComparison.OrdinalIgnoreCase));
-            }
-        }
+                if (_FFMpegDirectory == null)
+                {
+                    _FFMpegDirectory = System.IO.Path.Combine(ApplicationPaths.ProgramDataPath, "ffmpeg");
+
+                    if (!Directory.Exists(_FFMpegDirectory))
+                    {
+                        Directory.CreateDirectory(_FFMpegDirectory);
+
+                        // Extract ffmpeg
+                        using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Api.ffmpeg.ffmpeg.exe"))
+                        {
+                            using (FileStream fileStream = new FileStream(FFMpegPath, FileMode.Create))
+                            {
+                                stream.CopyTo(fileStream);
+                            }
+                        }
+                    }
+                }
 
 
-        /// <summary>
-        /// Removes a transcoding job from the active list
-        /// </summary>
-        public static void RemoveTranscodingJob(TranscodingJob job)
-        {
-            lock (CurrentTranscodingJobs)
-            {
-                CurrentTranscodingJobs.Remove(job);
+                return _FFMpegDirectory;
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Adds a transcoding job to the active list
-        /// </summary>
-        public static void AddTranscodingJob(TranscodingJob job)
+        public static string FFMpegPath
         {
         {
-            lock (CurrentTranscodingJobs)
+            get
             {
             {
-                CurrentTranscodingJobs.Add(job);
+                return System.IO.Path.Combine(FFMpegDirectory, "ffmpeg.exe");
             }
             }
         }
         }
 
 

+ 82 - 148
MediaBrowser.Api/HttpHandlers/AudioHandler.cs

@@ -1,16 +1,18 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.IO;
-using System.Reflection;
-using MediaBrowser.Api.Transcoding;
-using MediaBrowser.Common.Configuration;
+using System.Linq;
+using System.Net;
+using MediaBrowser.Common.Logging;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
-    public class AudioHandler : StaticFileHandler
+    public class AudioHandler : BaseHandler
     {
     {
         private Audio _LibraryItem;
         private Audio _LibraryItem;
         /// <summary>
         /// <summary>
@@ -34,68 +36,42 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        public override string Path
+        public override bool CompressResponse
         {
         {
             get
             get
             {
             {
-                return TranscodedPath;
+                return false;
             }
             }
         }
         }
 
 
-        private string _TranscodedPath;
-        /// <summary>
-        /// Gets the library item that will be played, if any
-        /// </summary>
-        private string TranscodedPath
+        protected override bool IsAsyncHandler
         {
         {
             get
             get
             {
             {
-                if (_TranscodedPath == null)
-                {
-                    string originalMediaPath = LibraryItem == null ? base.Path : LibraryItem.Path;
-                    
-                    if (!RequiresTranscoding())
-                    {
-                        _TranscodedPath = originalMediaPath;
-                    }
-                    else
-                    {
-                        string outputPath = GetOutputFilePath(originalMediaPath);
-
-                        // Find the job in the list
-                        TranscodingJob job = ApiService.GetTranscodingJob(outputPath);
-
-                        if (job == null && !File.Exists(outputPath))
-                        {
-                            job = GetNewTranscodingJob(originalMediaPath, outputPath);
-                            job.Start();
-                        }
-
-                        if (job != null)
-                        {
-                            job.WaitForExit();
-                        }
-
-                        _TranscodedPath = outputPath;
-                    }
-                }
+                return true;
+            }
+        }
 
 
-                return _TranscodedPath;
+        public override string ContentType
+        {
+            get
+            {
+                return MimeTypes.GetMimeType("." + GetOutputFormat());
             }
             }
         }
         }
 
 
-        public string AudioFormat
+        public IEnumerable<string> AudioFormats
         {
         {
             get
             get
             {
             {
-                string val = QueryString["audiobitrate"];
+                string val = QueryString["audioformat"];
 
 
                 if (string.IsNullOrEmpty(val))
                 if (string.IsNullOrEmpty(val))
                 {
                 {
-                    return "mp3";
+                    return new string[] { "mp3" };
                 }
                 }
 
 
-                return val;
+                return val.Split(',');
             }
             }
         }
         }
 
 
@@ -114,7 +90,7 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        public int? NumAudioChannels
+        public int? AudioChannels
         {
         {
             get
             get
             {
             {
@@ -144,87 +120,17 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        private static string _StreamsDirectory = null;
-        /// <summary>
-        /// Gets the folder path to where transcodes will be cached
-        /// </summary>
-        public static string StreamsDirectory
-        {
-            get
-            {
-                if (_StreamsDirectory == null)
-                {
-                    _StreamsDirectory = System.IO.Path.Combine(ApplicationPaths.ProgramDataPath, "streams");
-
-                    if (!Directory.Exists(_StreamsDirectory))
-                    {
-                        Directory.CreateDirectory(_StreamsDirectory);
-                    }
-                }
-
-                return _StreamsDirectory;
-            }
-        }
-
-        private static string _FFMpegDirectory = null;
-        /// <summary>
-        /// Gets the folder path to ffmpeg
-        /// </summary>
-        public static string FFMpegDirectory
-        {
-            get
-            {
-                if (_FFMpegDirectory == null)
-                {
-                    _FFMpegDirectory = System.IO.Path.Combine(ApplicationPaths.ProgramDataPath, "ffmpeg");
-
-                    if (!Directory.Exists(_FFMpegDirectory))
-                    {
-                        Directory.CreateDirectory(_FFMpegDirectory);
-
-                        // Extract ffmpeg
-                        using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Api.ffmpeg.ffmpeg.exe"))
-                        {
-                            using (FileStream fileStream = new FileStream(FFMpegPath, FileMode.Create))
-                            {
-                                stream.CopyTo(fileStream);
-                            }
-                        }
-                    }
-                }
-
-                return _FFMpegDirectory;
-            }
-        }
-
-        private static string FFMpegPath
+        public override void ProcessRequest(HttpListenerContext ctx)
         {
         {
-            get
-            {
-                return System.IO.Path.Combine(FFMpegDirectory, "ffmpeg.exe");
-            }
-        }
+            HttpListenerContext = ctx;
 
 
-        private string GetOutputFilePath(string input)
-        {
-            string hash = Kernel.GetMD5(input).ToString();
-
-            if (AudioBitRate.HasValue)
+            if (!RequiresTranscoding())
             {
             {
-                hash += "_ab" + AudioBitRate;
+                new StaticFileHandler() { Path = LibraryItem.Path }.ProcessRequest(ctx);
+                return;
             }
             }
-            if (NumAudioChannels.HasValue)
-            {
-                hash += "_ac" + NumAudioChannels;
-            }
-            if (AudioSampleRate.HasValue)
-            {
-                hash += "_ar" + AudioSampleRate;
-            }
-
-            string filename = hash + "." + AudioFormat.ToLower();
 
 
-            return System.IO.Path.Combine(StreamsDirectory, filename);
+            base.ProcessRequest(ctx);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -232,14 +138,8 @@ namespace MediaBrowser.Api.HttpHandlers
         /// </summary>
         /// </summary>
         private bool RequiresTranscoding()
         private bool RequiresTranscoding()
         {
         {
-            // Only support skipping transcoding for library items
-            if (LibraryItem == null)
-            {
-                return true;
-            }
-
-            // If it's not in the same format, we need to transcode
-            if (!LibraryItem.Path.EndsWith(AudioFormat, StringComparison.OrdinalIgnoreCase))
+            // If it's not in a format the consumer accepts, return true
+            if (!AudioFormats.Any(f => LibraryItem.Path.EndsWith(f, StringComparison.OrdinalIgnoreCase)))
             {
             {
                 return true;
                 return true;
             }
             }
@@ -254,9 +154,9 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
 
 
             // If the number of channels is greater than our desired channels, we need to transcode
             // If the number of channels is greater than our desired channels, we need to transcode
-            if (NumAudioChannels.HasValue)
+            if (AudioChannels.HasValue)
             {
             {
-                if (NumAudioChannels.Value < LibraryItem.Channels)
+                if (AudioChannels.Value < LibraryItem.Channels)
                 {
                 {
                     return true;
                     return true;
                 }
                 }
@@ -270,29 +170,27 @@ namespace MediaBrowser.Api.HttpHandlers
                     return true;
                     return true;
                 }
                 }
             }
             }
-            
+
             // Yay
             // Yay
             return false;
             return false;
         }
         }
 
 
-        /// <summary>
-        /// Creates a new transcoding job
-        /// </summary>
-        private TranscodingJob GetNewTranscodingJob(string input, string output)
+        private string GetOutputFormat()
         {
         {
-            return new TranscodingJob()
+            string format = AudioFormats.FirstOrDefault(f => LibraryItem.Path.EndsWith(f, StringComparison.OrdinalIgnoreCase));
+
+            if (!string.IsNullOrWhiteSpace(format))
             {
             {
-                InputFile = input,
-                OutputFile = output,
-                TranscoderPath = FFMpegPath,
-                Arguments = GetAudioArguments(input, output)
-            };
+                return format;
+            }
+
+            return AudioFormats.First();
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Creates arguments to pass to ffmpeg
         /// Creates arguments to pass to ffmpeg
         /// </summary>
         /// </summary>
-        private string GetAudioArguments(string input, string output)
+        private string GetAudioArguments()
         {
         {
             List<string> audioTranscodeParams = new List<string>();
             List<string> audioTranscodeParams = new List<string>();
 
 
@@ -301,9 +199,9 @@ namespace MediaBrowser.Api.HttpHandlers
                 audioTranscodeParams.Add("-ab " + AudioBitRate.Value);
                 audioTranscodeParams.Add("-ab " + AudioBitRate.Value);
             }
             }
 
 
-            if (NumAudioChannels.HasValue)
+            if (AudioChannels.HasValue)
             {
             {
-                audioTranscodeParams.Add("-ac " + NumAudioChannels.Value);
+                audioTranscodeParams.Add("-ac " + AudioChannels.Value);
             }
             }
 
 
             if (AudioSampleRate.HasValue)
             if (AudioSampleRate.HasValue)
@@ -311,9 +209,45 @@ namespace MediaBrowser.Api.HttpHandlers
                 audioTranscodeParams.Add("-ar " + AudioSampleRate.Value);
                 audioTranscodeParams.Add("-ar " + AudioSampleRate.Value);
             }
             }
 
 
-            audioTranscodeParams.Add("-f " + AudioFormat);
+            audioTranscodeParams.Add("-f " + GetOutputFormat());
+
+            return "-i \"" + LibraryItem.Path + "\" -vn " + string.Join(" ", audioTranscodeParams.ToArray()) + " -";
+        }
+
+        protected async override void WriteResponseToOutputStream(Stream stream)
+        {
+            ProcessStartInfo startInfo = new ProcessStartInfo();
+
+            startInfo.CreateNoWindow = true;
+
+            startInfo.UseShellExecute = false;
+            startInfo.RedirectStandardOutput = true;
 
 
-            return "-i \"" + input + "\" -vn " + string.Join(" ", audioTranscodeParams.ToArray()) + " \"" + output + "\"";
+            startInfo.FileName = ApiService.FFMpegPath;
+            startInfo.WorkingDirectory = ApiService.FFMpegDirectory;
+            startInfo.Arguments = GetAudioArguments();
+
+            Logger.LogInfo("Audio Handler Transcode: " + ApiService.FFMpegPath + " " + startInfo.Arguments);
+
+            Process process = new Process();
+            process.StartInfo = startInfo;
+
+            try
+            {
+                process.Start();
+
+                await process.StandardOutput.BaseStream.CopyToAsync(stream);
+            }
+            catch (Exception ex)
+            {
+                Logger.LogException(ex);
+            }
+            finally
+            {
+                DisposeResponseStream();
+
+                process.Dispose();
+            }
         }
         }
     }
     }
 }
 }

+ 0 - 1
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -66,7 +66,6 @@
     <Compile Include="ImageProcessor.cs" />
     <Compile Include="ImageProcessor.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Transcoding\TranscodingJob.cs" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">

+ 0 - 102
MediaBrowser.Api/Transcoding/TranscodingJob.cs

@@ -1,102 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.Logging;
-
-namespace MediaBrowser.Api.Transcoding
-{
-    /// <summary>
-    /// Represents an active transcoding job
-    /// </summary>
-    public class TranscodingJob
-    {
-        public string InputFile { get; set; }
-        public string OutputFile { get; set; }
-        public string TranscoderPath { get; set; }
-        public string Arguments { get; set; }
-
-        public TranscoderJobStatus Status { get; private set; }
-
-        /// <summary>
-        /// Starts the job
-        /// </summary>
-        public void Start()
-        {
-            ApiService.AddTranscodingJob(this);
-            
-            ProcessStartInfo startInfo = new ProcessStartInfo();
-
-            startInfo.CreateNoWindow = true;
-
-            startInfo.UseShellExecute = false;
-
-            startInfo.FileName = TranscoderPath;
-            startInfo.WorkingDirectory = Path.GetDirectoryName(TranscoderPath);
-            startInfo.Arguments = Arguments;
-
-            Logger.LogInfo("TranscodingJob.Start: " + TranscoderPath + " " + Arguments);
-
-            Process process = new Process();
-
-            process.StartInfo = startInfo;
-
-            process.EnableRaisingEvents = true;
-
-            process.Start();
-
-            process.Exited += process_Exited;
-        }
-
-        void process_Exited(object sender, EventArgs e)
-        {
-            ApiService.RemoveTranscodingJob(this);
-            
-            Process process = sender as Process;
-
-            // If it terminated with an error
-            if (process.ExitCode != 0)
-            {
-                Status = TranscoderJobStatus.Error;
-
-                // Delete this since it won't be valid
-                if (File.Exists(OutputFile))
-                {
-                    File.Delete(OutputFile);
-                }
-            }
-            else
-            {
-                Status = TranscoderJobStatus.Completed;
-            }
-
-            process.Dispose();
-        }
-
-        /// <summary>
-        /// Provides a helper to wait for the job to exit
-        /// </summary>
-        public void WaitForExit()
-        {
-            while (true)
-            {
-                TranscoderJobStatus status = Status;
-
-                if (status == TranscoderJobStatus.Completed || status == TranscoderJobStatus.Error)
-                {
-                    break;
-                }
-
-                Thread.Sleep(500);
-            }
-        }
-    }
-
-    public enum TranscoderJobStatus
-    {
-        Queued,
-        Started,
-        Completed,
-        Error
-    }
-}

+ 0 - 1
MediaBrowser.Controller/Kernel.cs

@@ -8,7 +8,6 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Kernel;
 using MediaBrowser.Common.Kernel;
-using MediaBrowser.Common.Serialization;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;