From 18cb87559d33ad27c10d8555b15bfdb5e2bc1052 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Tue, 12 May 2020 22:48:20 +0200 Subject: [PATCH] Snapshot improvements completely in-memory is now possible Former-commit-id: ca89cac2f013153bf848670e728b8daa99eb33ad --- FFMpegCore.Test/VideoTest.cs | 10 ++-- FFMpegCore/FFMpeg/FFMpeg.cs | 93 +++++++++++++++++++++--------------- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index ffa5da3..8101123 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -3,6 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; +using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; @@ -427,7 +428,7 @@ public void Video_ToOGV_MultiThread() } [TestMethod] - public void Video_Snapshot() + public void Video_Snapshot_InMemory() { var output = Input.OutputLocation(ImageType.Png); @@ -435,7 +436,7 @@ public void Video_Snapshot() { var input = FFProbe.Analyse(Input.FullName); - using var bitmap = FFMpeg.Snapshot(input, output); + using var bitmap = FFMpeg.Snapshot(input); Assert.AreEqual(input.PrimaryVideoStream.Width, bitmap.Width); Assert.AreEqual(input.PrimaryVideoStream.Height, bitmap.Height); Assert.AreEqual(bitmap.RawFormat, ImageFormat.Png); @@ -455,11 +456,12 @@ public void Video_Snapshot_PersistSnapshot() { var input = FFProbe.Analyse(Input.FullName); - using var bitmap = FFMpeg.Snapshot(input, output, persistSnapshotOnFileSystem: true); + FFMpeg.Snapshot(input, output); + + var bitmap = Image.FromFile(output); Assert.AreEqual(input.PrimaryVideoStream.Width, bitmap.Width); Assert.AreEqual(input.PrimaryVideoStream.Height, bitmap.Height); Assert.AreEqual(bitmap.RawFormat, ImageFormat.Png); - Assert.IsTrue(File.Exists(output)); } finally { diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 13d141f..8bcdc1f 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -7,29 +7,68 @@ using FFMpegCore.Enums; using FFMpegCore.Exceptions; using FFMpegCore.Helpers; +using FFMpegCore.Pipes; namespace FFMpegCore { public static class FFMpeg { /// - /// Saves a 'png' thumbnail from the input video. + /// Saves a 'png' thumbnail from the input video to drive /// - /// Source video file. - /// Output video file + /// Source video analysis + /// Output video file path /// Seek position where the thumbnail should be taken. /// Thumbnail size. If width or height equal 0, the other will be computed automatically. - /// By default, it deletes the created image on disk. If set to true, it won't delete the image /// Bitmap with the requested snapshot. - public static Bitmap Snapshot(MediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null, - bool persistSnapshotOnFileSystem = false) + public static bool Snapshot(MediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null) { - if (captureTime == null) - captureTime = TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); + captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); if (Path.GetExtension(output) != FileExtension.Png) output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; + size = PrepareSnapshotSize(source, size); + + return FFMpegArguments + .FromInputFiles(source.Path) + .WithVideoCodec(VideoCodec.Png) + .WithFrameOutputCount(1) + .Resize(size) + .Seek(captureTime) + .OutputToFile(output) + .ProcessSynchronously(); + } + /// + /// Saves a 'png' thumbnail to an in-memory bitmap + /// + /// Source video file. + /// Seek position where the thumbnail should be taken. + /// Thumbnail size. If width or height equal 0, the other will be computed automatically. + /// Bitmap with the requested snapshot. + public static Bitmap Snapshot(MediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) + { + captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); + + size = PrepareSnapshotSize(source, size); + + using var ms = new MemoryStream(); + FFMpegArguments + .FromInputFiles(source.Path) + .WithVideoCodec(VideoCodec.Png) + .WithFrameOutputCount(1) + .Resize(size) + .Seek(captureTime) + .ForceFormat("rawvideo") + .OutputToPipe(new StreamPipeDataReader(ms)) + .ProcessSynchronously(); + + ms.Position = 0; + return new Bitmap(ms); + } + + private static Size? PrepareSnapshotSize(MediaAnalysis source, Size? size) + { if (size == null || (size.Value.Height == 0 && size.Value.Width == 0)) size = new Size(source.PrimaryVideoStream.Width, source.PrimaryVideoStream.Height); @@ -37,44 +76,22 @@ public static Bitmap Snapshot(MediaAnalysis source, string output, Size? size = { if (size.Value.Width == 0) { - var ratio = source.PrimaryVideoStream.Width / (double)size.Value.Width; + var ratio = source.PrimaryVideoStream.Width / (double) size.Value.Width; - size = new Size((int)(source.PrimaryVideoStream.Width * ratio), (int)(source.PrimaryVideoStream.Height * ratio)); + size = new Size((int) (source.PrimaryVideoStream.Width * ratio), + (int) (source.PrimaryVideoStream.Height * ratio)); } if (size.Value.Height == 0) { - var ratio = source.PrimaryVideoStream.Height / (double)size.Value.Height; + var ratio = source.PrimaryVideoStream.Height / (double) size.Value.Height; - size = new Size((int)(source.PrimaryVideoStream.Width * ratio), (int)(source.PrimaryVideoStream.Height * ratio)); + size = new Size((int) (source.PrimaryVideoStream.Width * ratio), + (int) (source.PrimaryVideoStream.Height * ratio)); } } - var success = FFMpegArguments - .FromInputFiles(true, source.Path) - .WithVideoCodec(VideoCodec.Png) - .WithFrameOutputCount(1) - .Resize(size) - .Seek(captureTime) - .OutputToFile(output) - .ProcessSynchronously(); - - - if (!success) - throw new OperationCanceledException("Could not take snapshot!"); - - Bitmap result; - using (var bmp = (Bitmap)Image.FromFile(output)) - { - using var ms = new MemoryStream(); - bmp.Save(ms, ImageFormat.Png); - result = new Bitmap(ms); - } - - if (File.Exists(output) && !persistSnapshotOnFileSystem) - File.Delete(output); - - return result; + return size; } /// @@ -489,7 +506,7 @@ public static bool TryGetContainerFormat(string name, out ContainerFormat fmt) return FFMpegCache.ContainerFormats.TryGetValue(name, out fmt); } - public static ContainerFormat GetContinerFormat(string name) + public static ContainerFormat GetContainerFormat(string name) { if (TryGetContainerFormat(name, out var fmt)) return fmt;