2014-11-15 02:31:03 +00:00
|
|
|
|
using MediaBrowser.Common.Configuration;
|
|
|
|
|
using MediaBrowser.Common.Extensions;
|
|
|
|
|
using MediaBrowser.Model.Logging;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Text;
|
2015-09-13 23:07:54 +00:00
|
|
|
|
using MediaBrowser.Common.IO;
|
2015-09-18 03:08:45 +00:00
|
|
|
|
using System.Collections.Generic;
|
2014-11-15 02:31:03 +00:00
|
|
|
|
|
|
|
|
|
namespace MediaBrowser.Server.Startup.Common.FFMpeg
|
|
|
|
|
{
|
|
|
|
|
public class FFmpegValidator
|
|
|
|
|
{
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
private readonly IApplicationPaths _appPaths;
|
2015-09-13 23:07:54 +00:00
|
|
|
|
private readonly IFileSystem _fileSystem;
|
2014-11-15 02:31:03 +00:00
|
|
|
|
|
2015-09-13 23:07:54 +00:00
|
|
|
|
public FFmpegValidator(ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem)
|
2014-11-15 02:31:03 +00:00
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_appPaths = appPaths;
|
2015-09-13 23:07:54 +00:00
|
|
|
|
_fileSystem = fileSystem;
|
2014-11-15 02:31:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
public Tuple<List<string>,List<string>> Validate(FFMpegInfo info)
|
2014-11-15 02:31:03 +00:00
|
|
|
|
{
|
|
|
|
|
_logger.Info("FFMpeg: {0}", info.EncoderPath);
|
|
|
|
|
_logger.Info("FFProbe: {0}", info.ProbePath);
|
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
var decoders = GetDecoders(info.EncoderPath);
|
|
|
|
|
var encoders = GetEncoders(info.EncoderPath);
|
2014-11-29 02:40:46 +00:00
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
return new Tuple<List<string>, List<string>>(decoders, encoders);
|
2014-11-18 02:48:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
private List<string> GetDecoders(string ffmpegPath)
|
2014-11-18 02:48:22 +00:00
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
string output = string.Empty;
|
2014-11-18 02:48:22 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
output = GetFFMpegOutput(ffmpegPath, "-decoders");
|
2014-11-18 02:48:22 +00:00
|
|
|
|
}
|
|
|
|
|
catch
|
2014-11-15 02:31:03 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
2014-11-18 02:48:22 +00:00
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
var found = new List<string>();
|
|
|
|
|
var required = new[]
|
2014-11-18 02:48:22 +00:00
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
"h264_qsv",
|
|
|
|
|
"mpeg2_qsv",
|
|
|
|
|
"vc1_qsv"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
foreach (var codec in required)
|
|
|
|
|
{
|
|
|
|
|
var srch = " " + codec + " ";
|
2014-11-18 02:48:22 +00:00
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1)
|
2014-11-18 02:48:22 +00:00
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
_logger.Warn("ffmpeg is missing decoder " + codec);
|
2014-11-18 02:48:22 +00:00
|
|
|
|
}
|
2015-09-18 03:08:45 +00:00
|
|
|
|
else
|
2014-11-18 02:48:22 +00:00
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
found.Add(codec);
|
2014-11-18 02:48:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
return found;
|
2014-11-15 02:31:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
private List<string> GetEncoders(string ffmpegPath)
|
2014-11-15 02:31:03 +00:00
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
string output = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
output = GetFFMpegOutput(ffmpegPath, "-encoders");
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var found = new List<string>();
|
2014-11-15 02:31:03 +00:00
|
|
|
|
var required = new[]
|
|
|
|
|
{
|
|
|
|
|
"libx264",
|
2015-01-11 03:06:16 +00:00
|
|
|
|
"libx265",
|
2014-11-15 02:31:03 +00:00
|
|
|
|
"mpeg4",
|
|
|
|
|
"msmpeg4",
|
|
|
|
|
"libvpx",
|
|
|
|
|
//"libvpx-vp9",
|
|
|
|
|
"aac",
|
|
|
|
|
"ac3",
|
|
|
|
|
"libmp3lame",
|
|
|
|
|
"libvorbis",
|
|
|
|
|
"srt"
|
|
|
|
|
};
|
|
|
|
|
|
2015-09-18 03:08:45 +00:00
|
|
|
|
foreach (var codec in required)
|
2014-11-15 02:31:03 +00:00
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
var srch = " " + codec + " ";
|
2014-11-15 02:31:03 +00:00
|
|
|
|
|
|
|
|
|
if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1)
|
|
|
|
|
{
|
2015-09-18 03:08:45 +00:00
|
|
|
|
_logger.Warn("ffmpeg is missing encoder " + codec);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
found.Add(codec);
|
2014-11-15 02:31:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-09-18 03:08:45 +00:00
|
|
|
|
|
|
|
|
|
return found;
|
2014-11-15 02:31:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-18 02:48:22 +00:00
|
|
|
|
private string GetFFMpegOutput(string path, string arguments)
|
2014-11-15 02:31:03 +00:00
|
|
|
|
{
|
|
|
|
|
var process = new Process
|
|
|
|
|
{
|
|
|
|
|
StartInfo = new ProcessStartInfo
|
|
|
|
|
{
|
|
|
|
|
CreateNoWindow = true,
|
|
|
|
|
UseShellExecute = false,
|
|
|
|
|
FileName = path,
|
|
|
|
|
Arguments = arguments,
|
|
|
|
|
WindowStyle = ProcessWindowStyle.Hidden,
|
|
|
|
|
ErrorDialog = false,
|
|
|
|
|
RedirectStandardOutput = true,
|
|
|
|
|
RedirectStandardError = true
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using (process)
|
|
|
|
|
{
|
|
|
|
|
process.Start();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
process.BeginErrorReadLine();
|
|
|
|
|
|
|
|
|
|
using (var reader = new StreamReader(process.StandardOutput.BaseStream))
|
|
|
|
|
{
|
|
|
|
|
return reader.ReadToEnd();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// Hate having to do this
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
process.Kill();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex1)
|
|
|
|
|
{
|
|
|
|
|
_logger.ErrorException("Error killing ffmpeg", ex1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|