2020-05-08 09:07:51 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2020-05-12 19:28:50 +00:00
|
|
|
|
using FFMpegCore.Exceptions;
|
2020-05-08 09:07:51 +00:00
|
|
|
|
using FFMpegCore.Helpers;
|
|
|
|
|
using Instances;
|
|
|
|
|
|
2020-05-09 15:53:03 +00:00
|
|
|
|
namespace FFMpegCore
|
2020-05-08 09:07:51 +00:00
|
|
|
|
{
|
|
|
|
|
public class FFMpegArgumentProcessor
|
|
|
|
|
{
|
|
|
|
|
private readonly FFMpegArguments _ffMpegArguments;
|
|
|
|
|
|
|
|
|
|
internal FFMpegArgumentProcessor(FFMpegArguments ffMpegArguments)
|
|
|
|
|
{
|
|
|
|
|
_ffMpegArguments = ffMpegArguments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the percentage of the current conversion progress.
|
|
|
|
|
/// </summary>
|
|
|
|
|
// public event ConversionHandler OnProgress;
|
|
|
|
|
|
|
|
|
|
public string Arguments => _ffMpegArguments.Text;
|
|
|
|
|
|
|
|
|
|
public FFMpegArgumentProcessor NotifyOnProgress(Action<double>? onPercentageProgress, TimeSpan? totalTimeSpan)
|
|
|
|
|
{
|
|
|
|
|
_totalTimespan = totalTimeSpan;
|
|
|
|
|
_onPercentageProgress = onPercentageProgress;
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
public FFMpegArgumentProcessor NotifyOnProgress(Action<TimeSpan>? onTimeProgress)
|
|
|
|
|
{
|
|
|
|
|
_onTimeProgress = onTimeProgress;
|
|
|
|
|
return this;
|
|
|
|
|
}
|
2020-05-12 15:55:31 +00:00
|
|
|
|
public bool ProcessSynchronously(bool throwOnError = true)
|
2020-05-08 09:07:51 +00:00
|
|
|
|
{
|
|
|
|
|
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
2020-05-12 15:30:35 +00:00
|
|
|
|
using var instance = new Instance(FFMpegOptions.Options.FFmpegBinary(), _ffMpegArguments.Text);
|
2020-05-08 09:07:51 +00:00
|
|
|
|
instance.DataReceived += OutputData;
|
|
|
|
|
var errorCode = -1;
|
|
|
|
|
|
|
|
|
|
_ffMpegArguments.Pre();
|
|
|
|
|
|
|
|
|
|
var cancellationTokenSource = new CancellationTokenSource();
|
2020-05-12 15:55:31 +00:00
|
|
|
|
try
|
2020-05-08 09:07:51 +00:00
|
|
|
|
{
|
2020-05-12 15:55:31 +00:00
|
|
|
|
Task.WaitAll(instance.FinishedRunning().ContinueWith(t =>
|
|
|
|
|
{
|
|
|
|
|
errorCode = t.Result;
|
|
|
|
|
cancellationTokenSource.Cancel();
|
|
|
|
|
}), _ffMpegArguments.During(cancellationTokenSource.Token));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
if (!throwOnError)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e,
|
|
|
|
|
string.Join("\n", instance.ErrorData));
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_ffMpegArguments.Post();
|
|
|
|
|
}
|
2020-05-08 09:07:51 +00:00
|
|
|
|
|
2020-05-12 15:55:31 +00:00
|
|
|
|
if (throwOnError && errorCode != 0)
|
|
|
|
|
throw new FFMpegException(FFMpegExceptionType.Conversion, string.Join("\n", instance.ErrorData));
|
2020-05-08 09:07:51 +00:00
|
|
|
|
|
|
|
|
|
return errorCode == 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-12 15:55:31 +00:00
|
|
|
|
public async Task<bool> ProcessAsynchronously(bool throwOnError = true)
|
2020-05-08 09:07:51 +00:00
|
|
|
|
{
|
|
|
|
|
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
2020-05-12 15:30:35 +00:00
|
|
|
|
using var instance = new Instance(FFMpegOptions.Options.FFmpegBinary(), _ffMpegArguments.Text);
|
2020-05-10 22:04:53 +00:00
|
|
|
|
if (_onTimeProgress != null || (_onPercentageProgress != null && _totalTimespan != null))
|
|
|
|
|
instance.DataReceived += OutputData;
|
2020-05-08 09:07:51 +00:00
|
|
|
|
var errorCode = -1;
|
|
|
|
|
|
|
|
|
|
_ffMpegArguments.Pre();
|
|
|
|
|
|
|
|
|
|
var cancellationTokenSource = new CancellationTokenSource();
|
2020-05-12 15:55:31 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
|
|
|
|
|
{
|
|
|
|
|
errorCode = t.Result;
|
|
|
|
|
cancellationTokenSource.Cancel();
|
|
|
|
|
}), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
2020-05-08 09:07:51 +00:00
|
|
|
|
{
|
2020-05-12 15:55:31 +00:00
|
|
|
|
if (!throwOnError)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e,
|
|
|
|
|
string.Join("\n", instance.ErrorData));
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_ffMpegArguments.Post();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (throwOnError && errorCode != 0)
|
|
|
|
|
throw new FFMpegException(FFMpegExceptionType.Conversion, string.Join("\n", instance.ErrorData));
|
2020-05-08 09:07:51 +00:00
|
|
|
|
|
|
|
|
|
return errorCode == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static readonly Regex ProgressRegex = new Regex(@"time=(\d\d:\d\d:\d\d.\d\d?)", RegexOptions.Compiled);
|
|
|
|
|
private Action<double>? _onPercentageProgress;
|
|
|
|
|
private Action<TimeSpan>? _onTimeProgress;
|
|
|
|
|
private TimeSpan? _totalTimespan;
|
|
|
|
|
|
|
|
|
|
private void OutputData(object sender, (DataType Type, string Data) msg)
|
|
|
|
|
{
|
|
|
|
|
#if DEBUG
|
|
|
|
|
Trace.WriteLine(msg.Data);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
var match = ProgressRegex.Match(msg.Data);
|
|
|
|
|
if (!match.Success) return;
|
|
|
|
|
|
|
|
|
|
var processed = TimeSpan.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
|
|
|
|
|
_onTimeProgress?.Invoke(processed);
|
|
|
|
|
|
|
|
|
|
if (_onPercentageProgress == null || _totalTimespan == null) return;
|
|
|
|
|
var percentage = Math.Round(processed.TotalSeconds / _totalTimespan.Value.TotalSeconds * 100, 2);
|
|
|
|
|
_onPercentageProgress(percentage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|