Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions FFMpegCore/FFMpeg/FFMpeg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,31 @@ internal static IReadOnlyList<PixelFormat> GetPixelFormatsInternal()
return list.AsReadOnly();
}

internal static async Task<IReadOnlyList<PixelFormat>> GetPixelFormatsInternalAsync(CancellationToken cancellationToken = default)
{
FFMpegHelper.RootExceptionCheck();

var apa = await GlobalFFOptions.GetFFMpegBinaryPathAsync(cancellationToken: cancellationToken).ConfigureAwait(false);

var list = new List<PixelFormat>();
var processArguments = new ProcessArguments(apa, "-pix_fmts");
processArguments.OutputDataReceived += (e, data) =>
{
if (PixelFormat.TryParse(data, out var format))
{
list.Add(format);
}
};

var result = await processArguments.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
if (result.ExitCode != 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", result.OutputData));
}

return list.AsReadOnly();
}

public static IReadOnlyList<PixelFormat> GetPixelFormats()
{
if (!GlobalFFOptions.Current.UseCache)
Expand All @@ -469,6 +494,16 @@ public static IReadOnlyList<PixelFormat> GetPixelFormats()
return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
}

public static async Task<IReadOnlyList<PixelFormat>> GetPixelFormatsAsync(CancellationToken cancellationToken = default)
{
if (!GlobalFFOptions.Current.UseCache)
{
return await GetPixelFormatsInternalAsync(cancellationToken).ConfigureAwait(false);
}

return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly();
}

public static bool TryGetPixelFormat(string name, out PixelFormat format)
{
if (!GlobalFFOptions.Current.UseCache)
Expand Down Expand Up @@ -522,6 +557,36 @@ private static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string a
}
}

private static async Task ParsePartOfCodecsAsync(Dictionary<string, Codec> codecs, string arguments, Func<string, Codec?> parser, CancellationToken cancellationToken = default)
{
FFMpegHelper.RootExceptionCheck();

var ffmpegPath = await GlobalFFOptions.GetFFMpegBinaryPathAsync(cancellationToken: cancellationToken).ConfigureAwait(false);

var processArguments = new ProcessArguments(ffmpegPath, arguments);
processArguments.OutputDataReceived += (e, data) =>
{
var codec = parser(data);
if (codec != null)
{
if (codecs.TryGetValue(codec.Name, out var parentCodec))
{
parentCodec.Merge(codec);
}
else
{
codecs.Add(codec.Name, codec);
}
}
};

var result = await processArguments.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
if (result.ExitCode != 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", result.OutputData));
}
}

internal static Dictionary<string, Codec> GetCodecsInternal()
{
var res = new Dictionary<string, Codec>();
Expand Down Expand Up @@ -556,6 +621,40 @@ internal static Dictionary<string, Codec> GetCodecsInternal()
return res;
}

internal static async Task<Dictionary<string, Codec>> GetCodecsInternalAsync(CancellationToken cancellationToken = default)
{
var res = new Dictionary<string, Codec>();
await ParsePartOfCodecsAsync(res, "-codecs", s =>
{
if (Codec.TryParseFromCodecs(s, out var codec))
{
return codec;
}

return null;
}, cancellationToken).ConfigureAwait(false);
await ParsePartOfCodecsAsync(res, "-encoders", s =>
{
if (Codec.TryParseFromEncodersDecoders(s, out var codec, true))
{
return codec;
}

return null;
}, cancellationToken).ConfigureAwait(false);
await ParsePartOfCodecsAsync(res, "-decoders", s =>
{
if (Codec.TryParseFromEncodersDecoders(s, out var codec, false))
{
return codec;
}

return null;
}, cancellationToken).ConfigureAwait(false);

return res;
}

public static IReadOnlyList<Codec> GetCodecs()
{
if (!GlobalFFOptions.Current.UseCache)
Expand All @@ -566,6 +665,17 @@ public static IReadOnlyList<Codec> GetCodecs()
return FFMpegCache.Codecs.Values.ToList().AsReadOnly();
}

public static async Task<IReadOnlyList<Codec>> GetCodecsAsync(CancellationToken cancellationToken = default)
{
if (!GlobalFFOptions.Current.UseCache)
{
var codecs = await GetCodecsInternalAsync(cancellationToken).ConfigureAwait(false);
return codecs.Values.ToList().AsReadOnly();
}

return FFMpegCache.Codecs.Values.ToList().AsReadOnly();
}

public static IReadOnlyList<Codec> GetCodecs(CodecType type)
{
if (!GlobalFFOptions.Current.UseCache)
Expand Down Expand Up @@ -644,6 +754,31 @@ internal static IReadOnlyList<ContainerFormat> GetContainersFormatsInternal()
return list.AsReadOnly();
}

internal static async Task<IReadOnlyList<ContainerFormat>> GetContainersFormatsInternalAsync(CancellationToken cancellationToken = default)
{
FFMpegHelper.RootExceptionCheck();

var ffmpegPath = await GlobalFFOptions.GetFFMpegBinaryPathAsync(cancellationToken: cancellationToken).ConfigureAwait(false);

var list = new List<ContainerFormat>();
var instance = new ProcessArguments(ffmpegPath, "-formats");
instance.OutputDataReceived += (e, data) =>
{
if (ContainerFormat.TryParse(data, out var fmt))
{
list.Add(fmt);
}
};

var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
if (result.ExitCode != 0)
{
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", result.OutputData));
}

return list.AsReadOnly();
}

public static IReadOnlyList<ContainerFormat> GetContainerFormats()
{
if (!GlobalFFOptions.Current.UseCache)
Expand All @@ -654,6 +789,16 @@ public static IReadOnlyList<ContainerFormat> GetContainerFormats()
return FFMpegCache.ContainerFormats.Values.ToList().AsReadOnly();
}

public static async Task<IReadOnlyList<ContainerFormat>> GetContainerFormatsAsync(CancellationToken cancellationToken = default)
{
if (!GlobalFFOptions.Current.UseCache)
{
return await GetContainersFormatsInternalAsync(cancellationToken).ConfigureAwait(false);
}

return FFMpegCache.ContainerFormats.Values.ToList().AsReadOnly();
}

public static bool TryGetContainerFormat(string name, out ContainerFormat fmt)
{
if (!GlobalFFOptions.Current.UseCache)
Expand Down
25 changes: 21 additions & 4 deletions FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,18 @@ public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOpti
return HandleCompletion(throwOnError, processResult?.ExitCode ?? -1, processResult?.ErrorData ?? Array.Empty<string>());
}

public async Task<bool> ProcessAsynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null)
public async Task<bool> ProcessAsynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null, CancellationToken cancellationToken = default)
{
var options = GetConfiguredOptions(ffMpegOptions);
var processArguments = PrepareProcessArguments(options);
var processArguments = await PrepareProcessArgumentsAsync(options, cancellationToken).ConfigureAwait(false);
using var cancellationTokenSource = new CancellationTokenSource();

IProcessResult? processResult = null;
try
{
processResult = await Process(processArguments, cancellationTokenSource).ConfigureAwait(false);
}
catch (OperationCanceledException)
catch (OperationCanceledException) when (throwOnError)
{
if (throwOnError)
{
Expand Down Expand Up @@ -252,6 +252,23 @@ private ProcessArguments PrepareProcessArguments(FFOptions ffOptions)
FFMpegHelper.RootExceptionCheck();
FFMpegHelper.VerifyFFMpegExists(ffOptions);

var fileName = GlobalFFOptions.GetFFMpegBinaryPath(ffOptions);

return GetProcessArguments(ffOptions, fileName);
}

public async Task<ProcessArguments> PrepareProcessArgumentsAsync(FFOptions ffOptions, CancellationToken cancellationToken = default)
{
FFMpegHelper.RootExceptionCheck();
await FFMpegHelper.VerifyFFMpegExistsAsync(ffOptions, cancellationToken).ConfigureAwait(false);

var fileName = await GlobalFFOptions.GetFFMpegBinaryPathAsync(ffOptions, cancellationToken).ConfigureAwait(false);

return GetProcessArguments(ffOptions, fileName);
}

private ProcessArguments GetProcessArguments(FFOptions ffOptions, string fileName)
{
var arguments = _ffMpegArguments.Text;

//If local loglevel is null, set the global.
Expand All @@ -270,7 +287,7 @@ private ProcessArguments PrepareProcessArguments(FFOptions ffOptions)

var startInfo = new ProcessStartInfo
{
FileName = GlobalFFOptions.GetFFMpegBinaryPath(ffOptions),
FileName = fileName,
Arguments = arguments,
StandardOutputEncoding = ffOptions.Encoding,
StandardErrorEncoding = ffOptions.Encoding,
Expand Down
46 changes: 40 additions & 6 deletions FFMpegCore/FFProbe/FFProbe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public static async Task<IMediaAnalysis> AnalyseAsync(string filePath, FFOptions
{
ThrowIfInputFileDoesNotExist(filePath);

var instance = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments);
var instance = await PrepareStreamAnalysisInstanceAsync(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments, cancellationToken).ConfigureAwait(false);
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
ThrowIfExitCodeNotZero(result);
Expand Down Expand Up @@ -114,15 +114,15 @@ public static async Task<FFProbePackets> GetPacketsAsync(string filePath, FFOpti
{
ThrowIfInputFileDoesNotExist(filePath);

var instance = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments);
var instance = await PreparePacketAnalysisInstanceAsync(filePath, ffOptions ?? GlobalFFOptions.Current, customArguments, cancellationToken).ConfigureAwait(false);
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
return ParsePacketsOutput(result);
}

public static async Task<IMediaAnalysis> AnalyseAsync(Uri uri, FFOptions? ffOptions = null, CancellationToken cancellationToken = default,
string? customArguments = null)
{
var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments);
var instance = await PrepareStreamAnalysisInstanceAsync(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments, cancellationToken).ConfigureAwait(false);
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
ThrowIfExitCodeNotZero(result);
Expand All @@ -135,7 +135,7 @@ public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, FFOptions?
{
var streamPipeSource = new StreamPipeSource(stream);
var pipeArgument = new InputPipeArgument(streamPipeSource);
var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current, customArguments);
var instance = await PrepareStreamAnalysisInstanceAsync(pipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current, customArguments, cancellationToken).ConfigureAwait(false);
pipeArgument.Pre();

var task = instance.StartAndWaitForExitAsync(cancellationToken);
Expand All @@ -162,7 +162,7 @@ public static async Task<IMediaAnalysis> AnalyseAsync(Stream stream, FFOptions?
public static async Task<FFProbeFrames> GetFramesAsync(Uri uri, FFOptions? ffOptions = null, CancellationToken cancellationToken = default,
string? customArguments = null)
{
var instance = PrepareFrameAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments);
var instance = await PrepareFrameAnalysisInstanceAsync(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current, customArguments, cancellationToken).ConfigureAwait(false);
var result = await instance.StartAndWaitForExitAsync(cancellationToken).ConfigureAwait(false);
return ParseFramesOutput(result);
}
Expand Down Expand Up @@ -240,11 +240,45 @@ private static ProcessArguments PreparePacketAnalysisInstance(string filePath, F
return PrepareInstance($"-loglevel error -print_format json -show_packets -v quiet -sexagesimal \"{filePath}\"", ffOptions, customArguments);
}

private static async Task<ProcessArguments> PrepareStreamAnalysisInstanceAsync(string filePath, FFOptions ffOptions, string? customArguments, CancellationToken cancellationToken = default)
{
return await PrepareInstanceAsync($"-loglevel error -print_format json -show_format -sexagesimal -show_streams -show_chapters \"{filePath}\"", ffOptions,
customArguments, cancellationToken).ConfigureAwait(false);
}

private static async Task<ProcessArguments> PrepareFrameAnalysisInstanceAsync(string filePath, FFOptions ffOptions, string? customArguments, CancellationToken cancellationToken = default)
{
return await PrepareInstanceAsync($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"{filePath}\"", ffOptions, customArguments, cancellationToken).ConfigureAwait(false);
}

private static async Task<ProcessArguments> PreparePacketAnalysisInstanceAsync(string filePath, FFOptions ffOptions, string? customArguments, CancellationToken cancellationToken = default)
{
return await PrepareInstanceAsync($"-loglevel error -print_format json -show_packets -v quiet -sexagesimal \"{filePath}\"", ffOptions, customArguments, cancellationToken).ConfigureAwait(false);
}

private static ProcessArguments PrepareInstance(string arguments, FFOptions ffOptions, string? customArguments)
{
FFProbeHelper.RootExceptionCheck();
FFProbeHelper.VerifyFFProbeExists(ffOptions);
var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(ffOptions), $"{arguments} {customArguments}")

var filePath = GlobalFFOptions.GetFFProbeBinaryPath(ffOptions);

return GetProcessArguments(arguments, ffOptions, customArguments, filePath);
}

private static async Task<ProcessArguments> PrepareInstanceAsync(string arguments, FFOptions ffOptions, string? customArguments, CancellationToken cancellationToken = default)
{
FFProbeHelper.RootExceptionCheck();
await FFProbeHelper.VerifyFFProbeExistsAsync(ffOptions, cancellationToken).ConfigureAwait(false);

var filePath = await GlobalFFOptions.GetFFProbeBinaryPathAsync(ffOptions, cancellationToken).ConfigureAwait(false);

return GetProcessArguments(arguments, ffOptions, customArguments, filePath);
}

private static ProcessArguments GetProcessArguments(string arguments, FFOptions ffOptions, string? customArguments, string filePath)
{
var startInfo = new ProcessStartInfo(filePath, $"{arguments} {customArguments}")
{
StandardOutputEncoding = ffOptions.Encoding,
StandardErrorEncoding = ffOptions.Encoding,
Expand Down
Loading
Loading