Skip to content
Merged
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
9 changes: 6 additions & 3 deletions FFMpegCore.Extensions.SkiaSharp/FFMpegImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,21 @@ public static SKBitmap Snapshot(string input, Size? size = null, TimeSpan? captu
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<SKBitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null,
int inputFileIndex = 0)
int inputFileIndex = 0, CancellationToken cancellationToken = default)
{
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var source = await FFProbe.AnalyseAsync(input, cancellationToken: cancellationToken).ConfigureAwait(false);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
using var ms = new MemoryStream();

await arguments
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
.ForceFormat("rawvideo")))
.ProcessAsynchronously();
.CancellableThrough(cancellationToken)
.ProcessAsynchronously()
.ConfigureAwait(false);

ms.Position = 0;
return SKBitmap.Decode(ms);
Expand Down
9 changes: 6 additions & 3 deletions FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,21 @@ public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? capture
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<Bitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null,
int inputFileIndex = 0)
int inputFileIndex = 0, CancellationToken cancellationToken = default)
{
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var source = await FFProbe.AnalyseAsync(input, cancellationToken: cancellationToken).ConfigureAwait(false);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
using var ms = new MemoryStream();

await arguments
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
.ForceFormat("rawvideo")))
.ProcessAsynchronously();
.CancellableThrough(cancellationToken)
.ProcessAsynchronously()
.ConfigureAwait(false);

ms.Position = 0;
return new Bitmap(ms);
Expand Down
6 changes: 4 additions & 2 deletions FFMpegCore.Test/VideoTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,8 @@ public async Task Video_GifSnapshot_PersistSnapshotAsync()
using var outputPath = new TemporaryFile("out.gif");
var input = FFProbe.Analyse(TestResources.Mp4Video);

await FFMpeg.GifSnapshotAsync(TestResources.Mp4Video, outputPath, captureTime: TimeSpan.FromSeconds(0));
await FFMpeg.GifSnapshotAsync(TestResources.Mp4Video, outputPath, captureTime: TimeSpan.FromSeconds(0),
cancellationToken: TestContext.CancellationToken);

var analysis = FFProbe.Analyse(outputPath);
Assert.AreNotEqual(input.PrimaryVideoStream!.Width, analysis.PrimaryVideoStream!.Width);
Expand All @@ -748,7 +749,8 @@ public async Task Video_GifSnapshot_PersistSnapshotAsync_SizeSupplied()
var input = FFProbe.Analyse(TestResources.Mp4Video);
var desiredGifSize = new Size(320, 240);

await FFMpeg.GifSnapshotAsync(TestResources.Mp4Video, outputPath, desiredGifSize, TimeSpan.FromSeconds(0));
await FFMpeg.GifSnapshotAsync(TestResources.Mp4Video, outputPath, desiredGifSize, TimeSpan.FromSeconds(0),
cancellationToken: TestContext.CancellationToken);

var analysis = FFProbe.Analyse(outputPath);
Assert.AreNotEqual(input.PrimaryVideoStream!.Width, desiredGifSize.Width);
Expand Down
25 changes: 17 additions & 8 deletions FFMpegCore/FFMpeg/FFMpeg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@ public static bool Snapshot(string input, string output, Size? size = null, Time
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<bool> SnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null,
int inputFileIndex = 0)
int inputFileIndex = 0, CancellationToken cancellationToken = default)
{
CheckSnapshotOutputExtension(output, FileExtension.Image.All);

var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var source = await FFProbe.AnalyseAsync(input, cancellationToken: cancellationToken).ConfigureAwait(false);

return await SnapshotProcess(input, output, source, size, captureTime, streamIndex, inputFileIndex)
.ProcessAsynchronously();
.CancellableThrough(cancellationToken)
.ProcessAsynchronously()
.ConfigureAwait(false);
}

public static bool GifSnapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, TimeSpan? duration = null,
Expand All @@ -61,14 +64,16 @@ public static bool GifSnapshot(string input, string output, Size? size = null, T
}

public static async Task<bool> GifSnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, TimeSpan? duration = null,
int? streamIndex = null)
int? streamIndex = null, CancellationToken cancellationToken = default)
{
CheckSnapshotOutputExtension(output, [FileExtension.Gif]);

var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var source = await FFProbe.AnalyseAsync(input, cancellationToken: cancellationToken).ConfigureAwait(false);

return await GifSnapshotProcess(input, output, source, size, captureTime, duration, streamIndex)
.ProcessAsynchronously();
.CancellableThrough(cancellationToken)
.ProcessAsynchronously()
.ConfigureAwait(false);
}

private static FFMpegArgumentProcessor SnapshotProcess(string input, string output, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null,
Expand Down Expand Up @@ -321,11 +326,15 @@ public static bool SubVideo(string input, string output, TimeSpan startTime, Tim
/// <param name="output">Output video file.</param>
/// <param name="startTime">The start time of when the sub video needs to start</param>
/// <param name="endTime">The end time of where the sub video needs to end</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Output video information.</returns>
public static async Task<bool> SubVideoAsync(string input, string output, TimeSpan startTime, TimeSpan endTime)
public static async Task<bool> SubVideoAsync(string input, string output, TimeSpan startTime, TimeSpan endTime,
CancellationToken cancellationToken = default)
{
return await BaseSubVideo(input, output, startTime, endTime)
.ProcessAsynchronously();
.CancellableThrough(cancellationToken)
.ProcessAsynchronously()
.ConfigureAwait(false);
}

/// <summary>
Expand Down
20 changes: 18 additions & 2 deletions FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,28 @@ private async Task<IProcessResult> Process(ProcessArguments processArguments, Ca

void OnCancelEvent(object sender, int timeout)
{
instance.SendInput("q");
ExecuteIgnoringFinishedProcessExceptions(() => instance.SendInput("q"));

if (!cancellationTokenSource.Token.WaitHandle.WaitOne(timeout, true))
{
cancellationTokenSource.Cancel();
instance.Kill();
ExecuteIgnoringFinishedProcessExceptions(() => instance.Kill());
}

static void ExecuteIgnoringFinishedProcessExceptions(Action action)
{
try
{
action();
}
catch (Instances.Exceptions.InstanceProcessAlreadyExitedException)
{
//ignore
}
catch (ObjectDisposedException)
{
//ignore
}
}
}

Expand Down