Remove unnecessary CommonProcess abstraction

pull/2680/head
Mark Monteiro 4 years ago
parent 10050a55a5
commit ee2f911a2b

@ -1727,15 +1727,15 @@ namespace Emby.Server.Implementations
throw new NotSupportedException();
}
var process = ProcessFactory.Create(new ProcessOptions
var process = ProcessFactory.Create(new ProcessStartInfo
{
FileName = url,
EnableRaisingEvents = true,
UseShellExecute = true,
ErrorDialog = false
});
process.Exited += ProcessExited;
process.EnableRaisingEvents = true;
process.Exited += (sender, args) => ((Process)sender).Dispose(); ;
try
{
@ -1748,11 +1748,6 @@ namespace Emby.Server.Implementations
}
}
private static void ProcessExited(object sender, EventArgs e)
{
((IProcess)sender).Dispose();
}
public virtual void EnableLoopback(string appName)
{
}

@ -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);
}
}
}

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

@ -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;
@ -1683,19 +1684,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
var process = _processFactory.Create(new ProcessOptions
var process = _processFactory.Create(new ProcessStartInfo
{
Arguments = GetPostProcessArguments(path, options.RecordingPostProcessorArguments),
CreateNoWindow = true,
EnableRaisingEvents = true,
ErrorDialog = false,
FileName = options.RecordingPostProcessor,
IsHidden = true,
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false
});
_logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
process.EnableRaisingEvents = true;
process.Exited += Process_Exited;
process.Start();
}
@ -1712,11 +1713,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void Process_Exited(object sender, EventArgs e)
{
using (var process = (IProcess)sender)
try
{
_logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode);
process.Dispose();
var exitCode = ((Process)sender).ExitCode;
_logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", exitCode);
}
finally
{
((Process)sender).Dispose();
}
}

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
@ -30,7 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private bool _hasExited;
private Stream _logFileStream;
private string _targetPath;
private IProcess _process;
private Process _process;
private readonly IProcessFactory _processFactory;
private readonly IJsonSerializer _json;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_targetPath = targetFile;
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
var process = _processFactory.Create(new ProcessOptions
_process = _processFactory.Create(new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
@ -91,14 +92,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
FileName = _mediaEncoder.EncoderPath,
Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
IsHidden = true,
ErrorDialog = false,
EnableRaisingEvents = true
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
});
_process = process;
var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
var commandLineLogMessage = _process.StartInfo.FileName + " " + _process.StartInfo.Arguments;
_logger.LogInformation(commandLineLogMessage);
var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
@ -110,16 +108,17 @@ 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.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);
@ -293,7 +292,7 @@ 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;

@ -0,0 +1,66 @@
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>
/// 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>
public static bool HasExitedSafe(this Process process)
{
try
{
return process.HasExited;
}
catch (InvalidOperationException)
{
return true;
}
}
/// <summary>
/// Asynchronously wait for the process to exit.
/// </summary>
/// <param name="process">The process to wait for.</param>
/// <param name="timeMs">A timeout, in milliseconds, after which to stop waiting for the task.</param>
/// <returns>True if the task exited normally, false if the timeout elapsed before the process exited.</returns>
public static async Task<bool> WaitForExitAsync(this Process process, int timeMs)
{
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;
}
// Add an additional timeout then await
using (var cancelTokenSource = new CancellationTokenSource(Math.Max(0, timeMs)))
using (var cancelRegistration = cancelTokenSource.Token.Register(() => tcs.TrySetResult(process.HasExitedSafe())))
{
return await tcs.Task.ConfigureAwait(false);
}
}
}
}

@ -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,8 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;
using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.MediaEncoding.Encoder
{
@ -362,7 +363,7 @@ 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 = _processFactory.Create(new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
@ -374,10 +375,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
Arguments = args,
IsHidden = true,
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false,
EnableRaisingEvents = true
});
process.EnableRaisingEvents = true;
if (forceEnableLogging)
{
@ -571,16 +572,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
var process = _processFactory.Create(new ProcessOptions
var process = _processFactory.Create(new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
FileName = _ffmpegPath,
Arguments = args,
IsHidden = true,
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false,
EnableRaisingEvents = true
});
process.EnableRaisingEvents = true;
_logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
@ -700,16 +701,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
var process = _processFactory.Create(new ProcessOptions
var process = _processFactory.Create(new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
FileName = _ffmpegPath,
Arguments = args,
IsHidden = true,
ErrorDialog = false,
EnableRaisingEvents = true
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
});
process.EnableRaisingEvents = true;
_logger.LogInformation(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
@ -949,14 +950,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 +965,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
void OnProcessExited(object sender, EventArgs e)
{
var process = (IProcess)sender;
var process = (Process)sender;
HasExited = true;
@ -979,7 +980,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
DisposeProcess(process);
}
private void DisposeProcess(IProcess process)
private void DisposeProcess(Process process)
{
lock (_mediaEncoder._runningProcessesLock)
{

@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
@ -429,14 +430,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
encodingParam = " -sub_charenc " + encodingParam;
}
var process = _processFactory.Create(new ProcessOptions
var process = _processFactory.Create(new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
FileName = _mediaEncoder.EncoderPath,
Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
EnableRaisingEvents = true,
IsHidden = true,
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
});
@ -453,6 +453,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw;
}
process.EnableRaisingEvents = true;
var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
if (!ranToCompletion)
@ -578,14 +579,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
outputCodec,
outputPath);
var process = _processFactory.Create(new ProcessOptions
var process = _processFactory.Create(new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
EnableRaisingEvents = true,
FileName = _mediaEncoder.EncoderPath,
Arguments = processArgs,
IsHidden = true,
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
});
@ -602,6 +602,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw;
}
process.EnableRaisingEvents = true;
var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false);
if (!ranToCompletion)

@ -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; }
}
}

@ -1,24 +1,11 @@
#pragma warning disable CS1591
using System.Diagnostics;
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; }
Process Create(ProcessStartInfo options);
}
}

Loading…
Cancel
Save