Refactoring

Former-commit-id: 6e67f77c32
This commit is contained in:
Malte Rosenbjerg 2020-02-20 23:04:51 +01:00
parent 662377903b
commit 4dd179c2d4
5 changed files with 46 additions and 190 deletions

View file

@ -1,103 +0,0 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using RunProcessAsTask;
namespace FFMpegCore.FFMPEG
{
public abstract class FFBase : IDisposable
{
protected Process Process;
protected FFBase()
{
}
/// <summary>
/// Is 'true' when an exception is thrown during process kill (for paranoia level users).
/// </summary>
public bool IsKillFaulty { get; private set; }
/// <summary>
/// Returns true if the associated process is still alive/running.
/// </summary>
public bool IsWorking
{
get
{
bool processHasExited;
try
{
processHasExited = Process.HasExited;
}
catch
{
processHasExited = true;
}
return !processHasExited && Process.GetProcesses().Any(x => x.Id == Process.Id);
}
}
public void Dispose()
{
Process?.Dispose();
}
protected void CreateProcess(string args, string processPath, bool rStandardInput = false,
bool rStandardOutput = false, bool rStandardError = false)
{
if (IsWorking)
throw new InvalidOperationException(
"The current FFMpeg process is busy with another operation. Create a new object for parallel executions.");
Process = new Process();
IsKillFaulty = false;
Process.StartInfo.FileName = processPath;
Process.StartInfo.Arguments = args;
Process.StartInfo.UseShellExecute = false;
Process.StartInfo.CreateNoWindow = true;
Process.StartInfo.RedirectStandardInput = rStandardInput;
Process.StartInfo.RedirectStandardOutput = rStandardOutput;
Process.StartInfo.RedirectStandardError = rStandardError;
}
public void Kill()
{
try
{
if (IsWorking)
Process.Kill();
}
catch
{
IsKillFaulty = true;
}
}
protected async Task<string> RunProcessAsync(string filePath, string arguments)
{
var result = await ProcessEx.RunAsync(filePath, arguments);
return string.Join("", result.StandardOutput);
}
}
public static class ProcessHelpers
{
public static void RunProcess(string filePath, string arguments)
{
}
public static async Task<string> RunProcessAsync(string fileName, string arguments)
{
var startInfo = new ProcessStartInfo(fileName, arguments);
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
var result = await ProcessEx.RunAsync(startInfo);
return string.Join("", result.StandardOutput);
}
}
}

View file

@ -13,12 +13,13 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Instances;
namespace FFMpegCore.FFMPEG
{
public delegate void ConversionHandler(double percentage);
public class FFMpeg : FFBase
public class FFMpeg
{
IArgumentBuilder ArgumentBuilder { get; set; }
@ -457,6 +458,11 @@ public VideoInfo Convert(ArgumentContainer arguments)
return new VideoInfo(output);
}
/// <summary>
/// Returns true if the associated process is still alive/running.
/// </summary>
public bool IsWorking => _instance.Started;
/// <summary>
/// Stops any current job that FFMpeg is running.
/// </summary>
@ -464,53 +470,31 @@ public void Stop()
{
if (IsWorking)
{
Process.StandardInput.Write('q');
_instance.SendInput("q").Wait();
}
}
#region Private Members & Methods
private string _ffmpegPath;
private readonly string _ffmpegPath;
private TimeSpan _totalTime;
private volatile StringBuilder _errorOutput = new StringBuilder();
private bool RunProcess(ArgumentContainer container, FileInfo output)
{
var successState = true;
_instance?.Dispose();
var arguments = ArgumentBuilder.BuildArguments(container);
_instance = new Instance(_ffmpegPath, arguments);
_instance.DataReceived += OutputData;
var exitCode = _instance.BlockUntilFinished();
CreateProcess(ArgumentBuilder.BuildArguments(container), _ffmpegPath, true, rStandardError: true);
try
if (!File.Exists(output.FullName) || new FileInfo(output.FullName).Length == 0)
{
Process.Start();
Process.ErrorDataReceived += OutputData;
Process.BeginErrorReadLine();
Process.WaitForExit();
}
catch (Exception)
{
successState = false;
}
finally
{
Process.Close();
if (File.Exists(output.FullName))
{
using var file = File.Open(output.FullName, FileMode.Open);
if (file.Length == 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, _errorOutput);
}
}
else
{
throw new FFMpegException(FFMpegExceptionType.Process, _errorOutput);
}
throw new FFMpegException(FFMpegExceptionType.Process, _errorOutput);
}
return successState;
return exitCode == 0;
}
private void Cleanup(IEnumerable<string> pathList)
@ -524,23 +508,28 @@ private void Cleanup(IEnumerable<string> pathList)
}
}
private static Regex _progressRegex = new Regex(@"\w\w:\w\w:\w\w", RegexOptions.Compiled);
private void OutputData(object sender, DataReceivedEventArgs e)
{
if (e.Data == null)
return;
private static readonly Regex ProgressRegex = new Regex(@"\w\w:\w\w:\w\w", RegexOptions.Compiled);
private Instance _instance;
_errorOutput.AppendLine(e.Data);
private void OutputData(object sender, (DataType Type, string Data) msg)
{
var (type, data) = msg;
if (data == null) return;
if (type == DataType.Error)
{
_errorOutput.AppendLine(data);
return;
}
#if DEBUG
Trace.WriteLine(e.Data);
Trace.WriteLine(data);
#endif
if (OnProgress == null || !IsWorking) return;
if (!e.Data.Contains("frame")) return;
if (OnProgress == null) return;
if (!data.Contains("frame")) return;
var match = _progressRegex.Match(e.Data);
var match = ProgressRegex.Match(data);
if (!match.Success) return;
var processed = TimeSpan.Parse(match.Value, CultureInfo.InvariantCulture);

View file

@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using Instances;
namespace FFMpegCore.FFMPEG
{
@ -44,13 +45,7 @@ private static string FFBinary(string name)
ffName = Path.Combine(target, ffName);
}
var path = Path.Combine(Options.RootDirectory, ffName);
if (!File.Exists(path))
throw new FFMpegException(FFMpegExceptionType.Dependency,
$"{name} cannot be found @ {path}");
return path;
return Path.Combine(Options.RootDirectory, ffName);
}
}
}

View file

@ -4,17 +4,18 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
using Instances;
namespace FFMpegCore.FFMPEG
{
public sealed class FFProbe : FFBase
public sealed class FFProbe
{
static readonly double BITS_TO_MB = 1024 * 1024 * 8;
private readonly string _ffprobePath;
public FFProbe(): base()
{
FFProbeHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
_ffprobePath = FFMpegOptions.Options.FFProbeBinary;
}
@ -44,7 +45,9 @@ public Task<VideoInfo> ParseVideoInfoAsync(string source)
/// <returns>A video info object containing all details necessary.</returns>
public VideoInfo ParseVideoInfo(VideoInfo info)
{
var output = RunProcess(BuildFFProbeArguments(info));
var instance = new Instance(_ffprobePath, BuildFFProbeArguments(info));
instance.BlockUntilFinished();
var output = string.Join("", instance.OutputData);
return ParseVideoInfoInternal(info, output);
}
/// <summary>
@ -54,7 +57,9 @@ public VideoInfo ParseVideoInfo(VideoInfo info)
/// <returns>A video info object containing all details necessary.</returns>
public async Task<VideoInfo> ParseVideoInfoAsync(VideoInfo info)
{
var output = await RunProcessAsync(_ffprobePath, BuildFFProbeArguments(info));
var instance = new Instance(_ffprobePath, BuildFFProbeArguments(info));
await instance.FinishedRunning();
var output = string.Join("", instance.OutputData);
return ParseVideoInfoInternal(info, output);
}
@ -115,35 +120,5 @@ private VideoInfo ParseVideoInfoInternal(VideoInfo info, string probeOutput)
return info;
}
#region Private Members & Methods
private readonly string _ffprobePath;
private string RunProcess(string args)
{
CreateProcess(args, _ffprobePath, rStandardOutput: true);
string output;
try
{
Process.Start();
output = Process.StandardOutput.ReadToEnd();
}
catch (Exception)
{
output = "";
}
finally
{
Process.WaitForExit();
Process.Close();
}
return output;
}
#endregion
}
}

View file

@ -129,7 +129,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Instances" Version="1.3.3" />
<PackageReference Include="Instances" Version="1.4.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="RunProcessAsTask" Version="1.2.4" />