diff --git a/MediaBrowser.Api/ApiService.cs b/MediaBrowser.Api/ApiService.cs
index 5945bf25ad..852c4e7db6 100644
--- a/MediaBrowser.Api/ApiService.cs
+++ b/MediaBrowser.Api/ApiService.cs
@@ -1,7 +1,8 @@
using System;
-using System.Collections.Generic;
+using System.IO;
using System.Linq;
-using MediaBrowser.Api.Transcoding;
+using System.Reflection;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Model.Entities;
@@ -12,41 +13,42 @@ namespace MediaBrowser.Api
///
public static class ApiService
{
+ private static string _FFMpegDirectory = null;
///
- /// Holds the list of active transcoding jobs
+ /// Gets the folder path to ffmpeg
///
- private static List CurrentTranscodingJobs = new List();
-
- ///
- /// Finds an active transcoding job
- ///
- 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);
+ }
+ }
+ }
+ }
+
+ return _FFMpegDirectory;
}
}
- ///
- /// Removes a transcoding job from the active list
- ///
- public static void RemoveTranscodingJob(TranscodingJob job)
+ public static string FFMpegPath
{
- lock (CurrentTranscodingJobs)
+ get
{
- CurrentTranscodingJobs.Remove(job);
- }
- }
-
- ///
- /// Adds a transcoding job to the active list
- ///
- public static void AddTranscodingJob(TranscodingJob job)
- {
- lock (CurrentTranscodingJobs)
- {
- CurrentTranscodingJobs.Add(job);
+ return System.IO.Path.Combine(FFMpegDirectory, "ffmpeg.exe");
}
}
diff --git a/MediaBrowser.Api/HttpHandlers/AudioHandler.cs b/MediaBrowser.Api/HttpHandlers/AudioHandler.cs
index 76a48308b9..5bfbfd1a45 100644
--- a/MediaBrowser.Api/HttpHandlers/AudioHandler.cs
+++ b/MediaBrowser.Api/HttpHandlers/AudioHandler.cs
@@ -1,16 +1,18 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
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.Controller;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Api.HttpHandlers
{
- public class AudioHandler : StaticFileHandler
+ public class AudioHandler : BaseHandler
{
private Audio _LibraryItem;
///
@@ -34,68 +36,42 @@ namespace MediaBrowser.Api.HttpHandlers
}
}
- public override string Path
+ public override bool CompressResponse
{
get
{
- return TranscodedPath;
+ return false;
}
}
- private string _TranscodedPath;
- ///
- /// Gets the library item that will be played, if any
- ///
- private string TranscodedPath
+ protected override bool IsAsyncHandler
{
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 _TranscodedPath;
+ return true;
}
}
- public string AudioFormat
+ public override string ContentType
{
get
{
- string val = QueryString["audiobitrate"];
+ return MimeTypes.GetMimeType("." + GetOutputFormat());
+ }
+ }
+
+ public IEnumerable AudioFormats
+ {
+ get
+ {
+ string val = QueryString["audioformat"];
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
{
@@ -144,87 +120,17 @@ namespace MediaBrowser.Api.HttpHandlers
}
}
- private static string _StreamsDirectory = null;
- ///
- /// Gets the folder path to where transcodes will be cached
- ///
- public static string StreamsDirectory
+ public override void ProcessRequest(HttpListenerContext ctx)
{
- get
+ HttpListenerContext = ctx;
+
+ if (!RequiresTranscoding())
{
- 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;
- ///
- /// Gets the folder path to ffmpeg
- ///
- 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
- {
- get
- {
- return System.IO.Path.Combine(FFMpegDirectory, "ffmpeg.exe");
- }
- }
-
- private string GetOutputFilePath(string input)
- {
- string hash = Kernel.GetMD5(input).ToString();
-
- if (AudioBitRate.HasValue)
- {
- hash += "_ab" + AudioBitRate;
- }
- if (NumAudioChannels.HasValue)
- {
- hash += "_ac" + NumAudioChannels;
- }
- if (AudioSampleRate.HasValue)
- {
- hash += "_ar" + AudioSampleRate;
+ new StaticFileHandler() { Path = LibraryItem.Path }.ProcessRequest(ctx);
+ return;
}
- string filename = hash + "." + AudioFormat.ToLower();
-
- return System.IO.Path.Combine(StreamsDirectory, filename);
+ base.ProcessRequest(ctx);
}
///
@@ -232,14 +138,8 @@ namespace MediaBrowser.Api.HttpHandlers
///
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;
}
@@ -254,9 +154,9 @@ namespace MediaBrowser.Api.HttpHandlers
}
// 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;
}
@@ -270,29 +170,27 @@ namespace MediaBrowser.Api.HttpHandlers
return true;
}
}
-
+
// Yay
return false;
}
- ///
- /// Creates a new transcoding job
- ///
- 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();
}
///
/// Creates arguments to pass to ffmpeg
///
- private string GetAudioArguments(string input, string output)
+ private string GetAudioArguments()
{
List audioTranscodeParams = new List();
@@ -301,9 +199,9 @@ namespace MediaBrowser.Api.HttpHandlers
audioTranscodeParams.Add("-ab " + AudioBitRate.Value);
}
- if (NumAudioChannels.HasValue)
+ if (AudioChannels.HasValue)
{
- audioTranscodeParams.Add("-ac " + NumAudioChannels.Value);
+ audioTranscodeParams.Add("-ac " + AudioChannels.Value);
}
if (AudioSampleRate.HasValue)
@@ -311,9 +209,45 @@ namespace MediaBrowser.Api.HttpHandlers
audioTranscodeParams.Add("-ar " + AudioSampleRate.Value);
}
- audioTranscodeParams.Add("-f " + AudioFormat);
+ audioTranscodeParams.Add("-f " + GetOutputFormat());
- return "-i \"" + input + "\" -vn " + string.Join(" ", audioTranscodeParams.ToArray()) + " \"" + output + "\"";
+ 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;
+
+ 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();
+ }
}
}
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index bbb8dcbb43..83ab5a7ee9 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -66,7 +66,6 @@
-
diff --git a/MediaBrowser.Api/Transcoding/TranscodingJob.cs b/MediaBrowser.Api/Transcoding/TranscodingJob.cs
deleted file mode 100644
index e504fec095..0000000000
--- a/MediaBrowser.Api/Transcoding/TranscodingJob.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.Logging;
-
-namespace MediaBrowser.Api.Transcoding
-{
- ///
- /// Represents an active transcoding job
- ///
- 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; }
-
- ///
- /// Starts the job
- ///
- 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();
- }
-
- ///
- /// Provides a helper to wait for the job to exit
- ///
- 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
- }
-}
diff --git a/MediaBrowser.Api/ffmpeg/ffmpeg.exe.REMOVED.git-id b/MediaBrowser.Api/ffmpeg/ffmpeg.exe.REMOVED.git-id
index cefa9ca782..cd77f682ac 100644
--- a/MediaBrowser.Api/ffmpeg/ffmpeg.exe.REMOVED.git-id
+++ b/MediaBrowser.Api/ffmpeg/ffmpeg.exe.REMOVED.git-id
@@ -1 +1 @@
-a9ba5e8a56932043f5fe75db9b4f3b29fe210dbf
\ No newline at end of file
+56b4040e2eeb431bb27af6d66dc5a9305eb0879f
\ No newline at end of file
diff --git a/MediaBrowser.Common/Logging/Logger.cs b/MediaBrowser.Common/Logging/Logger.cs
index d1ae9b8f35..e66c1d8444 100644
--- a/MediaBrowser.Common/Logging/Logger.cs
+++ b/MediaBrowser.Common/Logging/Logger.cs
@@ -21,6 +21,11 @@ namespace MediaBrowser.Common.Logging
LoggerInstance.LogError(message, paramList);
}
+ public static void LogException(Exception ex, params object[] paramList)
+ {
+ LogException(string.Empty, ex, paramList);
+ }
+
public static void LogException(string message, Exception ex, params object[] paramList)
{
LoggerInstance.LogException(message, ex, paramList);
diff --git a/MediaBrowser.Common/Net/Handlers/BaseHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseHandler.cs
index 5cb476e027..120d2ce7b2 100644
--- a/MediaBrowser.Common/Net/Handlers/BaseHandler.cs
+++ b/MediaBrowser.Common/Net/Handlers/BaseHandler.cs
@@ -59,7 +59,7 @@ namespace MediaBrowser.Common.Net.Handlers
///
/// The original HttpListenerContext
///
- protected HttpListenerContext HttpListenerContext { get; private set; }
+ protected HttpListenerContext HttpListenerContext { get; set; }
///
/// The original QueryString
@@ -196,7 +196,7 @@ namespace MediaBrowser.Common.Net.Handlers
}
}
- public void ProcessRequest(HttpListenerContext ctx)
+ public virtual void ProcessRequest(HttpListenerContext ctx)
{
HttpListenerContext = ctx;
diff --git a/MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs b/MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs
index 9bffd8e597..35d38fb4a7 100644
--- a/MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs
+++ b/MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs
@@ -10,12 +10,22 @@ namespace MediaBrowser.Common.Net.Handlers
{
public class StaticFileHandler : BaseHandler
{
+ private string _Path;
public virtual string Path
{
get
{
+ if (!string.IsNullOrWhiteSpace(_Path))
+ {
+ return _Path;
+ }
+
return QueryString["path"];
}
+ set
+ {
+ _Path = value;
+ }
}
private bool FileStreamDiscovered = false;
diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs
index 7b257e90dc..9f9ce924d2 100644
--- a/MediaBrowser.Controller/Kernel.cs
+++ b/MediaBrowser.Controller/Kernel.cs
@@ -8,7 +8,6 @@ using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Kernel;
-using MediaBrowser.Common.Serialization;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.IO;