From b4deb22b537b2330f13eafbcd6a609be4e2e771c Mon Sep 17 00:00:00 2001 From: la Date: Fri, 9 Jun 2023 05:58:52 -0700 Subject: [PATCH] Overload for 'Snapshot' with Input Stream & Output --- FFMpegCore/FFMpeg/FFMpeg.cs | 42 ++++++++++++++++++++ FFMpegCore/FFMpeg/SnapshotArgumentBuilder.cs | 38 +++++++++++++++--- 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 820d9fb..282dd88 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -2,6 +2,7 @@ using FFMpegCore.Enums; using FFMpegCore.Exceptions; using FFMpegCore.Helpers; +using FFMpegCore.Pipes; using Instances; namespace FFMpegCore @@ -32,6 +33,27 @@ public static bool Snapshot(string input, string output, Size? size = null, Time .OutputToFile(output, true, outputOptions) .ProcessSynchronously(); } + + /// + /// Saves a PNG thumbnail from the input video to the specified output stream. + /// + /// Input video stream + /// Output stream to save the thumbnail + /// Thumbnail size. If width or height is set to 0, the other dimension will be computed automatically. + /// Seek position where the thumbnail should be taken. + /// Index of the selected video stream. If not specified, the primary video stream or the first video stream will be selected. + /// Index of the input file + /// True if the snapshot was successfully created and saved; otherwise, false. + public static bool Snapshot(Stream input, Stream output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) + { + var source = FFProbe.Analyse(input); + var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); + + return arguments + .OutputToPipe(new StreamPipeSink(output), outputOptions) + .ProcessSynchronously(); + } + /// /// Saves a 'png' thumbnail from the input video to drive /// @@ -57,6 +79,26 @@ public static async Task SnapshotAsync(string input, string output, Size? .ProcessAsynchronously(); } + /// + /// Saves a PNG thumbnail from the input video to the specified output stream. + /// + /// Input video stream + /// Output stream to save the thumbnail + /// Thumbnail size. If width or height is set to 0, the other dimension will be computed automatically. + /// Seek position where the thumbnail should be taken. + /// Index of the selected video stream. If not specified, the primary video stream or the first video stream will be selected. + /// Index of the input file + /// True if the snapshot was successfully created and saved; otherwise, false. + public static async Task SnapshotAsync(Stream input, Stream output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) + { + var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false); + var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); + + return await arguments + .OutputToPipe(new StreamPipeSink(output), outputOptions) + .ProcessAsynchronously(); + } + public static bool GifSnapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, TimeSpan? duration = null, int? streamIndex = null) { if (Path.GetExtension(output)?.ToLower() != FileExtension.Gif) diff --git a/FFMpegCore/FFMpeg/SnapshotArgumentBuilder.cs b/FFMpegCore/FFMpeg/SnapshotArgumentBuilder.cs index 0d9b414..eafe57b 100644 --- a/FFMpegCore/FFMpeg/SnapshotArgumentBuilder.cs +++ b/FFMpegCore/FFMpeg/SnapshotArgumentBuilder.cs @@ -2,6 +2,7 @@ using System.Drawing; using FFMpegCore.Enums; +using FFMpegCore.Pipes; namespace FFMpegCore; @@ -31,13 +32,38 @@ public static (FFMpegArguments, Action outputOptions) Bui .Resize(size)); } + public static (FFMpegArguments, Action outputOptions) BuildSnapshotArguments( + Stream inputStream, + IMediaAnalysis source, + Size? size = null, + TimeSpan? captureTime = null, + int? streamIndex = null, + int inputFileIndex = 0) + { + captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); + size = PrepareSnapshotSize(source, size); + streamIndex ??= source.PrimaryVideoStream?.Index + ?? source.VideoStreams.FirstOrDefault()?.Index + ?? 0; + + return (FFMpegArguments + .FromPipeInput(new StreamPipeSource(inputStream), options => options + .Seek(captureTime)), + options => options + .SelectStream((int)streamIndex, inputFileIndex) + .WithVideoCodec(VideoCodec.Png) + .WithFrameOutputCount(1) + .Resize(size) + ); + } + public static (FFMpegArguments, Action outputOptions) BuildGifSnapshotArguments( - string input, - IMediaAnalysis source, - Size? size = null, - TimeSpan? captureTime = null, - TimeSpan? duration = null, - int? streamIndex = null, + string input, + IMediaAnalysis source, + Size? size = null, + TimeSpan? captureTime = null, + TimeSpan? duration = null, + int? streamIndex = null, int fps = 12) { var defaultGifOutputSize = new Size(480, -1);