Ver Fonte

make media encoding project portable

Luke Pulverenti há 8 anos atrás
pai
commit
b1276dc208
23 ficheiros alterados com 449 adições e 219 exclusões
  1. 14 0
      Emby.Common.Implementations/BaseApplicationHost.cs
  2. 108 0
      Emby.Common.Implementations/Diagnostics/CommonProcess.cs
  3. 12 0
      Emby.Common.Implementations/Diagnostics/ProcessFactory.cs
  4. 39 0
      Emby.Common.Implementations/Threading/CommonTimer.cs
  5. 21 0
      Emby.Common.Implementations/Threading/TimerFactory.cs
  6. 2 0
      Emby.Common.Implementations/project.json
  7. 18 0
      Emby.Server.sln
  8. 3 4
      MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
  9. 18 23
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  10. 13 13
      MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
  11. 3 3
      MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
  12. 70 78
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  13. 3 4
      MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
  14. 4 11
      MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
  15. 6 0
      MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets
  16. 9 6
      MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
  17. 43 68
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  18. 17 0
      MediaBrowser.MediaEncoding/project.json
  19. 1 4
      MediaBrowser.Model/Diagnostics/IProcessFactory.cs
  20. 4 1
      MediaBrowser.Model/TextEncoding/IEncoding.cs
  21. 3 0
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  22. 32 1
      MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs
  23. 6 3
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs

+ 14 - 0
Emby.Common.Implementations/BaseApplicationHost.cs

@@ -27,11 +27,16 @@ using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Extensions;
 using Emby.Common.Implementations.Cryptography;
+using Emby.Common.Implementations.Diagnostics;
+using Emby.Common.Implementations.Threading;
 using MediaBrowser.Common;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Threading;
+
 #if NETSTANDARD1_6
 using System.Runtime.Loader;
 #endif
@@ -146,6 +151,9 @@ namespace Emby.Common.Implementations
 
         protected ISystemEvents SystemEvents { get; private set; }
 
+        protected IProcessFactory ProcessFactory { get; private set; }
+        protected ITimerFactory TimerFactory { get; private set; }
+
         /// <summary>
         /// Gets the name.
         /// </summary>
@@ -535,6 +543,12 @@ return null;
             IsoManager = new IsoManager();
             RegisterSingleInstance(IsoManager);
 
+            ProcessFactory = new ProcessFactory();
+            RegisterSingleInstance(ProcessFactory);
+
+            TimerFactory = new TimerFactory();
+            RegisterSingleInstance(TimerFactory);
+
             return Task.FromResult(true);
         }
 

+ 108 - 0
Emby.Common.Implementations/Diagnostics/CommonProcess.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Diagnostics;
+
+namespace Emby.Common.Implementations.Diagnostics
+{
+    public class CommonProcess : IProcess
+    {
+        public event EventHandler Exited;
+
+        private readonly ProcessOptions _options;
+        private readonly Process _process;
+
+        public CommonProcess(ProcessOptions options)
+        {
+            _options = 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
+            };
+
+#if NET46
+            startInfo.ErrorDialog = options.ErrorDialog;
+
+            if (options.IsHidden)
+            {
+                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+            }
+#endif    
+
+            _process = new Process
+            {
+                StartInfo = startInfo
+            };
+
+            if (options.EnableRaisingEvents)
+            {
+                _process.EnableRaisingEvents = true;
+                _process.Exited += _process_Exited;
+            }
+        }
+
+        private void _process_Exited(object sender, EventArgs e)
+        {
+            if (Exited != null)
+            {
+                Exited(_process, e);
+            }
+        }
+
+        public ProcessOptions StartInfo
+        {
+            get { return _options; }
+        }
+
+        public StreamWriter StandardInput
+        {
+            get { return _process.StandardInput; }
+        }
+
+        public StreamReader StandardError
+        {
+            get { return _process.StandardError; }
+        }
+
+        public StreamReader StandardOutput
+        {
+            get { return _process.StandardOutput; }
+        }
+
+        public int ExitCode
+        {
+            get { return _process.ExitCode; }
+        }
+
+        public void Start()
+        {
+            _process.Start();
+        }
+
+        public void Kill()
+        {
+            _process.Kill();
+        }
+
+        public bool WaitForExit(int timeMs)
+        {
+            return _process.WaitForExit(timeMs);
+        }
+
+        public void Dispose()
+        {
+            _process.Dispose();
+        }
+    }
+}

+ 12 - 0
Emby.Common.Implementations/Diagnostics/ProcessFactory.cs

@@ -0,0 +1,12 @@
+using MediaBrowser.Model.Diagnostics;
+
+namespace Emby.Common.Implementations.Diagnostics
+{
+    public class ProcessFactory : IProcessFactory
+    {
+        public IProcess Create(ProcessOptions options)
+        {
+            return new CommonProcess(options);
+        }
+    }
+}

+ 39 - 0
Emby.Common.Implementations/Threading/CommonTimer.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
+
+namespace Emby.Common.Implementations.Threading
+{
+    public class CommonTimer : ITimer
+    {
+        private readonly Timer _timer;
+
+        public CommonTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+        {
+            _timer = new Timer(new TimerCallback(callback), state, dueTime, period);
+        }
+
+        public CommonTimer(Action<object> callback, object state, int dueTimeMs, int periodMs)
+        {
+            _timer = new Timer(new TimerCallback(callback), state, dueTimeMs, periodMs);
+        }
+
+        public void Change(TimeSpan dueTime, TimeSpan period)
+        {
+            _timer.Change(dueTime, period);
+        }
+
+        public void Change(int dueTimeMs, int periodMs)
+        {
+            _timer.Change(dueTimeMs, periodMs);
+        }
+
+        public void Dispose()
+        {
+            _timer.Dispose();
+        }
+    }
+}

+ 21 - 0
Emby.Common.Implementations/Threading/TimerFactory.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
+
+namespace Emby.Common.Implementations.Threading
+{
+    public class TimerFactory : ITimerFactory
+    {
+        public ITimer Create(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+        {
+            return new CommonTimer(callback, state, dueTime, period);
+        }
+
+        public ITimer Create(Action<object> callback, object state, int dueTimeMs, int periodMs)
+        {
+            return new CommonTimer(callback, state, dueTimeMs, periodMs);
+        }
+    }
+}

+ 2 - 0
Emby.Common.Implementations/project.json

@@ -44,6 +44,8 @@
           "target": "project"
         },
         "System.IO.FileSystem.DriveInfo": "4.0.0",
+		"System.Diagnostics.Process": "4.1.0",
+		"System.Threading.Timer": "4.0.1",
 		"System.Net.Requests": "4.0.11",
 		"System.Xml.XmlSerializer": "4.0.11",
 		"System.Net.Http": "4.1.0",

+ 18 - 0
Emby.Server.sln

@@ -42,6 +42,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Dlna", "Emby.Dlna\Emby
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -145,6 +149,18 @@ Global
 		{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Any CPU.Build.0 = Release|Any CPU
 		{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+		{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+		{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.Build.0 = Release|Any CPU
+		{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+		{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+		{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -166,5 +182,7 @@ Global
 		{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
 		{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
 		{89AB4548-770D-41FD-A891-8DAFF44F452C} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+		{4FD51AC5-2C16-4308-A993-C3A84F3B4582} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+		{0BD82FA6-EB8A-4452-8AF5-74F9C3849451} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
 	EndGlobalSection
 EndGlobal

+ 3 - 4
MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs

@@ -7,15 +7,13 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
+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)
         {
         }
 
@@ -116,5 +114,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
         {
             get { return false; }
         }
+
     }
 }

+ 18 - 23
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -11,15 +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 MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.MediaEncoding.Encoder
@@ -35,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");
 
@@ -46,7 +44,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             ILibraryManager libraryManager,
             ISessionManager sessionManager,
             ISubtitleEncoder subtitleEncoder,
-            IMediaSourceManager mediaSourceManager)
+            IMediaSourceManager mediaSourceManager, IProcessFactory processFactory)
         {
             MediaEncoder = mediaEncoder;
             Logger = logger;
@@ -57,6 +55,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             SessionManager = sessionManager;
             SubtitleEncoder = subtitleEncoder;
             MediaSourceManager = mediaSourceManager;
+            ProcessFactory = processFactory;
         }
 
         public async Task<EncodingJob> Start(EncodingJobOptions options,
@@ -75,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))
@@ -149,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);
 
@@ -164,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;
 

+ 13 - 13
MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Logging;
 
 namespace MediaBrowser.MediaEncoding.Encoder
@@ -8,10 +9,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)
@@ -145,19 +148,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);
 

+ 3 - 3
MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs

@@ -88,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)
             {

+ 70 - 78
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -14,19 +14,16 @@ 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 MediaBrowser.Model.IO;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Diagnostics;
 
 namespace MediaBrowser.MediaEncoding.Encoder
 {
@@ -81,14 +78,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
         protected readonly Func<IMediaSourceManager> MediaSourceManager;
         private readonly IHttpClient _httpClient;
         private readonly IZipClient _zipClient;
+        private readonly IProcessFactory _processFactory;
         private readonly IMemoryStreamProvider _memoryStreamProvider;
 
         private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
         private readonly bool _hasExternalEncoder;
-        private string _originalFFMpegPath;
-        private string _originalFFProbePath;
-
-        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 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, IProcessFactory processFactory, 
+            int defaultImageExtractionTimeoutMs, 
+            bool enableEncoderFontFile)
         {
             _logger = logger;
             _jsonSerializer = jsonSerializer;
@@ -104,6 +106,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
             _httpClient = httpClient;
             _zipClient = zipClient;
             _memoryStreamProvider = memoryStreamProvider;
+            _processFactory = processFactory;
+            DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
+            EnableEncoderFontFile = enableEncoderFontFile;
             FFProbePath = ffProbePath;
             FFMpegPath = ffMpegPath;
             _originalFFProbePath = ffProbePath;
@@ -158,12 +163,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);
 
@@ -255,7 +260,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
         private bool ValidateVersion(string path, bool logOutput)
         {
-            return new EncoderValidator(_logger).ValidateVersion(path, logOutput);
+            return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
         }
 
         private void ConfigureEncoderPaths()
@@ -509,27 +514,22 @@ 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(),
-
-                    WindowStyle = ProcessWindowStyle.Hidden,
-                    ErrorDialog = false
-                },
-
+                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(),
+
+                IsHidden = true,
+                ErrorDialog = false,
                 EnableRaisingEvents = true
-            };
+            });
 
             _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
@@ -644,26 +644,22 @@ 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(),
-
-                    WindowStyle = ProcessWindowStyle.Hidden,
-                    ErrorDialog = false
-                },
-
+                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(),
+
+                IsHidden = true,
+                ErrorDialog = false,
                 EnableRaisingEvents = true
-            };
+            });
 
             _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
             var idetFoundInterlaced = false;
@@ -916,18 +912,15 @@ 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);
 
@@ -944,7 +937,7 @@ 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);
@@ -1022,19 +1015,16 @@ 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,
+                RedirectStandardInput = true
+            });
 
             _logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
 
@@ -1107,7 +1097,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 +1118,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 LibraryManager,
                 SessionManager,
                 SubtitleEncoder(),
-                MediaSourceManager())
+                MediaSourceManager(),
+                _processFactory)
                 .Start(options, progress, cancellationToken).ConfigureAwait(false);
 
             await job.TaskCompletionSource.Task.ConfigureAwait(false);
@@ -1231,14 +1223,14 @@ 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, bool isRedirectingStdin)
             {
                 Process = process;
                 _mediaEncoder = mediaEncoder;
@@ -1249,7 +1241,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             void Process_Exited(object sender, EventArgs e)
             {
-                var process = (Process)sender;
+                var process = (IProcess)sender;
 
                 HasExited = true;
 
@@ -1269,7 +1261,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 DisposeProcess(process);
             }
 
-            private void DisposeProcess(Process process)
+            private void DisposeProcess(IProcess process)
             {
                 try
                 {

+ 3 - 4
MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs

@@ -8,15 +8,13 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.IO;
 using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
+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)
         {
         }
 
@@ -193,5 +191,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
         {
             get { return true; }
         }
+
     }
 }

+ 4 - 11
MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj

@@ -9,10 +9,10 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>MediaBrowser.MediaEncoding</RootNamespace>
     <AssemblyName>MediaBrowser.MediaEncoding</AssemblyName>
-    <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
-    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
-    <TargetFrameworkProfile />
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -37,13 +37,6 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Data.DataSetExtensions" />
-    <Reference Include="Microsoft.CSharp" />
-    <Reference Include="System.Data" />
-    <Reference Include="System.Xml" />
     <Reference Include="UniversalDetector, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\packages\UniversalDetector.1.0.1\lib\portable-net45+sl4+wp71+win8+wpa81\UniversalDetector.dll</HintPath>
       <Private>True</Private>
@@ -108,7 +101,7 @@
   <ItemGroup>
     <None Include="packages.config" />
   </ItemGroup>
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">

+ 6 - 0
MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Target Name="EmitMSBuildWarning" BeforeTargets="Build">
+    <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
+  </Target>
+</Project>

+ 9 - 6
MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs

@@ -14,6 +14,7 @@ using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Subtitles;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Serialization;
@@ -31,14 +32,16 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         private readonly IEncryptionManager _encryption;
 
         private readonly IJsonSerializer _json;
+        private readonly IFileSystem _fileSystem;
 
-        public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json)
+        public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json, IFileSystem fileSystem)
         {
             _logger = logManager.GetLogger(GetType().Name);
             _httpClient = httpClient;
             _config = config;
             _encryption = encryption;
             _json = json;
+            _fileSystem = fileSystem;
 
             _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
 
@@ -133,7 +136,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
             if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1)
             {
-                throw new ApplicationException("OpenSubtitles rate limit reached");
+                throw new Exception("OpenSubtitles rate limit reached");
             }
 
             var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false);
@@ -141,12 +144,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1)
             {
                 _lastRateLimitException = DateTime.UtcNow;
-                throw new ApplicationException("OpenSubtitles rate limit reached");
+                throw new Exception("OpenSubtitles rate limit reached");
             }
 
             if (!(resultDownLoad is MethodResponseSubtitleDownload))
             {
-                throw new ApplicationException("Invalid response type");
+                throw new Exception("Invalid response type");
             }
 
             var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results;
@@ -269,11 +272,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             var subLanguageId = NormalizeLanguage(request.Language);
             string hash;
 
-            using (var fileStream = File.OpenRead(request.MediaPath))
+            using (var fileStream = _fileSystem.OpenRead(request.MediaPath))
             {
                 hash = Utilities.ComputeHash(fileStream);
             }
-            var fileInfo = new FileInfo(request.MediaPath);
+            var fileInfo = _fileSystem.GetFileInfo(request.MediaPath);
             var movieByteSize = fileInfo.Length;
             var searchImdbId = request.ContentType == VideoContentType.Movie ? imdbId.ToString(_usCulture) : "";
             var subtitleSearchParameters = request.ContentType == VideoContentType.Episode

+ 43 - 68
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -18,9 +18,8 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.IO;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.TextEncoding;
 using UniversalDetector;
 
 namespace MediaBrowser.MediaEncoding.Subtitles
@@ -36,8 +35,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         private readonly IHttpClient _httpClient;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IMemoryStreamProvider _memoryStreamProvider;
+        private readonly IProcessFactory _processFactory;
+        private readonly IEncoding _textEncoding;
 
-        public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamProvider memoryStreamProvider)
+        public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamProvider memoryStreamProvider, IProcessFactory processFactory, IEncoding textEncoding)
         {
             _libraryManager = libraryManager;
             _logger = logger;
@@ -48,6 +49,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             _httpClient = httpClient;
             _mediaSourceManager = mediaSourceManager;
             _memoryStreamProvider = memoryStreamProvider;
+            _processFactory = processFactory;
+            _textEncoding = textEncoding;
         }
 
         private string SubtitleCachePath
@@ -418,7 +421,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         /// or
         /// outputPath
         /// </exception>
-        /// <exception cref="System.ApplicationException"></exception>
         private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
         {
             if (string.IsNullOrEmpty(inputPath))
@@ -440,23 +442,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 encodingParam = " -sub_charenc " + encodingParam;
             }
 
-            var process = new Process
+            var process = _processFactory.Create(new ProcessOptions
             {
-                StartInfo = new ProcessStartInfo
-                {
-                    RedirectStandardOutput = false,
-                    RedirectStandardError = true,
-                    RedirectStandardInput = true,
+                RedirectStandardOutput = false,
+                RedirectStandardError = true,
+                RedirectStandardInput = true,
 
-                    CreateNoWindow = true,
-                    UseShellExecute = false,
-                    FileName = _mediaEncoder.EncoderPath,
-                    Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
+                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
-                }
-            };
+                IsHidden = true,
+                ErrorDialog = false
+            });
 
             _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
@@ -538,7 +537,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
                 _logger.Error(msg);
 
-                throw new ApplicationException(msg);
+                throw new Exception(msg);
             }
             await SetAssFont(outputPath).ConfigureAwait(false);
         }
@@ -593,23 +592,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath,
                 subtitleStreamIndex, outputCodec, outputPath);
 
-            var process = new Process
+            var process = _processFactory.Create(new ProcessOptions
             {
-                StartInfo = new ProcessStartInfo
-                {
-                    CreateNoWindow = true,
-                    UseShellExecute = false,
+                CreateNoWindow = true,
+                UseShellExecute = false,
 
-                    RedirectStandardOutput = false,
-                    RedirectStandardError = true,
-                    RedirectStandardInput = true,
+                RedirectStandardOutput = false,
+                RedirectStandardError = true,
+                RedirectStandardInput = true,
 
-                    FileName = _mediaEncoder.EncoderPath,
-                    Arguments = processArgs,
-                    WindowStyle = ProcessWindowStyle.Hidden,
-                    ErrorDialog = false
-                }
-            };
+                FileName = _mediaEncoder.EncoderPath,
+                Arguments = processArgs,
+                IsHidden = true,
+                ErrorDialog = false
+            });
 
             _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
@@ -674,10 +670,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 catch (FileNotFoundException)
                 {
 
-                }
-                catch (DirectoryNotFoundException)
-                {
-
                 }
                 catch (IOException ex)
                 {
@@ -695,7 +687,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
                 _logger.Error(msg);
 
-                throw new ApplicationException(msg);
+                throw new Exception(msg);
             }
             else
             {
@@ -749,20 +741,26 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             string text;
             Encoding encoding;
 
-            using (var reader = new StreamReader(file, true))
+            using (var fileStream = _fileSystem.OpenRead(file))
             {
-                encoding = reader.CurrentEncoding;
+                using (var reader = new StreamReader(fileStream, true))
+                {
+                    encoding = reader.CurrentEncoding;
 
-                text = await reader.ReadToEndAsync().ConfigureAwait(false);
+                    text = await reader.ReadToEndAsync().ConfigureAwait(false);
+                }
             }
 
             var newText = text.Replace(",Arial,", ",Arial Unicode MS,");
 
             if (!string.Equals(text, newText))
             {
-                using (var writer = new StreamWriter(file, false, encoding))
+                using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
                 {
-                    writer.Write(newText);
+                    using (var writer = new StreamWriter(fileStream, encoding))
+                    {
+                        writer.Write(newText);
+                    }
                 }
             }
         }
@@ -795,7 +793,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         {
             if (protocol == MediaProtocol.File)
             {
-                if (GetFileEncoding(path).Equals(Encoding.UTF8))
+                if (_textEncoding.GetFileEncoding(path).Equals(Encoding.UTF8))
                 {
                     return string.Empty;
                 }
@@ -902,29 +900,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             return null;
         }
 
-        private Encoding GetFileEncoding(string srcFile)
-        {
-            // *** Detect byte order mark if any - otherwise assume default
-            var buffer = new byte[5];
-
-            using (var file = _fileSystem.GetFileStream(srcFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite))
-            {
-                file.Read(buffer, 0, 5);
-            }
-
-            if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
-                return Encoding.UTF8;
-            if (buffer[0] == 0xfe && buffer[1] == 0xff)
-                return Encoding.Unicode;
-            if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
-                return Encoding.UTF32;
-            if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
-                return Encoding.UTF7;
-
-            // It's ok - anything aside from utf is ok since that's what we're looking for
-            return Encoding.Default;
-        }
-
         private async Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken)
         {
             if (protocol == MediaProtocol.Http)

+ 17 - 0
MediaBrowser.MediaEncoding/project.json

@@ -0,0 +1,17 @@
+{
+    "frameworks":{
+        "netstandard1.6":{
+           "dependencies":{
+                "NETStandard.Library":"1.6.0",
+            }
+        },
+        ".NETPortable,Version=v4.5,Profile=Profile7":{
+            "buildOptions": {
+                "define": [  ]
+            },
+            "frameworkAssemblies":{
+                
+            }
+        }
+    }
+}

+ 1 - 4
MediaBrowser.Model/Diagnostics/IProcessFactory.cs

@@ -1,8 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Model.Diagnostics
 {
@@ -22,6 +18,7 @@ namespace MediaBrowser.Model.Diagnostics
         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; }
     }
 }

+ 4 - 1
MediaBrowser.Model/TextEncoding/IEncoding.cs

@@ -1,9 +1,12 @@
-
+using System.Text;
+
 namespace MediaBrowser.Model.TextEncoding
 {
     public interface IEncoding
     {
         byte[] GetASCIIBytes(string text);
         string GetASCIIString(byte[] bytes, int startIndex, int length);
+
+        Encoding GetFileEncoding(string path);
     }
 }

+ 3 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -70,6 +70,9 @@
     <Reference Include="ServiceStack.Api.Swagger">
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath>
     </Reference>
+    <Reference Include="ServiceStack.Common">
+      <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Common.dll</HintPath>
+    </Reference>
     <Reference Include="SharpCompress, Version=0.10.3.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>

+ 32 - 1
MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs

@@ -1,10 +1,18 @@
 using System.Text;
+using MediaBrowser.Model.IO;
 using MediaBrowser.Model.TextEncoding;
 
 namespace MediaBrowser.Server.Implementations.TextEncoding
 {
-    public  class TextEncoding : IEncoding
+    public class TextEncoding : IEncoding
     {
+        private readonly IFileSystem _fileSystem;
+
+        public TextEncoding(IFileSystem fileSystem)
+        {
+            _fileSystem = fileSystem;
+        }
+
         public byte[] GetASCIIBytes(string text)
         {
             return Encoding.ASCII.GetBytes(text);
@@ -14,5 +22,28 @@ namespace MediaBrowser.Server.Implementations.TextEncoding
         {
             return Encoding.ASCII.GetString(bytes, 0, bytes.Length);
         }
+
+        public Encoding GetFileEncoding(string srcFile)
+        {
+            // *** Detect byte order mark if any - otherwise assume default
+            var buffer = new byte[5];
+
+            using (var file = _fileSystem.OpenRead(srcFile))
+            {
+                file.Read(buffer, 0, 5);
+            }
+
+            if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
+                return Encoding.UTF8;
+            if (buffer[0] == 0xfe && buffer[1] == 0xff)
+                return Encoding.Unicode;
+            if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
+                return Encoding.UTF32;
+            if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
+                return Encoding.UTF7;
+
+            // It's ok - anything aside from utf is ok since that's what we're looking for
+            return Encoding.Default;
+        }
     }
 }

+ 6 - 3
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -547,7 +547,7 @@ namespace MediaBrowser.Server.Startup.Common
             StringExtensions.LocalizationManager = LocalizationManager;
             RegisterSingleInstance(LocalizationManager);
 
-            IEncoding textEncoding = new TextEncoding();
+            IEncoding textEncoding = new TextEncoding(FileSystemManager);
             RegisterSingleInstance(textEncoding);
             Utilities.EncodingHelper = textEncoding;
             RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer(FileSystemManager, textEncoding));
@@ -697,7 +697,7 @@ namespace MediaBrowser.Server.Startup.Common
             RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
             RegisterSingleInstance<IAuthService>(new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager));
 
-            SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamProvider);
+            SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamProvider, ProcessFactory, textEncoding);
             RegisterSingleInstance(SubtitleEncoder);
 
             await displayPreferencesRepo.Initialize().ConfigureAwait(false);
@@ -789,7 +789,10 @@ namespace MediaBrowser.Server.Startup.Common
                 () => SubtitleEncoder,
                 () => MediaSourceManager,
                 HttpClient,
-                ZipClient, MemoryStreamProvider);
+                ZipClient, MemoryStreamProvider,
+                ProcessFactory,
+                Environment.Is64BitOperatingSystem ? (Environment.ProcessorCount > 2 ? 14000 : 20000) : 40000,
+                Environment.OSVersion.Platform == PlatformID.Win32NT);
 
             MediaEncoder = mediaEncoder;
             RegisterSingleInstance(MediaEncoder);