diff options
author | sr55 <[email protected]> | 2020-06-05 14:08:15 +0100 |
---|---|---|
committer | sr55 <[email protected]> | 2020-06-05 14:08:28 +0100 |
commit | afc7e2a2c94bc81b425063361e87e9351c16dd73 (patch) | |
tree | 92c3f343d2e23068d929a42c879fc82b0c7bf94e /win | |
parent | f61c986d9a5a706b7bbc96c79f5eba209dd18ca5 (diff) |
WinGui: UI performance improvement when starting multiple jobs. #2912
Diffstat (limited to 'win')
9 files changed, 144 insertions, 60 deletions
diff --git a/win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs b/win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs index 51ac11458..1017ba1e1 100644 --- a/win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs +++ b/win/CS/HandBrakeWPF/Instance/HandBrakeInstanceManager.cs @@ -37,7 +37,7 @@ namespace HandBrakeWPF.Instance HandBrakeUtils.EnsureGlobalInit(noHardwareMode); } - public static IEncodeInstance GetEncodeInstance(int verbosity, HBConfiguration configuration, ILog logService, IUserSettingService userSettingService) + public static IEncodeInstance GetEncodeInstance(int verbosity, HBConfiguration configuration, ILog logService, IUserSettingService userSettingService, IPortService portService) { if (!HandBrakeUtils.IsInitialised()) { @@ -48,7 +48,7 @@ namespace HandBrakeWPF.Instance if (userSettingService.GetUserSetting<bool>(UserSettingConstants.ProcessIsolationEnabled) && Portable.IsProcessIsolationEnabled()) { - newInstance = new RemoteInstance(configuration, logService, userSettingService); + newInstance = new RemoteInstance(logService, userSettingService, portService); } else { diff --git a/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs b/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs index 97abf671b..757878a66 100644 --- a/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs +++ b/win/CS/HandBrakeWPF/Instance/RemoteInstance.cs @@ -11,39 +11,33 @@ namespace HandBrakeWPF.Instance { using System; - using System.Collections.Generic; using System.Diagnostics; using System.IO; - using System.Linq; - using System.Net; - using System.Net.NetworkInformation; using System.Text; + using System.Threading; using System.Threading.Tasks; - using System.Timers; - using System.Windows.Media.Animation; - using HandBrake.Interop.Interop.EventArgs; using HandBrake.Interop.Interop.Interfaces; using HandBrake.Interop.Interop.Json.Encode; using HandBrake.Interop.Interop.Json.State; - using HandBrake.Interop.Model; using HandBrake.Worker.Routing.Commands; using HandBrakeWPF.Instance.Model; using HandBrakeWPF.Model.Options; - using HandBrakeWPF.Services; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Services.Logging.Interfaces; using HandBrakeWPF.Utilities; using Newtonsoft.Json; + using Timer = System.Timers.Timer; + public class RemoteInstance : HttpRequestBase, IEncodeInstance, IDisposable { - private readonly HBConfiguration configuration; private readonly ILog logService; private readonly IUserSettingService userSettingService; + private readonly IPortService portService; private const double EncodePollIntervalMs = 500; @@ -51,13 +45,11 @@ namespace HandBrakeWPF.Instance private Timer encodePollTimer; private int retryCount = 0; - public RemoteInstance(HBConfiguration configuration, ILog logService, IUserSettingService userSettingService) + public RemoteInstance(ILog logService, IUserSettingService userSettingService, IPortService portService) { - this.configuration = configuration; this.logService = logService; this.userSettingService = userSettingService; - this.port = this.GetOpenPort(userSettingService.GetUserSetting<int>(UserSettingConstants.ProcessIsolationPort)); - this.serverUrl = string.Format("http://127.0.0.1:{0}/", this.port); + this.portService = portService; } public event EventHandler<EncodeCompletedEventArgs> EncodeCompleted; @@ -80,24 +72,30 @@ namespace HandBrakeWPF.Instance public void StartEncode(JsonEncodeObject jobToStart) { + Thread thread1 = new Thread(() => RunEncodeInitProcess(jobToStart)); + thread1.Start(); + } + + private void RunEncodeInitProcess(JsonEncodeObject jobToStart) + { InitCommand initCommand = new InitCommand - { - EnableDiskLogging = false, - AllowDisconnectedWorker = false, - DisableLibDvdNav = !this.userSettingService.GetUserSetting<bool>(UserSettingConstants.DisableLibDvdNav), - EnableHardwareAcceleration = true, - LogDirectory = DirectoryUtilities.GetLogDirectory(), - LogVerbosity = this.userSettingService.GetUserSetting<int>(UserSettingConstants.Verbosity) - }; + { + EnableDiskLogging = false, + AllowDisconnectedWorker = false, + DisableLibDvdNav = !this.userSettingService.GetUserSetting<bool>(UserSettingConstants.DisableLibDvdNav), + EnableHardwareAcceleration = true, + LogDirectory = DirectoryUtilities.GetLogDirectory(), + LogVerbosity = this.userSettingService.GetUserSetting<int>(UserSettingConstants.Verbosity) + }; initCommand.LogFile = Path.Combine(initCommand.LogDirectory, string.Format("activity_log.worker.{0}.txt", GeneralUtilities.ProcessId)); JsonSerializerSettings settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; + string job = JsonConvert.SerializeObject(new EncodeCommand { InitialiseCommand = initCommand, EncodeJob = jobToStart }, Formatting.None, settings); var task = Task.Run(async () => await this.MakeHttpJsonPostRequest("StartEncode", job)); task.Wait(); - this.MonitorEncodeProgress(); } @@ -128,6 +126,8 @@ namespace HandBrakeWPF.Instance { var plainTextBytes = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()); this.base64Token = Convert.ToBase64String(plainTextBytes); + this.port = this.portService.GetOpenPort(userSettingService.GetUserSetting<int>(UserSettingConstants.ProcessIsolationPort)); + this.serverUrl = string.Format("http://127.0.0.1:{0}/", this.port); workerProcess = new Process { @@ -287,32 +287,8 @@ namespace HandBrakeWPF.Instance } this.EncodeCompleted?.Invoke(sender: this, e: new EncodeCompletedEventArgs(state.WorkDone.Error)); + this.portService.FreePort(this.port); } } - - private int GetOpenPort(int startPort) - { - if (startPort == 0) - { - startPort = 8037; - } - - int portStartIndex = startPort; - - IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties(); - IPEndPoint[] tcpEndPoints = properties.GetActiveTcpListeners(); - - List<int> usedPorts = tcpEndPoints.Select(p => p.Port).ToList<int>(); - int unusedPort = 0; - - unusedPort = Enumerable.Range(portStartIndex, 99).FirstOrDefault(p => !usedPorts.Contains(p)); - - if (startPort != unusedPort) - { - this.logService.LogMessage(string.Format("Port {0} in use. Using {1} instead", startPort, unusedPort)); - } - - return unusedPort; - } } } diff --git a/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs b/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs index 78cfd54c9..f30300f9d 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/LibEncode.cs @@ -12,10 +12,10 @@ namespace HandBrakeWPF.Services.Encode using System; using System.Diagnostics; using System.IO; - using System.Windows.Forms.VisualStyles; using HandBrake.Interop.Interop.EventArgs; using HandBrake.Interop.Interop.Interfaces; + using HandBrake.Interop.Interop.Json.Encode; using HandBrake.Interop.Interop.Json.State; using HandBrake.Interop.Interop.Providers.Interfaces; using HandBrake.Interop.Model; @@ -49,12 +49,15 @@ namespace HandBrakeWPF.Services.Encode private bool isLoggingInitialised; private int encodeCounter; - public LibEncode(IHbFunctionsProvider hbFunctionsProvider, IUserSettingService userSettingService, ILogInstanceManager logInstanceManager, int encodeCounter) : base(userSettingService) + private readonly IPortService portService; + + public LibEncode(IHbFunctionsProvider hbFunctionsProvider, IUserSettingService userSettingService, ILogInstanceManager logInstanceManager, int encodeCounter, IPortService portService) : base(userSettingService) { this.userSettingService = userSettingService; this.logInstanceManager = logInstanceManager; this.hbFunctionsProvider = hbFunctionsProvider; this.encodeCounter = encodeCounter; + this.portService = portService; } public bool IsPasued { get; private set; } @@ -112,7 +115,7 @@ namespace HandBrakeWPF.Services.Encode // Prevent port stealing if multiple jobs start at the same time. lock (portLock) { - this.instance = task.IsPreviewEncode ? HandBrakeInstanceManager.GetPreviewInstance(verbosity, this.userSettingService) : HandBrakeInstanceManager.GetEncodeInstance(verbosity, configuration, this.encodeLogService, userSettingService); + this.instance = task.IsPreviewEncode ? HandBrakeInstanceManager.GetPreviewInstance(verbosity, this.userSettingService) : HandBrakeInstanceManager.GetEncodeInstance(verbosity, configuration, this.encodeLogService, userSettingService, this.portService); this.instance.EncodeCompleted += this.InstanceEncodeCompleted; this.instance.EncodeProgress += this.InstanceEncodeProgress; @@ -124,7 +127,12 @@ namespace HandBrakeWPF.Services.Encode this.VerifyEncodeDestinationPath(task); // Get an EncodeJob object for the Interop Library - this.instance.StartEncode(EncodeTaskFactory.Create(task, configuration, hbFunctionsProvider.GetHbFunctionsWrapper())); + JsonEncodeObject work = EncodeTaskFactory.Create( + task, + configuration, + hbFunctionsProvider.GetHbFunctionsWrapper()); + + this.instance.StartEncode(work); } // Fire the Encode Started Event diff --git a/win/CS/HandBrakeWPF/Services/Interfaces/IPortService.cs b/win/CS/HandBrakeWPF/Services/Interfaces/IPortService.cs new file mode 100644 index 000000000..f9234f775 --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/Interfaces/IPortService.cs @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="IPortService.cs" company="HandBrake Project (http://handbrake.fr)"> +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// </copyright> +// <summary> +// Defines the IPortService type. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Interfaces +{ + public interface IPortService + { + int GetOpenPort(int startPort); + + void FreePort(int port); + } +} diff --git a/win/CS/HandBrakeWPF/Services/PortService.cs b/win/CS/HandBrakeWPF/Services/PortService.cs new file mode 100644 index 000000000..1a10db298 --- /dev/null +++ b/win/CS/HandBrakeWPF/Services/PortService.cs @@ -0,0 +1,76 @@ +// -------------------------------------------------------------------------------------------------------------------- +// <copyright file="PortService.cs" company="HandBrake Project (http://handbrake.fr)"> +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// </copyright> +// <summary> +// Defines the PortService type. +// </summary> +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services +{ + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Net.NetworkInformation; + + using HandBrakeWPF.Services.Interfaces; + + public class PortService : IPortService + { + private readonly object lockObject = new object(); + private List<int> usedPorts = new List<int>(); + + public PortService() + { + } + + public void FreePort(int port) + { + lock (this.lockObject) + { + this.usedPorts.Remove(port); + } + } + + public int GetOpenPort(int startPort) + { + lock (this.lockObject) + { + int foundPort = FindUnusedPort(startPort); + + // If we find a port that's free on the system, but we've already allocated, try find another. + while (this.usedPorts.Contains(foundPort)) + { + int nextPort = foundPort + 1; + foundPort = FindUnusedPort(nextPort); + } + + // Record we've used this port. + this.usedPorts.Add(foundPort); + + return foundPort; + } + } + + private int FindUnusedPort(int startPort) + { + if (startPort == 0) + { + startPort = 8037; + } + + int portStartIndex = startPort; + + IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties(); + IPEndPoint[] tcpEndPoints = properties.GetActiveTcpListeners(); + + List<int> usedPorts = tcpEndPoints.Select(p => p.Port).ToList<int>(); + int unusedPort = 0; + + unusedPort = Enumerable.Range(portStartIndex, 99).FirstOrDefault(p => !usedPorts.Contains(p)); + + return unusedPort; + } + } +} diff --git a/win/CS/HandBrakeWPF/Services/Queue/ActiveJob.cs b/win/CS/HandBrakeWPF/Services/Queue/ActiveJob.cs index 10fffcbf0..8daa6064d 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/ActiveJob.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/ActiveJob.cs @@ -10,6 +10,7 @@ namespace HandBrakeWPF.Services.Queue { using System; + using System.Diagnostics; using HandBrake.Interop.Interop.Providers.Interfaces; @@ -26,10 +27,10 @@ namespace HandBrakeWPF.Services.Queue private readonly QueueTask job; private readonly IEncode encodeService; - public ActiveJob(QueueTask task, IHbFunctionsProvider hbFunctionsProvider, IUserSettingService userSettingService, ILogInstanceManager logInstanceManager, int jobId) + public ActiveJob(QueueTask task, IHbFunctionsProvider hbFunctionsProvider, IUserSettingService userSettingService, ILogInstanceManager logInstanceManager, int jobId, IPortService portService) { this.job = task; - this.encodeService = new LibEncode(hbFunctionsProvider, userSettingService, logInstanceManager, jobId); + this.encodeService = new LibEncode(hbFunctionsProvider, userSettingService, logInstanceManager, jobId, portService); } public event EventHandler<ActiveJobCompletedEventArgs> JobFinished; @@ -60,6 +61,7 @@ namespace HandBrakeWPF.Services.Queue this.encodeService.EncodeCompleted += this.EncodeServiceEncodeCompleted; this.encodeService.EncodeStatusChanged += this.EncodeStatusChanged; + this.encodeService.Start(this.job.Task, this.job.Configuration, this.job.SelectedPresetKey); } } diff --git a/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs b/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs index f3228db7a..2eb7cd7ce 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs @@ -57,6 +57,8 @@ namespace HandBrakeWPF.Services.Queue private readonly ILogInstanceManager logInstanceManager; private readonly IHbFunctionsProvider hbFunctionsProvider; + private readonly IPortService portService; + private readonly ObservableCollection<QueueTask> queue = new ObservableCollection<QueueTask>(); private readonly string queueFile; private readonly object queueFileLock = new object(); @@ -65,13 +67,14 @@ namespace HandBrakeWPF.Services.Queue private int jobIdCounter = 0; private bool processIsolationEnabled; - public QueueService(IUserSettingService userSettingService, ILog logService, IErrorService errorService, ILogInstanceManager logInstanceManager, IHbFunctionsProvider hbFunctionsProvider) + public QueueService(IUserSettingService userSettingService, ILog logService, IErrorService errorService, ILogInstanceManager logInstanceManager, IHbFunctionsProvider hbFunctionsProvider, IPortService portService) { this.userSettingService = userSettingService; this.logService = logService; this.errorService = errorService; this.logInstanceManager = logInstanceManager; this.hbFunctionsProvider = hbFunctionsProvider; + this.portService = portService; // If this is the first instance, just use the main queue file, otherwise add the instance id to the filename. this.queueFile = string.Format("{0}{1}.json", QueueRecoveryHelper.QueueFileName, GeneralUtilities.ProcessId); @@ -567,7 +570,7 @@ namespace HandBrakeWPF.Services.Queue } this.jobIdCounter = this.jobIdCounter + 1; - ActiveJob activeJob = new ActiveJob(job, this.hbFunctionsProvider, this.userSettingService, this.logInstanceManager, this.jobIdCounter); + ActiveJob activeJob = new ActiveJob(job, this.hbFunctionsProvider, this.userSettingService, this.logInstanceManager, this.jobIdCounter, this.portService); activeJob.JobFinished += this.ActiveJob_JobFinished; activeJob.JobStatusUpdated += this.ActiveJob_JobStatusUpdated; this.activeJobs.Add(activeJob); diff --git a/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs b/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs index 16e5cad60..e2280ee80 100644 --- a/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs +++ b/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs @@ -87,6 +87,7 @@ namespace HandBrakeWPF.Startup this.container.Singleton<ISystemService, SystemService>();
this.container.Singleton<IHbFunctionsProvider, HbFunctionsProvider>();
this.container.Singleton<ILogInstanceManager, LogInstanceManager>();
+ this.container.Singleton<IPortService, PortService>();
// Tab Components
this.container.Singleton<IAudioViewModel, AudioViewModel>();
diff --git a/win/CS/HandBrakeWPF/ViewModels/StaticPreviewViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/StaticPreviewViewModel.cs index 52d4bb4b3..1908c964f 100644 --- a/win/CS/HandBrakeWPF/ViewModels/StaticPreviewViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/StaticPreviewViewModel.cs @@ -58,7 +58,7 @@ namespace HandBrakeWPF.ViewModels private bool useSystemDefaultPlayer;
private bool previewRotateFlip;
- public StaticPreviewViewModel(IScan scanService, IUserSettingService userSettingService, IErrorService errorService, IHbFunctionsProvider hbFunctionsProvider, ILog logService, ILogInstanceManager logInstanceManager)
+ public StaticPreviewViewModel(IScan scanService, IUserSettingService userSettingService, IErrorService errorService, IHbFunctionsProvider hbFunctionsProvider, ILog logService, ILogInstanceManager logInstanceManager, IPortService portService)
{
this.scanService = scanService;
this.selectedPreviewImage = 1;
@@ -68,7 +68,7 @@ namespace HandBrakeWPF.ViewModels // Live Preview
this.userSettingService = userSettingService;
this.errorService = errorService;
- this.encodeService = new LibEncode(hbFunctionsProvider, userSettingService, logInstanceManager, 0); // Preview needs a separate instance rather than the shared singleton. This could maybe do with being refactored at some point
+ this.encodeService = new LibEncode(hbFunctionsProvider, userSettingService, logInstanceManager, 0, portService); // Preview needs a separate instance rather than the shared singleton. This could maybe do with being refactored at some point
this.Title = "Preview";
this.Percentage = "0.00%";
|