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

Added more audio streaming improvements and extracted BaseMediaHandler

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

+ 6 - 151
MediaBrowser.Api/HttpHandlers/AudioHandler.cs

@@ -1,21 +1,16 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
-using System.Net;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Logging;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Net.Handlers;
-using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Api.HttpHandlers
 namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class AudioHandler : BaseMediaHandler<Audio>
     public class AudioHandler : BaseMediaHandler<Audio>
     {
     {
+        /// <summary>
+        /// Supported values: mp3,flac,ogg,wav,asf
+        /// </summary>
         public IEnumerable<string> AudioFormats
         public IEnumerable<string> AudioFormats
         {
         {
             get
             get
@@ -35,7 +30,7 @@ namespace MediaBrowser.Api.HttpHandlers
         {
         {
             get
             get
             {
             {
-                string val = QueryString["audioformats"];
+                string val = QueryString["audiobitrates"];
 
 
                 if (string.IsNullOrEmpty(val))
                 if (string.IsNullOrEmpty(val))
                 {
                 {
@@ -48,13 +43,13 @@ namespace MediaBrowser.Api.HttpHandlers
 
 
         private int? GetMaxAcceptedBitRate(string audioFormat)
         private int? GetMaxAcceptedBitRate(string audioFormat)
         {
         {
-            int index = AudioFormats.ToList().IndexOf(audioFormat);
-
             if (!AudioBitRates.Any())
             if (!AudioBitRates.Any())
             {
             {
                 return null;
                 return null;
             }
             }
 
 
+            int index = AudioFormats.ToList().IndexOf(audioFormat);
+
             return AudioBitRates.ElementAt(index);
             return AudioBitRates.ElementAt(index);
         }
         }
 
 
@@ -134,144 +129,4 @@ namespace MediaBrowser.Api.HttpHandlers
             return "-i \"" + LibraryItem.Path + "\" -vn " + string.Join(" ", audioTranscodeParams.ToArray()) + " -";
             return "-i \"" + LibraryItem.Path + "\" -vn " + string.Join(" ", audioTranscodeParams.ToArray()) + " -";
         }
         }
     }
     }
-
-    public abstract class BaseMediaHandler<T> : BaseHandler
-        where T : BaseItem, new()
-    {
-        private T _LibraryItem;
-        /// <summary>
-        /// Gets the library item that will be played, if any
-        /// </summary>
-        protected T LibraryItem
-        {
-            get
-            {
-                if (_LibraryItem == null)
-                {
-                    string id = QueryString["id"];
-
-                    if (!string.IsNullOrEmpty(id))
-                    {
-                        _LibraryItem = Kernel.Instance.GetItemById(Guid.Parse(id)) as T;
-                    }
-                }
-
-                return _LibraryItem;
-            }
-        }
-
-        public int? AudioChannels
-        {
-            get
-            {
-                string val = QueryString["audiochannels"];
-
-                if (string.IsNullOrEmpty(val))
-                {
-                    return null;
-                }
-
-                return int.Parse(val);
-            }
-        }
-
-        public int? AudioSampleRate
-        {
-            get
-            {
-                string val = QueryString["audiosamplerate"];
-
-                if (string.IsNullOrEmpty(val))
-                {
-                    return 44100;
-                }
-
-                return int.Parse(val);
-            }
-        }
-
-        public override string ContentType
-        {
-            get
-            {
-                return MimeTypes.GetMimeType("." + GetOutputFormat());
-            }
-        }
-
-        public override bool CompressResponse
-        {
-            get
-            {
-                return false;
-            }
-        }
-
-        public override void ProcessRequest(HttpListenerContext ctx)
-        {
-            HttpListenerContext = ctx;
-
-            if (!RequiresConversion())
-            {
-                new StaticFileHandler() { Path = LibraryItem.Path }.ProcessRequest(ctx);
-                return;
-            }
-
-            base.ProcessRequest(ctx);
-        }
-
-        protected abstract string GetCommandLineArguments();
-        protected abstract string GetOutputFormat();
-        protected abstract bool RequiresConversion();
-
-        protected async override Task WriteResponseToOutputStream(Stream stream)
-        {
-            ProcessStartInfo startInfo = new ProcessStartInfo();
-
-            startInfo.CreateNoWindow = true;
-
-            startInfo.UseShellExecute = false;
-            startInfo.RedirectStandardOutput = true;
-            startInfo.RedirectStandardError = true;
-
-            startInfo.FileName = ApiService.FFMpegPath;
-            startInfo.WorkingDirectory = ApiService.FFMpegDirectory;
-            startInfo.Arguments = GetCommandLineArguments();
-
-            Logger.LogInfo(startInfo.FileName + " " + startInfo.Arguments);
-
-            Process process = new Process();
-            process.StartInfo = startInfo;
-
-            // FFMpeg writes debug info to StdErr. This is useful when debugging so let's put it in the log directory.
-            FileStream logStream = new FileStream(Path.Combine(ApplicationPaths.LogDirectoryPath, "ffmpeg-" + Guid.NewGuid().ToString() + ".txt"), FileMode.Create);
-
-            try
-            {
-                process.Start();
-
-                // MUST read both stdout and stderr asynchronously or a deadlock may occurr
-                // If we ever decide to disable the ffmpeg log then you must uncomment the below line.
-                //process.BeginErrorReadLine();
-
-                Task errorTask = Task.Run(async () => { await process.StandardError.BaseStream.CopyToAsync(logStream); });
-
-                await process.StandardOutput.BaseStream.CopyToAsync(stream);
-
-                process.WaitForExit();
-
-                await errorTask;
-
-                Logger.LogInfo("FFMpeg exited with code " + process.ExitCode);
-            }
-            catch (Exception ex)
-            {
-                Logger.LogException(ex);
-            }
-            finally
-            {
-                logStream.Dispose();
-                process.Dispose();
-            }
-        }
-    }
 }
 }

+ 165 - 0
MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs

@@ -0,0 +1,165 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Logging;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Net.Handlers;
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Api.HttpHandlers
+{
+    public abstract class BaseMediaHandler<T> : BaseHandler
+        where T : BaseItem, new()
+    {
+        private T _LibraryItem;
+        /// <summary>
+        /// Gets the library item that will be played, if any
+        /// </summary>
+        protected T LibraryItem
+        {
+            get
+            {
+                if (_LibraryItem == null)
+                {
+                    string id = QueryString["id"];
+
+                    if (!string.IsNullOrEmpty(id))
+                    {
+                        _LibraryItem = Kernel.Instance.GetItemById(Guid.Parse(id)) as T;
+                    }
+                }
+
+                return _LibraryItem;
+            }
+        }
+
+        public int? AudioChannels
+        {
+            get
+            {
+                string val = QueryString["audiochannels"];
+
+                if (string.IsNullOrEmpty(val))
+                {
+                    return null;
+                }
+
+                return int.Parse(val);
+            }
+        }
+
+        public int? AudioSampleRate
+        {
+            get
+            {
+                string val = QueryString["audiosamplerate"];
+
+                if (string.IsNullOrEmpty(val))
+                {
+                    return 44100;
+                }
+
+                return int.Parse(val);
+            }
+        }
+
+        public override string ContentType
+        {
+            get
+            {
+                return MimeTypes.GetMimeType("." + GetOutputFormat());
+            }
+        }
+
+        public override bool CompressResponse
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        public override void ProcessRequest(HttpListenerContext ctx)
+        {
+            HttpListenerContext = ctx;
+
+            if (!RequiresConversion())
+            {
+                new StaticFileHandler() { Path = LibraryItem.Path }.ProcessRequest(ctx);
+                return;
+            }
+
+            base.ProcessRequest(ctx);
+        }
+
+        protected abstract string GetCommandLineArguments();
+        protected abstract string GetOutputFormat();
+        protected abstract bool RequiresConversion();
+
+        protected async override Task WriteResponseToOutputStream(Stream stream)
+        {
+            ProcessStartInfo startInfo = new ProcessStartInfo();
+
+            startInfo.CreateNoWindow = true;
+
+            startInfo.UseShellExecute = false;
+
+            // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
+            startInfo.RedirectStandardOutput = true;
+            startInfo.RedirectStandardError = true;
+
+            startInfo.FileName = ApiService.FFMpegPath;
+            startInfo.WorkingDirectory = ApiService.FFMpegDirectory;
+            startInfo.Arguments = GetCommandLineArguments();
+
+            Logger.LogInfo(startInfo.FileName + " " + startInfo.Arguments);
+
+            Process process = new Process();
+            process.StartInfo = startInfo;
+
+            // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
+            FileStream logStream = new FileStream(Path.Combine(ApplicationPaths.LogDirectoryPath, "ffmpeg-" + Guid.NewGuid().ToString() + ".txt"), FileMode.Create);
+
+            try
+            {
+                process.Start();
+
+                // MUST read both stdout and stderr asynchronously or a deadlock may occurr
+                // If we ever decide to disable the ffmpeg log then you must uncomment the below line.
+                //process.BeginErrorReadLine();
+
+                Task debugLogTask = Task.Run(async () => { await process.StandardError.BaseStream.CopyToAsync(logStream); });
+
+                await process.StandardOutput.BaseStream.CopyToAsync(stream);
+
+                process.WaitForExit();
+
+                Logger.LogInfo("FFMpeg exited with code " + process.ExitCode);
+
+                await debugLogTask;
+            }
+            catch (Exception ex)
+            {
+                Logger.LogException(ex);
+
+                // Hate having to do this
+                try
+                {
+                    process.Kill();
+                }
+                catch
+                {
+                }
+            }
+            finally
+            {
+                logStream.Dispose();
+                process.Dispose();
+            }
+        }
+    }
+}

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

@@ -48,6 +48,7 @@
   <ItemGroup>
   <ItemGroup>
     <Compile Include="ApiService.cs" />
     <Compile Include="ApiService.cs" />
     <Compile Include="HttpHandlers\AudioHandler.cs" />
     <Compile Include="HttpHandlers\AudioHandler.cs" />
+    <Compile Include="HttpHandlers\BaseMediaHandler.cs" />
     <Compile Include="HttpHandlers\GenreHandler.cs" />
     <Compile Include="HttpHandlers\GenreHandler.cs" />
     <Compile Include="HttpHandlers\GenresHandler.cs" />
     <Compile Include="HttpHandlers\GenresHandler.cs" />
     <Compile Include="HttpHandlers\ImageHandler.cs" />
     <Compile Include="HttpHandlers\ImageHandler.cs" />

+ 8 - 0
MediaBrowser.Common/Net/MimeTypes.cs

@@ -115,6 +115,14 @@ namespace MediaBrowser.Common.Net
             {
             {
                 return "audio/x-ms-wma";
                 return "audio/x-ms-wma";
             }
             }
+            else if (ext.EndsWith("flac", StringComparison.OrdinalIgnoreCase))
+            {
+                return "audio/flac";
+            }
+            else if (ext.EndsWith("aac", StringComparison.OrdinalIgnoreCase))
+            {
+                return "audio/x-aac";
+            }
             else if (ext.EndsWith("ogg", StringComparison.OrdinalIgnoreCase) || ext.EndsWith("oga", StringComparison.OrdinalIgnoreCase))
             else if (ext.EndsWith("ogg", StringComparison.OrdinalIgnoreCase) || ext.EndsWith("oga", StringComparison.OrdinalIgnoreCase))
             {
             {
                 return "audio/ogg";
                 return "audio/ogg";