Browse Source

Merge pull request #2680 from mark-monteiro/remove-common-process

Remove CommonProcess and ProcessFactory
Bond-009 5 năm trước cách đây
mục cha
commit
3d611743ed

+ 11 - 23
Emby.Server.Implementations/ApplicationHost.cs

@@ -30,7 +30,6 @@ using Emby.Server.Implementations.Configuration;
 using Emby.Server.Implementations.Cryptography;
 using Emby.Server.Implementations.Data;
 using Emby.Server.Implementations.Devices;
-using Emby.Server.Implementations.Diagnostics;
 using Emby.Server.Implementations.Dto;
 using Emby.Server.Implementations.HttpServer;
 using Emby.Server.Implementations.HttpServer.Security;
@@ -86,7 +85,6 @@ using MediaBrowser.MediaEncoding.BdInfo;
 using MediaBrowser.Model.Activity;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Globalization;
@@ -337,8 +335,6 @@ namespace Emby.Server.Implementations
 
         internal IImageEncoder ImageEncoder { get; private set; }
 
-        protected IProcessFactory ProcessFactory { get; private set; }
-
         protected readonly IXmlSerializer XmlSerializer;
 
         protected ISocketFactory SocketFactory { get; private set; }
@@ -680,9 +676,6 @@ namespace Emby.Server.Implementations
 
             serviceCollection.AddSingleton(XmlSerializer);
 
-            ProcessFactory = new ProcessFactory();
-            serviceCollection.AddSingleton(ProcessFactory);
-
             serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper));
 
             var cryptoProvider = new CryptographyProvider();
@@ -743,7 +736,6 @@ namespace Emby.Server.Implementations
                 LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(),
                 ServerConfigurationManager,
                 FileSystemManager,
-                ProcessFactory,
                 LocalizationManager,
                 () => SubtitleEncoder,
                 startupConfig,
@@ -857,8 +849,7 @@ namespace Emby.Server.Implementations
                 FileSystemManager,
                 MediaEncoder,
                 HttpClient,
-                MediaSourceManager,
-                ProcessFactory);
+                MediaSourceManager);
             serviceCollection.AddSingleton(SubtitleEncoder);
 
             serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
@@ -1678,15 +1669,17 @@ namespace Emby.Server.Implementations
                 throw new NotSupportedException();
             }
 
-            var process = ProcessFactory.Create(new ProcessOptions
+            var process = new Process
             {
-                FileName = url,
-                EnableRaisingEvents = true,
-                UseShellExecute = true,
-                ErrorDialog = false
-            });
-
-            process.Exited += ProcessExited;
+                StartInfo = new ProcessStartInfo
+                {
+                    FileName = url,
+                    UseShellExecute = true,
+                    ErrorDialog = false
+                },
+                EnableRaisingEvents = true
+            };
+            process.Exited += (sender, args) => ((Process)sender).Dispose();
 
             try
             {
@@ -1699,11 +1692,6 @@ namespace Emby.Server.Implementations
             }
         }
 
-        private static void ProcessExited(object sender, EventArgs e)
-        {
-            ((IProcess)sender).Dispose();
-        }
-
         public virtual void EnableLoopback(string appName)
         {
         }

+ 0 - 152
Emby.Server.Implementations/Diagnostics/CommonProcess.cs

@@ -1,152 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Diagnostics;
-
-namespace Emby.Server.Implementations.Diagnostics
-{
-    public class CommonProcess : IProcess
-    {
-        private readonly Process _process;
-
-        private bool _disposed = false;
-        private bool _hasExited;
-
-        public CommonProcess(ProcessOptions options)
-        {
-            StartInfo = options;
-
-            var startInfo = new ProcessStartInfo
-            {
-                Arguments = options.Arguments,
-                FileName = options.FileName,
-                WorkingDirectory = options.WorkingDirectory,
-                UseShellExecute = options.UseShellExecute,
-                CreateNoWindow = options.CreateNoWindow,
-                RedirectStandardError = options.RedirectStandardError,
-                RedirectStandardInput = options.RedirectStandardInput,
-                RedirectStandardOutput = options.RedirectStandardOutput,
-                ErrorDialog = options.ErrorDialog
-            };
-
-
-            if (options.IsHidden)
-            {
-                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
-            }
-
-            _process = new Process
-            {
-                StartInfo = startInfo
-            };
-
-            if (options.EnableRaisingEvents)
-            {
-                _process.EnableRaisingEvents = true;
-                _process.Exited += OnProcessExited;
-            }
-        }
-
-        public event EventHandler Exited;
-
-        public ProcessOptions StartInfo { get; }
-
-        public StreamWriter StandardInput => _process.StandardInput;
-
-        public StreamReader StandardError => _process.StandardError;
-
-        public StreamReader StandardOutput => _process.StandardOutput;
-
-        public int ExitCode => _process.ExitCode;
-
-        private bool HasExited
-        {
-            get
-            {
-                if (_hasExited)
-                {
-                    return true;
-                }
-
-                try
-                {
-                    _hasExited = _process.HasExited;
-                }
-                catch (InvalidOperationException)
-                {
-                    _hasExited = true;
-                }
-
-                return _hasExited;
-            }
-        }
-
-        public void Start()
-        {
-            _process.Start();
-        }
-
-        public void Kill()
-        {
-            _process.Kill();
-        }
-
-        public bool WaitForExit(int timeMs)
-        {
-            return _process.WaitForExit(timeMs);
-        }
-
-        public Task<bool> WaitForExitAsync(int timeMs)
-        {
-            // Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
-
-            if (HasExited)
-            {
-                return Task.FromResult(true);
-            }
-
-            timeMs = Math.Max(0, timeMs);
-
-            var tcs = new TaskCompletionSource<bool>();
-
-            var cancellationToken = new CancellationTokenSource(timeMs).Token;
-
-            _process.Exited += (sender, args) => tcs.TrySetResult(true);
-
-            cancellationToken.Register(() => tcs.TrySetResult(HasExited));
-
-            return tcs.Task;
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        protected virtual void Dispose(bool disposing)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            if (disposing)
-            {
-                _process?.Dispose();
-            }
-
-            _disposed = true;
-        }
-
-        private void OnProcessExited(object sender, EventArgs e)
-        {
-            _hasExited = true;
-            Exited?.Invoke(this, e);
-        }
-    }
-}

+ 0 - 14
Emby.Server.Implementations/Diagnostics/ProcessFactory.cs

@@ -1,14 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Diagnostics;
-
-namespace Emby.Server.Implementations.Diagnostics
-{
-    public class ProcessFactory : IProcessFactory
-    {
-        public IProcess Create(ProcessOptions options)
-        {
-            return new CommonProcess(options);
-        }
-    }
-}

+ 16 - 18
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -25,7 +26,6 @@ using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Events;
@@ -61,7 +61,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private readonly ILibraryManager _libraryManager;
         private readonly IProviderManager _providerManager;
         private readonly IMediaEncoder _mediaEncoder;
-        private readonly IProcessFactory _processFactory;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IStreamHelper _streamHelper;
 
@@ -88,8 +87,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             ILibraryManager libraryManager,
             ILibraryMonitor libraryMonitor,
             IProviderManager providerManager,
-            IMediaEncoder mediaEncoder,
-            IProcessFactory processFactory)
+            IMediaEncoder mediaEncoder)
         {
             Current = this;
 
@@ -102,7 +100,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _libraryMonitor = libraryMonitor;
             _providerManager = providerManager;
             _mediaEncoder = mediaEncoder;
-            _processFactory = processFactory;
             _liveTvManager = (LiveTvManager)liveTvManager;
             _jsonSerializer = jsonSerializer;
             _mediaSourceManager = mediaSourceManager;
@@ -1662,7 +1659,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         {
             if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
             {
-                return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config);
+                return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _config);
             }
 
             return new DirectRecorder(_logger, _httpClient, _streamHelper);
@@ -1683,16 +1680,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
             try
             {
-                var process = _processFactory.Create(new ProcessOptions
+                var process = new Process
                 {
-                    Arguments = GetPostProcessArguments(path, options.RecordingPostProcessorArguments),
-                    CreateNoWindow = true,
-                    EnableRaisingEvents = true,
-                    ErrorDialog = false,
-                    FileName = options.RecordingPostProcessor,
-                    IsHidden = true,
-                    UseShellExecute = false
-                });
+                    StartInfo = new ProcessStartInfo
+                    {
+                        Arguments = GetPostProcessArguments(path, options.RecordingPostProcessorArguments),
+                        CreateNoWindow = true,
+                        ErrorDialog = false,
+                        FileName = options.RecordingPostProcessor,
+                        WindowStyle = ProcessWindowStyle.Hidden,
+                        UseShellExecute = false
+                    },
+                    EnableRaisingEvents = true
+                };
 
                 _logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
@@ -1712,11 +1712,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
         private void Process_Exited(object sender, EventArgs e)
         {
-            using (var process = (IProcess)sender)
+            using (var process = (Process)sender)
             {
                 _logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode);
-
-                process.Dispose();
             }
         }
 

+ 37 - 35
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Text;
@@ -13,7 +14,6 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Serialization;
@@ -29,8 +29,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private bool _hasExited;
         private Stream _logFileStream;
         private string _targetPath;
-        private IProcess _process;
-        private readonly IProcessFactory _processFactory;
+        private Process _process;
         private readonly IJsonSerializer _json;
         private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
         private readonly IServerConfigurationManager _config;
@@ -40,14 +39,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             IMediaEncoder mediaEncoder,
             IServerApplicationPaths appPaths,
             IJsonSerializer json,
-            IProcessFactory processFactory,
             IServerConfigurationManager config)
         {
             _logger = logger;
             _mediaEncoder = mediaEncoder;
             _appPaths = appPaths;
             _json = json;
-            _processFactory = processFactory;
             _config = config;
         }
 
@@ -79,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _targetPath = targetFile;
             Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
 
-            var process = _processFactory.Create(new ProcessOptions
+            var processStartInfo = new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
@@ -90,14 +87,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 FileName = _mediaEncoder.EncoderPath,
                 Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
 
-                IsHidden = true,
-                ErrorDialog = false,
-                EnableRaisingEvents = true
-            });
-
-            _process = process;
+                WindowStyle = ProcessWindowStyle.Hidden,
+                ErrorDialog = false
+            };
 
-            var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
+            var commandLineLogMessage = processStartInfo.FileName + " " + processStartInfo.Arguments;
             _logger.LogInformation(commandLineLogMessage);
 
             var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
@@ -109,16 +103,21 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
             _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
 
-            process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
+            _process = new Process
+            {
+                StartInfo = processStartInfo,
+                EnableRaisingEvents = true
+            };
+            _process.Exited += (sender, args) => OnFfMpegProcessExited(_process, inputFile);
 
-            process.Start();
+            _process.Start();
 
             cancellationToken.Register(Stop);
 
             onStarted();
 
             // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
-            StartStreamingLog(process.StandardError.BaseStream, _logFileStream);
+            StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
 
             _logger.LogInformation("ffmpeg recording process started for {0}", _targetPath);
 
@@ -292,30 +291,33 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         /// <summary>
         /// Processes the exited.
         /// </summary>
-        private void OnFfMpegProcessExited(IProcess process, string inputFile)
+        private void OnFfMpegProcessExited(Process process, string inputFile)
         {
-            _hasExited = true;
+            using (process)
+            {
+                _hasExited = true;
 
-            _logFileStream?.Dispose();
-            _logFileStream = null;
+                _logFileStream?.Dispose();
+                _logFileStream = null;
 
-            var exitCode = process.ExitCode;
+                var exitCode = process.ExitCode;
 
-            _logger.LogInformation("FFMpeg recording exited with code {ExitCode} for {Path}", exitCode, _targetPath);
+                _logger.LogInformation("FFMpeg recording exited with code {ExitCode} for {Path}", exitCode, _targetPath);
 
-            if (exitCode == 0)
-            {
-                _taskCompletionSource.TrySetResult(true);
-            }
-            else
-            {
-                _taskCompletionSource.TrySetException(
-                    new Exception(
-                        string.Format(
-                            CultureInfo.InvariantCulture,
-                            "Recording for {0} failed. Exit code {1}",
-                            _targetPath,
-                            exitCode)));
+                if (exitCode == 0)
+                {
+                    _taskCompletionSource.TrySetResult(true);
+                }
+                else
+                {
+                    _taskCompletionSource.TrySetException(
+                        new Exception(
+                            string.Format(
+                                CultureInfo.InvariantCulture,
+                                "Recording for {0} failed. Exit code {1}",
+                                _targetPath,
+                                exitCode)));
+                }
             }
         }
 

+ 80 - 0
MediaBrowser.Common/Extensions/ProcessExtensions.cs

@@ -0,0 +1,80 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Extensions
+{
+    /// <summary>
+    /// Extension methods for <see cref="Process"/>.
+    /// </summary>
+    public static class ProcessExtensions
+    {
+        /// <summary>
+        /// Asynchronously wait for the process to exit.
+        /// </summary>
+        /// <param name="process">The process to wait for.</param>
+        /// <param name="timeout">The duration to wait before cancelling waiting for the task.</param>
+        /// <returns>True if the task exited normally, false if the timeout elapsed before the process exited.</returns>
+        /// <exception cref="InvalidOperationException">If <see cref="Process.EnableRaisingEvents"/> is not set to true for the process.</exception>
+        public static async Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout)
+        {
+            using (var cancelTokenSource = new CancellationTokenSource(timeout))
+            {
+                return await WaitForExitAsync(process, cancelTokenSource.Token).ConfigureAwait(false);
+            }
+        }
+
+        /// <summary>
+        /// Asynchronously wait for the process to exit.
+        /// </summary>
+        /// <param name="process">The process to wait for.</param>
+        /// <param name="cancelToken">A <see cref="CancellationToken"/> to observe while waiting for the process to exit.</param>
+        /// <returns>True if the task exited normally, false if cancelled before the process exited.</returns>
+        public static async Task<bool> WaitForExitAsync(this Process process, CancellationToken cancelToken)
+        {
+            if (!process.EnableRaisingEvents)
+            {
+                throw new InvalidOperationException("EnableRisingEvents must be enabled to async wait for a task to exit.");
+            }
+
+            // Add an event handler for the process exit event
+            var tcs = new TaskCompletionSource<bool>();
+            process.Exited += (sender, args) => tcs.TrySetResult(true);
+
+            // Return immediately if the process has already exited
+            if (process.HasExitedSafe())
+            {
+                return true;
+            }
+
+            // Register with the cancellation token then await
+            using (var cancelRegistration = cancelToken.Register(() => tcs.TrySetResult(process.HasExitedSafe())))
+            {
+                return await tcs.Task.ConfigureAwait(false);
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether the associated process has been terminated using
+        /// <see cref="Process.HasExited"/>. This is safe to call even if there is no operating system process
+        /// associated with the <see cref="Process"/>.
+        /// </summary>
+        /// <param name="process">The process to check the exit status for.</param>
+        /// <returns>
+        /// True if the operating system process referenced by the <see cref="Process"/> component has
+        /// terminated, or if there is no associated operating system process; otherwise, false.
+        /// </returns>
+        private static bool HasExitedSafe(this Process process)
+        {
+            try
+            {
+                return process.HasExited;
+            }
+            catch (InvalidOperationException)
+            {
+                return true;
+            }
+        }
+    }
+}

+ 31 - 34
MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs

@@ -155,47 +155,44 @@ namespace MediaBrowser.MediaEncoding.Attachments
                 inputPath,
                 attachmentStreamIndex,
                 outputPath);
-            var startInfo = new ProcessStartInfo
-            {
-                Arguments = processArgs,
-                FileName = _mediaEncoder.EncoderPath,
-                UseShellExecute = false,
-                CreateNoWindow = true,
-                WindowStyle = ProcessWindowStyle.Hidden,
-                ErrorDialog = false
-            };
-            var process = new Process
-            {
-                StartInfo = startInfo
-            };
 
-            _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
+            int exitCode;
 
-            process.Start();
+            using (var process = new Process
+                {
+                    StartInfo = new ProcessStartInfo
+                    {
+                        Arguments = processArgs,
+                        FileName = _mediaEncoder.EncoderPath,
+                        UseShellExecute = false,
+                        CreateNoWindow = true,
+                        WindowStyle = ProcessWindowStyle.Hidden,
+                        ErrorDialog = false
+                    },
+                    EnableRaisingEvents = true
+                })
+            {
+                _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
-            var processTcs = new TaskCompletionSource<bool>();
-            process.EnableRaisingEvents = true;
-            process.Exited += (sender, args) => processTcs.TrySetResult(true);
-            var unregister = cancellationToken.Register(() => processTcs.TrySetResult(process.HasExited));
-            var ranToCompletion = await processTcs.Task.ConfigureAwait(false);
-            unregister.Dispose();
+                process.Start();
 
-            if (!ranToCompletion)
-            {
-                try
-                {
-                    _logger.LogWarning("Killing ffmpeg attachment extraction process");
-                    process.Kill();
-                }
-                catch (Exception ex)
+                var ranToCompletion = await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
+
+                if (!ranToCompletion)
                 {
-                    _logger.LogError(ex, "Error killing attachment extraction process");
+                    try
+                    {
+                        _logger.LogWarning("Killing ffmpeg attachment extraction process");
+                        process.Kill();
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, "Error killing attachment extraction process");
+                    }
                 }
-            }
 
-            var exitCode = ranToCompletion ? process.ExitCode : -1;
-
-            process.Dispose();
+                exitCode = ranToCompletion ? process.ExitCode : -1;
+            }
 
             var failed = false;
 

+ 44 - 37
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -13,7 +13,6 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.MediaEncoding.Probing;
 using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
@@ -22,6 +21,7 @@ using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.System;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
+using System.Diagnostics;
 
 namespace MediaBrowser.MediaEncoding.Encoder
 {
@@ -38,7 +38,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
         private readonly ILogger _logger;
         private readonly IServerConfigurationManager _configurationManager;
         private readonly IFileSystem _fileSystem;
-        private readonly IProcessFactory _processFactory;
         private readonly ILocalizationManager _localization;
         private readonly Func<ISubtitleEncoder> _subtitleEncoder;
         private readonly IConfiguration _configuration;
@@ -58,7 +57,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             ILogger<MediaEncoder> logger,
             IServerConfigurationManager configurationManager,
             IFileSystem fileSystem,
-            IProcessFactory processFactory,
             ILocalizationManager localization,
             Func<ISubtitleEncoder> subtitleEncoder,
             IConfiguration configuration,
@@ -67,7 +65,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             _logger = logger;
             _configurationManager = configurationManager;
             _fileSystem = fileSystem;
-            _processFactory = processFactory;
             _localization = localization;
             _startupOptionFFmpegPath = startupOptionsFFmpegPath;
             _subtitleEncoder = subtitleEncoder;
@@ -362,30 +359,33 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 : "{0} -i {1} -threads 0 -v warning -print_format json -show_streams -show_format";
             args = string.Format(args, probeSizeArgument, inputPath).Trim();
 
-            var process = _processFactory.Create(new ProcessOptions
+            var process = new Process
             {
-                CreateNoWindow = true,
-                UseShellExecute = false,
+                StartInfo = new ProcessStartInfo
+                {
+                    CreateNoWindow = true,
+                    UseShellExecute = false,
 
-                // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
-                RedirectStandardOutput = true,
+                    // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
+                    RedirectStandardOutput = true,
 
-                FileName = _ffprobePath,
-                Arguments = args,
+                    FileName = _ffprobePath,
+                    Arguments = args,
 
 
-                IsHidden = true,
-                ErrorDialog = false,
+                    WindowStyle = ProcessWindowStyle.Hidden,
+                    ErrorDialog = false,
+                },
                 EnableRaisingEvents = true
-            });
+            };
 
             if (forceEnableLogging)
             {
-                _logger.LogInformation("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
+                _logger.LogInformation("{ProcessFileName} {ProcessArgs}", process.StartInfo.FileName, process.StartInfo.Arguments);
             }
             else
             {
-                _logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
+                _logger.LogDebug("{ProcessFileName} {ProcessArgs}", process.StartInfo.FileName, process.StartInfo.Arguments);
             }
 
             using (var processWrapper = new ProcessWrapper(process, this))
@@ -571,18 +571,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
-            var process = _processFactory.Create(new ProcessOptions
+            var process = new Process
             {
-                CreateNoWindow = true,
-                UseShellExecute = false,
-                FileName = _ffmpegPath,
-                Arguments = args,
-                IsHidden = true,
-                ErrorDialog = false,
+                StartInfo = new ProcessStartInfo
+                {
+                    CreateNoWindow = true,
+                    UseShellExecute = false,
+                    FileName = _ffmpegPath,
+                    Arguments = args,
+                    WindowStyle = ProcessWindowStyle.Hidden,
+                    ErrorDialog = false,
+                },
                 EnableRaisingEvents = true
-            });
+            };
 
-            _logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
+            _logger.LogDebug("{ProcessFileName} {ProcessArguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
             using (var processWrapper = new ProcessWrapper(process, this))
             {
@@ -599,7 +602,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                         timeoutMs = DefaultImageExtractionTimeout;
                     }
 
-                    ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
+                    ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
 
                     if (!ranToCompletion)
                     {
@@ -700,23 +703,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
-            var process = _processFactory.Create(new ProcessOptions
+            var processStartInfo = new ProcessStartInfo
             {
                 CreateNoWindow = true,
                 UseShellExecute = false,
                 FileName = _ffmpegPath,
                 Arguments = args,
-                IsHidden = true,
-                ErrorDialog = false,
-                EnableRaisingEvents = true
-            });
+                WindowStyle = ProcessWindowStyle.Hidden,
+                ErrorDialog = false
+            };
 
-            _logger.LogInformation(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
+            _logger.LogInformation(processStartInfo.FileName + " " + processStartInfo.Arguments);
 
             await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
 
             bool ranToCompletion = false;
 
+            var process = new Process
+            {
+                StartInfo = processStartInfo,
+                EnableRaisingEvents = true
+            };
             using (var processWrapper = new ProcessWrapper(process, this))
             {
                 try
@@ -732,7 +739,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                     while (isResponsive)
                     {
-                        if (await process.WaitForExitAsync(30000).ConfigureAwait(false))
+                        if (await process.WaitForExitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false))
                         {
                             ranToCompletion = true;
                             break;
@@ -949,14 +956,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             private bool _disposed = false;
 
-            public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder)
+            public ProcessWrapper(Process process, MediaEncoder mediaEncoder)
             {
                 Process = process;
                 _mediaEncoder = mediaEncoder;
                 Process.Exited += OnProcessExited;
             }
 
-            public IProcess Process { get; }
+            public Process Process { get; }
 
             public bool HasExited { get; private set; }
 
@@ -964,7 +971,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             void OnProcessExited(object sender, EventArgs e)
             {
-                var process = (IProcess)sender;
+                var process = (Process)sender;
 
                 HasExited = true;
 
@@ -979,7 +986,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 DisposeProcess(process);
             }
 
-            private void DisposeProcess(IProcess process)
+            private void DisposeProcess(Process process)
             {
                 lock (_mediaEncoder._runningProcessesLock)
                 {

+ 76 - 71
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Concurrent;
+using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -12,7 +13,6 @@ using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
@@ -31,7 +31,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IHttpClient _httpClient;
         private readonly IMediaSourceManager _mediaSourceManager;
-        private readonly IProcessFactory _processFactory;
 
         public SubtitleEncoder(
             ILibraryManager libraryManager,
@@ -40,8 +39,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             IFileSystem fileSystem,
             IMediaEncoder mediaEncoder,
             IHttpClient httpClient,
-            IMediaSourceManager mediaSourceManager,
-            IProcessFactory processFactory)
+            IMediaSourceManager mediaSourceManager)
         {
             _libraryManager = libraryManager;
             _logger = logger;
@@ -50,7 +48,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             _mediaEncoder = mediaEncoder;
             _httpClient = httpClient;
             _mediaSourceManager = mediaSourceManager;
-            _processFactory = processFactory;
         }
 
         private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles");
@@ -429,49 +426,53 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 encodingParam = " -sub_charenc " + encodingParam;
             }
 
-            var process = _processFactory.Create(new ProcessOptions
-            {
-                CreateNoWindow = true,
-                UseShellExecute = false,
-                FileName = _mediaEncoder.EncoderPath,
-                Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
-                EnableRaisingEvents = true,
-                IsHidden = true,
-                ErrorDialog = false
-            });
-
-            _logger.LogInformation("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
-
-            try
-            {
-                process.Start();
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error starting ffmpeg");
+            int exitCode;
 
-                throw;
-            }
-
-            var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
+            using (var process = new Process
+                {
+                    StartInfo = new ProcessStartInfo
+                    {
+                        CreateNoWindow = true,
+                        UseShellExecute = false,
+                        FileName = _mediaEncoder.EncoderPath,
+                        Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
+                        WindowStyle = ProcessWindowStyle.Hidden,
+                        ErrorDialog = false
+                    },
+                    EnableRaisingEvents = true
+                })
+            {
+                _logger.LogInformation("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
-            if (!ranToCompletion)
-            {
                 try
                 {
-                    _logger.LogInformation("Killing ffmpeg subtitle conversion process");
-
-                    process.Kill();
+                    process.Start();
                 }
                 catch (Exception ex)
                 {
-                    _logger.LogError(ex, "Error killing subtitle conversion process");
+                    _logger.LogError(ex, "Error starting ffmpeg");
+
+                    throw;
                 }
-            }
 
-            var exitCode = ranToCompletion ? process.ExitCode : -1;
+                var ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
+
+                if (!ranToCompletion)
+                {
+                    try
+                    {
+                        _logger.LogInformation("Killing ffmpeg subtitle conversion process");
 
-            process.Dispose();
+                        process.Kill();
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, "Error killing subtitle conversion process");
+                    }
+                }
+
+                exitCode = ranToCompletion ? process.ExitCode : -1;
+            }
 
             var failed = false;
 
@@ -578,49 +579,53 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 outputCodec,
                 outputPath);
 
-            var process = _processFactory.Create(new ProcessOptions
-            {
-                CreateNoWindow = true,
-                UseShellExecute = false,
-                EnableRaisingEvents = true,
-                FileName = _mediaEncoder.EncoderPath,
-                Arguments = processArgs,
-                IsHidden = true,
-                ErrorDialog = false
-            });
-
-            _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
-
-            try
-            {
-                process.Start();
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error starting ffmpeg");
+            int exitCode;
 
-                throw;
-            }
-
-            var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
+            using (var process = new Process
+                {
+                    StartInfo = new ProcessStartInfo
+                    {
+                        CreateNoWindow = true,
+                        UseShellExecute = false,
+                        FileName = _mediaEncoder.EncoderPath,
+                        Arguments = processArgs,
+                        WindowStyle = ProcessWindowStyle.Hidden,
+                        ErrorDialog = false
+                    },
+                    EnableRaisingEvents = true
+                })
+            {
+                _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
-            if (!ranToCompletion)
-            {
                 try
                 {
-                    _logger.LogWarning("Killing ffmpeg subtitle extraction process");
-
-                    process.Kill();
+                    process.Start();
                 }
                 catch (Exception ex)
                 {
-                    _logger.LogError(ex, "Error killing subtitle extraction process");
+                    _logger.LogError(ex, "Error starting ffmpeg");
+
+                    throw;
                 }
-            }
 
-            var exitCode = ranToCompletion ? process.ExitCode : -1;
+                var ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
+
+                if (!ranToCompletion)
+                {
+                    try
+                    {
+                        _logger.LogWarning("Killing ffmpeg subtitle extraction process");
 
-            process.Dispose();
+                        process.Kill();
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, "Error killing subtitle extraction process");
+                    }
+                }
+
+                exitCode = ranToCompletion ? process.ExitCode : -1;
+            }
 
             var failed = false;
 

+ 0 - 23
MediaBrowser.Model/Diagnostics/IProcess.cs

@@ -1,23 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Model.Diagnostics
-{
-    public interface IProcess : IDisposable
-    {
-        event EventHandler Exited;
-
-        void Kill();
-        bool WaitForExit(int timeMs);
-        Task<bool> WaitForExitAsync(int timeMs);
-        int ExitCode { get; }
-        void Start();
-        StreamWriter StandardInput { get; }
-        StreamReader StandardError { get; }
-        StreamReader StandardOutput { get; }
-        ProcessOptions StartInfo { get; }
-    }
-}

+ 0 - 24
MediaBrowser.Model/Diagnostics/IProcessFactory.cs

@@ -1,24 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Diagnostics
-{
-    public interface IProcessFactory
-    {
-        IProcess Create(ProcessOptions options);
-    }
-
-    public class ProcessOptions
-    {
-        public string FileName { get; set; }
-        public string Arguments { get; set; }
-        public string WorkingDirectory { get; set; }
-        public bool CreateNoWindow { get; set; }
-        public bool UseShellExecute { get; set; }
-        public bool EnableRaisingEvents { get; set; }
-        public bool ErrorDialog { get; set; }
-        public bool RedirectStandardError { get; set; }
-        public bool RedirectStandardInput { get; set; }
-        public bool RedirectStandardOutput { get; set; }
-        public bool IsHidden { get; set; }
-    }
-}