aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding/Encoder
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder')
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs5
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs44
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs54
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs1
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs14
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs317
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs5
8 files changed, 262 insertions, 185 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
index f42582270..5a554d26f 100644
--- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
@@ -7,13 +7,13 @@ using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class AudioEncoder : BaseEncoder
{
- public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager)
+ public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
{
}
@@ -114,5 +114,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
get { return false; }
}
+
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
index c7b78aae3..b8087fded 100644
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
@@ -11,13 +11,12 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dlna;
namespace MediaBrowser.MediaEncoding.Encoder
@@ -33,6 +32,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected readonly ISessionManager SessionManager;
protected readonly ISubtitleEncoder SubtitleEncoder;
protected readonly IMediaSourceManager MediaSourceManager;
+ protected IProcessFactory ProcessFactory;
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -44,7 +44,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
ILibraryManager libraryManager,
ISessionManager sessionManager,
ISubtitleEncoder subtitleEncoder,
- IMediaSourceManager mediaSourceManager)
+ IMediaSourceManager mediaSourceManager, IProcessFactory processFactory)
{
MediaEncoder = mediaEncoder;
Logger = logger;
@@ -55,6 +55,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
SessionManager = sessionManager;
SubtitleEncoder = subtitleEncoder;
MediaSourceManager = mediaSourceManager;
+ ProcessFactory = processFactory;
}
public async Task<EncodingJob> Start(EncodingJobOptions options,
@@ -73,27 +74,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
var commandLineArgs = await GetCommandLineArguments(encodingJob).ConfigureAwait(false);
- var process = new Process
+ var process = ProcessFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
+ CreateNoWindow = true,
+ UseShellExecute = false,
- FileName = MediaEncoder.EncoderPath,
- Arguments = commandLineArgs,
+ // Must consume both stdout and stderr or deadlocks may occur
+ //RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ FileName = MediaEncoder.EncoderPath,
+ Arguments = commandLineArgs,
+ IsHidden = true,
+ ErrorDialog = false,
EnableRaisingEvents = true
- };
+ });
var workingDirectory = GetWorkingDirectory(options);
if (!string.IsNullOrWhiteSpace(workingDirectory))
@@ -110,7 +107,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
+ encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
await encodingJob.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationToken).ConfigureAwait(false);
@@ -147,7 +144,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return encodingJob;
}
- private void Cancel(Process process, EncodingJob job)
+ private void Cancel(IProcess process, EncodingJob job)
{
Logger.Info("Killing ffmpeg process for {0}", job.OutputFilePath);
@@ -162,7 +159,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// </summary>
/// <param name="process">The process.</param>
/// <param name="job">The job.</param>
- private void OnFfMpegProcessExited(Process process, EncodingJob job)
+ private void OnFfMpegProcessExited(IProcess process, EncodingJob job)
{
job.HasExited = true;
@@ -215,7 +212,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
try
{
- job.TaskCompletionSource.TrySetException(new ApplicationException("Encoding failed"));
+ job.TaskCompletionSource.TrySetException(new Exception("Encoding failed"));
}
catch
{
@@ -773,7 +770,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 7acff8fc8..b37e783b8 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Globalization;
+using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Logging;
namespace MediaBrowser.MediaEncoding.Encoder
@@ -8,10 +10,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
public class EncoderValidator
{
private readonly ILogger _logger;
+ private readonly IProcessFactory _processFactory;
- public EncoderValidator(ILogger logger)
+ public EncoderValidator(ILogger logger, IProcessFactory processFactory)
{
_logger = logger;
+ _processFactory = processFactory;
}
public Tuple<List<string>, List<string>> Validate(string encoderPath)
@@ -26,15 +30,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
return new Tuple<List<string>, List<string>>(decoders, encoders);
}
- public bool ValidateVersion(string encoderAppPath)
+ public bool ValidateVersion(string encoderAppPath, bool logOutput)
{
string output = string.Empty;
try
{
output = GetProcessOutput(encoderAppPath, "-version");
}
- catch
+ catch (Exception ex)
{
+ if (logOutput)
+ {
+ _logger.ErrorException("Error validating encoder", ex);
+ }
}
if (string.IsNullOrWhiteSpace(output))
@@ -42,11 +50,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
return false;
}
+ if (logOutput)
+ {
+ _logger.Info("ffmpeg info: {0}", output);
+ }
+
if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1)
{
return false;
}
+ output = " " + output + " ";
+
+ for (var i = 2013; i <= 2015; i++)
+ {
+ var yearString = i.ToString(CultureInfo.InvariantCulture);
+ if (output.IndexOf(" " + yearString + " ", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return false;
+ }
+ }
+
return true;
}
@@ -57,8 +81,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
output = GetProcessOutput(encoderAppPath, "-decoders");
}
- catch
+ catch (Exception )
{
+ //_logger.ErrorException("Error detecting available decoders", ex);
}
var found = new List<string>();
@@ -140,19 +165,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetProcessOutput(string path, string arguments)
{
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = path,
- Arguments = arguments,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false,
- RedirectStandardOutput = true
- }
- };
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = path,
+ Arguments = arguments,
+ IsHidden = true,
+ ErrorDialog = false,
+ RedirectStandardOutput = true
+ });
_logger.Info("Running {0} {1}", path, arguments);
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
index 0c7ff1b76..b5ff5cbb6 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Model.Net;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
index f811a8d48..e197bdb6f 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
@@ -606,9 +606,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
// vaapi will throw an error with this input
// [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99.
- if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase) && videoStream.Level == -99)
+ if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase))
{
- return false;
+ if (videoStream.Level == -99 || videoStream.Level == 15)
+ {
+ return false;
+ }
}
}
return true;
diff --git a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
index 71306e0ec..42048ab9e 100644
--- a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
@@ -3,9 +3,11 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
@@ -86,9 +88,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <returns>Task.</returns>
private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress)
{
- var existingFile = Directory
- .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories)
- .FirstOrDefault();
+ var existingFile = _fileSystem
+ .GetFilePaths(_appPaths.ProgramDataPath, true)
+ .FirstOrDefault(i => string.Equals(fontFilename, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase));
if (existingFile != null)
{
@@ -168,8 +170,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var bytes = Encoding.UTF8.GetBytes(contents);
- using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
- FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileOpenMode.Create, FileAccessMode.Write,
+ FileShareMode.Read, true))
{
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 340990373..23e63dad0 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -14,18 +14,17 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.System;
namespace MediaBrowser.MediaEncoding.Encoder
{
@@ -80,14 +79,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected readonly Func<IMediaSourceManager> MediaSourceManager;
private readonly IHttpClient _httpClient;
private readonly IZipClient _zipClient;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IProcessFactory _processFactory;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
private readonly bool _hasExternalEncoder;
- private string _originalFFMpegPath;
- private string _originalFFProbePath;
+ private readonly string _originalFFMpegPath;
+ private readonly string _originalFFProbePath;
+ private readonly int DefaultImageExtractionTimeoutMs;
+ private readonly bool EnableEncoderFontFile;
- public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamProvider memoryStreamProvider)
+ private readonly IEnvironmentInfo _environmentInfo;
+
+ public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory,
+ int defaultImageExtractionTimeoutMs,
+ bool enableEncoderFontFile, IEnvironmentInfo environmentInfo)
{
_logger = logger;
_jsonSerializer = jsonSerializer;
@@ -103,18 +109,80 @@ namespace MediaBrowser.MediaEncoding.Encoder
_httpClient = httpClient;
_zipClient = zipClient;
_memoryStreamProvider = memoryStreamProvider;
+ _processFactory = processFactory;
+ DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
+ EnableEncoderFontFile = enableEncoderFontFile;
+ _environmentInfo = environmentInfo;
FFProbePath = ffProbePath;
FFMpegPath = ffMpegPath;
_originalFFProbePath = ffProbePath;
_originalFFMpegPath = ffMpegPath;
_hasExternalEncoder = hasExternalEncoder;
+
+ SetEnvironmentVariable();
+ }
+
+ private readonly object _logLock = new object();
+ public void SetLogFilename(string name)
+ {
+ lock (_logLock)
+ {
+ try
+ {
+ _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=" + name + ":level=32");
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
+ }
+ }
+
+ public void ClearLogFilename()
+ {
+ lock (_logLock)
+ {
+ try
+ {
+ _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", null);
+ }
+ catch (Exception ex)
+ {
+ //_logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
+ }
+ }
+
+ private void SetEnvironmentVariable()
+ {
+ try
+ {
+ //_environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
+ try
+ {
+ //_environmentInfo.SetUserEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
}
public string EncoderLocationType
{
get
{
+ if (_hasExternalEncoder)
+ {
+ return "External";
+ }
+
if (string.IsNullOrWhiteSpace(FFMpegPath))
{
return null;
@@ -157,12 +225,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrWhiteSpace(FFMpegPath))
{
- var result = new EncoderValidator(_logger).Validate(FFMpegPath);
+ var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
SetAvailableDecoders(result.Item1);
SetAvailableEncoders(result.Item2);
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+ if (EnableEncoderFontFile)
{
var directory = Path.GetDirectoryName(FFMpegPath);
@@ -179,6 +247,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
ConfigureEncoderPaths();
+ if (_hasExternalEncoder)
+ {
+ LogPaths();
+ return;
+ }
+
// If the path was passed in, save it into config now.
var encodingOptions = GetEncodingOptions();
var appPath = encodingOptions.EncoderAppPath;
@@ -188,7 +262,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrWhiteSpace(valueToSave))
{
// if using system variable, don't save this.
- if (IsSystemInstalledPath(valueToSave))
+ if (IsSystemInstalledPath(valueToSave) || _hasExternalEncoder)
{
valueToSave = null;
}
@@ -203,6 +277,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
public async Task UpdateEncoderPath(string path, string pathType)
{
+ if (_hasExternalEncoder)
+ {
+ return;
+ }
+
Tuple<string, string> newPaths;
if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase))
@@ -218,7 +297,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
throw new ArgumentNullException("path");
}
- if (!File.Exists(path) && !Directory.Exists(path))
+ if (!FileSystem.FileExists(path) && !FileSystem.DirectoryExists(path))
{
throw new ResourceNotFoundException();
}
@@ -240,7 +319,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
path = newPaths.Item1;
- if (!ValidateVersion(path))
+ if (!ValidateVersion(path, true))
{
throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
}
@@ -252,13 +331,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
Init();
}
- private bool ValidateVersion(string path)
+ private bool ValidateVersion(string path, bool logOutput)
{
- return new EncoderValidator(_logger).ValidateVersion(path);
+ return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
}
private void ConfigureEncoderPaths()
{
+ if (_hasExternalEncoder)
+ {
+ return;
+ }
+
var appPath = GetEncodingOptions().EncoderAppPath;
if (string.IsNullOrWhiteSpace(appPath))
@@ -287,12 +371,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrWhiteSpace(appPath))
{
- if (Directory.Exists(appPath))
+ if (FileSystem.DirectoryExists(appPath))
{
return GetPathsFromDirectory(appPath);
}
- if (File.Exists(appPath))
+ if (FileSystem.FileExists(appPath))
{
return new Tuple<string, string>(appPath, GetProbePathFromEncoderPath(appPath));
}
@@ -306,7 +390,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
string encoderPath = null;
string probePath = null;
- if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath))
+ if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true))
{
encoderPath = _originalFFMpegPath;
probePath = _originalFFProbePath;
@@ -314,7 +398,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (string.IsNullOrWhiteSpace(encoderPath))
{
- if (ValidateVersion("ffmpeg") && ValidateVersion("ffprobe"))
+ if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false))
{
encoderPath = "ffmpeg";
probePath = "ffprobe";
@@ -328,16 +412,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
// Since we can't predict the file extension, first try directly within the folder
// If that doesn't pan out, then do a recursive search
- var files = Directory.GetFiles(path);
+ var files = FileSystem.GetFilePaths(path);
var excludeExtensions = new[] { ".c" };
var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
- if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath))
+ if (string.IsNullOrWhiteSpace(ffmpegPath) || !FileSystem.FileExists(ffmpegPath))
{
- files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
+ files = FileSystem.GetFilePaths(path, true);
ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
@@ -352,7 +436,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetProbePathFromEncoderPath(string appPath)
{
- return Directory.GetFiles(Path.GetDirectoryName(appPath))
+ return FileSystem.GetFilePaths(Path.GetDirectoryName(appPath))
.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
}
@@ -432,7 +516,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (request.AnalyzeDurationSections > 0)
{
analyzeDuration = "-analyzeduration " +
- (request.AnalyzeDurationSections*1000000).ToString(CultureInfo.InvariantCulture);
+ (request.AnalyzeDurationSections * 1000000).ToString(CultureInfo.InvariantCulture);
}
else
{
@@ -495,7 +579,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <param name="videoType">Type of the video.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{MediaInfoResult}.</returns>
- /// <exception cref="System.ApplicationException">ffprobe failed - streams and format are both null.</exception>
private async Task<MediaInfo> GetMediaInfoInternal(string inputPath,
string primaryPath,
MediaProtocol protocol,
@@ -509,31 +592,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format"
: "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format";
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
- RedirectStandardOutput = true,
- //RedirectStandardError = true,
- RedirectStandardInput = false,
- FileName = FFProbePath,
- Arguments = string.Format(args,
- probeSizeArgument, inputPath).Trim(),
+ CreateNoWindow = true,
+ UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
+ RedirectStandardOutput = true,
+ FileName = FFProbePath,
+ Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
+ IsHidden = true,
+ ErrorDialog = false,
EnableRaisingEvents = true
- };
+ });
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- using (var processWrapper = new ProcessWrapper(process, this, _logger, false))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -558,7 +634,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (result.streams == null && result.format == null)
{
- throw new ApplicationException("ffprobe failed - streams and format are both null.");
+ throw new Exception("ffprobe failed - streams and format are both null.");
}
if (result.streams != null)
@@ -595,7 +671,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
catch
{
- StopProcess(processWrapper, 100, true);
+ StopProcess(processWrapper, 100);
throw;
}
@@ -644,31 +720,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = "{0} -i {1} -map 0:v:{2} -an -filter:v idet -frames:v 500 -an -f null /dev/null";
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = false,
- FileName = FFMpegPath,
- Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(),
+ CreateNoWindow = true,
+ UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
+ RedirectStandardError = true,
+ FileName = FFMpegPath,
+ Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(),
+ IsHidden = true,
+ ErrorDialog = false,
EnableRaisingEvents = true
- };
+ });
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
var idetFoundInterlaced = false;
- using (var processWrapper = new ProcessWrapper(process, this, _logger, false))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
try
{
@@ -711,7 +781,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
catch
{
- StopProcess(processWrapper, 100, true);
+ StopProcess(processWrapper, 100);
throw;
}
@@ -864,7 +934,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
var tempExtractPath = Path.Combine(ConfigurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg");
- Directory.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
+ FileSystem.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
// apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
// This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar
@@ -898,7 +968,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
var enableThumbnail = !new List<string> { "wtv" }.Contains(container ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- var thumbnail = enableThumbnail ? ",thumbnail=30" : string.Empty;
+ var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty;
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
@@ -916,22 +986,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args;
}
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = FFMpegPath,
- Arguments = args,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- }
- };
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = FFMpegPath,
+ Arguments = args,
+ IsHidden = true,
+ ErrorDialog = false
+ });
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- using (var processWrapper = new ProcessWrapper(process, this, _logger, false))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -944,14 +1011,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
if (timeoutMs <= 0)
{
- timeoutMs = Environment.Is64BitOperatingSystem ? (Environment.ProcessorCount > 2 ? 14000 : 20000) : 40000;
+ timeoutMs = DefaultImageExtractionTimeoutMs;
}
ranToCompletion = process.WaitForExit(timeoutMs);
if (!ranToCompletion)
{
- StopProcess(processWrapper, 1000, false);
+ StopProcess(processWrapper, 1000);
}
}
@@ -961,7 +1028,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
- var file = new FileInfo(tempExtractPath);
+ var file = FileSystem.GetFileInfo(tempExtractPath);
if (exitCode == -1 || !file.Exists || file.Length == 0)
{
@@ -969,7 +1036,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.Error(msg);
- throw new ApplicationException(msg);
+ throw new Exception(msg);
}
return tempExtractPath;
@@ -1022,19 +1089,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
args = probeSize + " " + args;
}
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = FFMpegPath,
- Arguments = args,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false,
- RedirectStandardInput = true
- }
- };
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = FFMpegPath,
+ Arguments = args,
+ IsHidden = true,
+ ErrorDialog = false
+ });
_logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
@@ -1042,7 +1105,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
bool ranToCompletion = false;
- using (var processWrapper = new ProcessWrapper(process, this, _logger, true))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
try
{
@@ -1065,7 +1128,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
cancellationToken.ThrowIfCancellationRequested();
- var jpegCount = Directory.GetFiles(targetDirectory)
+ var jpegCount = FileSystem.GetFilePaths(targetDirectory)
.Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
isResponsive = (jpegCount > lastCount);
@@ -1074,7 +1137,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!ranToCompletion)
{
- StopProcess(processWrapper, 1000, false);
+ StopProcess(processWrapper, 1000);
}
}
finally
@@ -1090,7 +1153,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.Error(msg);
- throw new ApplicationException(msg);
+ throw new Exception(msg);
}
}
}
@@ -1107,7 +1170,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
LibraryManager,
SessionManager,
SubtitleEncoder(),
- MediaSourceManager())
+ MediaSourceManager(),
+ _processFactory)
.Start(options, progress, cancellationToken).ConfigureAwait(false);
await job.TaskCompletionSource.Task.ConfigureAwait(false);
@@ -1127,7 +1191,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
LibraryManager,
SessionManager,
SubtitleEncoder(),
- MediaSourceManager())
+ MediaSourceManager(),
+ _processFactory)
.Start(options, progress, cancellationToken).ConfigureAwait(false);
await job.TaskCompletionSource.Task.ConfigureAwait(false);
@@ -1144,40 +1209,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
_runningProcesses.Add(process);
}
}
- private void StopProcess(ProcessWrapper process, int waitTimeMs, bool enableForceKill)
+ private void StopProcess(ProcessWrapper process, int waitTimeMs)
{
try
{
- _logger.Info("Killing ffmpeg process");
-
- if (process.IsRedirectingStdin)
+ if (process.Process.WaitForExit(waitTimeMs))
{
- try
- {
- process.Process.StandardInput.WriteLine("q");
- }
- catch (Exception)
- {
- _logger.Error("Error sending q command to process");
- }
+ return;
}
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Error in WaitForExit", ex);
+ }
- try
- {
- if (process.Process.WaitForExit(waitTimeMs))
- {
- return;
- }
- }
- catch (Exception ex)
- {
- _logger.Error("Error in WaitForExit", ex);
- }
+ try
+ {
+ _logger.Info("Killing ffmpeg process");
- if (enableForceKill)
- {
- process.Process.Kill();
- }
+ process.Process.Kill();
}
catch (Exception ex)
{
@@ -1198,14 +1248,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
if (!process.HasExited)
{
- StopProcess(process, 500, true);
+ StopProcess(process, 500);
}
}
}
public string EscapeSubtitleFilterPath(string path)
{
- return path.Replace('\\', '/').Replace(":/", "\\:/").Replace("'", "'\\\\\\''");
+ // https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping
+ // We need to double escape
+
+ return path.Replace('\\', '/').Replace(":", "\\:").Replace("'", "'\\\\\\''");
}
/// <summary>
@@ -1231,25 +1284,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
private class ProcessWrapper : IDisposable
{
- public readonly Process Process;
+ public readonly IProcess Process;
public bool HasExited;
public int? ExitCode;
private readonly MediaEncoder _mediaEncoder;
private readonly ILogger _logger;
- public bool IsRedirectingStdin { get; private set; }
- public ProcessWrapper(Process process, MediaEncoder mediaEncoder, ILogger logger, bool isRedirectingStdin)
+ public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder, ILogger logger)
{
Process = process;
_mediaEncoder = mediaEncoder;
_logger = logger;
Process.Exited += Process_Exited;
- IsRedirectingStdin = isRedirectingStdin;
}
void Process_Exited(object sender, EventArgs e)
{
- var process = (Process)sender;
+ var process = (IProcess)sender;
HasExited = true;
@@ -1269,7 +1320,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
DisposeProcess(process);
}
- private void DisposeProcess(Process process)
+ private void DisposeProcess(IProcess process)
{
try
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
index 457fbe2c2..cbbca479a 100644
--- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
@@ -8,13 +8,13 @@ using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class VideoEncoder : BaseEncoder
{
- public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager)
+ public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
{
}
@@ -191,5 +191,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
get { return true; }
}
+
}
} \ No newline at end of file