@ -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 System.Linq ;
using MediaBrowser.Api.Transcoding ;
using System.Net ;
using MediaBrowser.Common.Configuration ;
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 : StaticFil eHandler
public class AudioHandler : Bas eHandler
{
{
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 ;
protected override bool IsAsyncHandler
/// <summary>
/// Gets the library item that will be played, if any
/// </summary>
private string TranscodedPath
{
{
get
get
{
{
if ( _TranscodedPath = = null )
return true ;
{
}
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 ;
public override string ContentType
{
get
{
return MimeTypes . GetMimeType ( "." + GetOutputFormat ( ) ) ;
}
}
}
}
public string AudioFormat
public IEnumerable < string > AudioFormats
{
{
get
get
{
{
string val = QueryString [ "audio bitrate "] ;
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? Num AudioChannels
public int? AudioChannels
{
{
get
get
{
{
@ -144,87 +120,17 @@ namespace MediaBrowser.Api.HttpHandlers
}
}
}
}
private static string _StreamsDirectory = null ;
public override void ProcessRequest ( HttpListenerContext ctx )
/// <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
{
{
get
HttpListenerContext = ctx ;
{
return System . IO . Path . Combine ( FFMpegDirectory , "ffmpeg.exe" ) ;
}
}
private string GetOutputFilePath ( string input )
if ( ! RequiresTranscoding ( ) )
{
string hash = Kernel . GetMD5 ( input ) . ToString ( ) ;
if ( AudioBitRate . HasValue )
{
{
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 it's not in a format the consumer accepts, return true
if ( LibraryItem = = null )
if ( ! AudioFormats . Any ( f = > LibraryItem . Path . EndsWith ( f , StringComparison . OrdinalIgnoreCase ) ) )
{
return true ;
}
// If it's not in the same format, we need to transcode
if ( ! LibraryItem . Path . EndsWith ( AudioFormat , 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 ( Num AudioChannels. HasValue )
if ( AudioChannels. HasValue )
{
{
if ( Num AudioChannels. 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>
private string GetOutputFormat ( )
/// Creates a new transcoding job
/// </summary>
private TranscodingJob GetNewTranscodingJob ( string input , string output )
{
{
return new TranscodingJob ( )
string format = AudioFormats . FirstOrDefault ( f = > LibraryItem . Path . EndsWith ( f , StringComparison . OrdinalIgnoreCase ) ) ;
if ( ! string . IsNullOrWhiteSpace ( format ) )
{
{
InputFile = input ,
return format ;
OutputFile = output ,
}
TranscoderPath = FFMpegPath ,
Arguments = GetAudioArguments ( input , output )
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 ( Num AudioChannels. HasValue )
if ( AudioChannels. HasValue )
{
{
audioTranscodeParams . Add ( "-ac " + Num AudioChannels. 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 ( ) ;
}
}
}
}
}
}
}