|
@@ -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;
|
|
|
/// <summary>
|
|
@@ -34,68 +36,42 @@ namespace MediaBrowser.Api.HttpHandlers
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public override string Path
|
|
|
+ public override bool CompressResponse
|
|
|
{
|
|
|
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
|
|
|
{
|
|
|
- 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
|
|
|
{
|
|
|
- string val = QueryString["audiobitrate"];
|
|
|
+ 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;
|
|
|
- /// <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>
|
|
@@ -232,14 +138,8 @@ namespace MediaBrowser.Api.HttpHandlers
|
|
|
/// </summary>
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
- /// <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>
|
|
|
/// Creates arguments to pass to ffmpeg
|
|
|
/// </summary>
|
|
|
- private string GetAudioArguments(string input, string output)
|
|
|
+ private string GetAudioArguments()
|
|
|
{
|
|
|
List<string> audioTranscodeParams = new List<string>();
|
|
|
|
|
@@ -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 \"" + 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();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|