mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
parent
662377903b
commit
4dd179c2d4
5 changed files with 46 additions and 190 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in a new issue