mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
Init
This commit is contained in:
parent
a03cb42036
commit
53651a0275
15 changed files with 186 additions and 66 deletions
|
@ -6,6 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FFMpegCore.Extensions.System.Drawing.Common\FFMpegCore.Extensions.System.Drawing.Common.csproj" />
|
||||||
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj" />
|
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
using FFMpegCore;
|
using FFMpegCore;
|
||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
using FFMpegCore.Pipes;
|
using FFMpegCore.Pipes;
|
||||||
using FFMpegCore.Extend;
|
using FFMpegCore.Extensions.System.Drawing.Common;
|
||||||
|
|
||||||
var inputPath = "/path/to/input";
|
var inputPath = "/path/to/input";
|
||||||
var outputPath = "/path/to/output";
|
var outputPath = "/path/to/output";
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
// process the snapshot in-memory and use the Bitmap directly
|
// process the snapshot in-memory and use the Bitmap directly
|
||||||
var bitmap = FFMpeg.Snapshot(inputPath, new Size(200, 400), TimeSpan.FromMinutes(1));
|
var bitmap = FFMpegImage.Snapshot(inputPath, new Size(200, 400), TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
// or persists the image on the drive
|
// or persists the image on the drive
|
||||||
FFMpeg.Snapshot(inputPath, outputPath, new Size(200, 400), TimeSpan.FromMinutes(1));
|
FFMpeg.Snapshot(inputPath, outputPath, new Size(200, 400), TimeSpan.FromMinutes(1));
|
||||||
|
@ -61,7 +61,7 @@ await FFMpegArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
FFMpeg.JoinImageSequence(@"..\joined_video.mp4", frameRate: 1,
|
FFMpegImage.JoinImageSequence(@"..\joined_video.mp4", frameRate: 1,
|
||||||
ImageInfo.FromPath(@"..\1.png"),
|
ImageInfo.FromPath(@"..\1.png"),
|
||||||
ImageInfo.FromPath(@"..\2.png"),
|
ImageInfo.FromPath(@"..\2.png"),
|
||||||
ImageInfo.FromPath(@"..\3.png")
|
ImageInfo.FromPath(@"..\3.png")
|
||||||
|
@ -83,7 +83,7 @@ await FFMpegArguments
|
||||||
|
|
||||||
var inputImagePath = "/path/to/input/image";
|
var inputImagePath = "/path/to/input/image";
|
||||||
{
|
{
|
||||||
FFMpeg.PosterWithAudio(inputPath, inputAudioPath, outputPath);
|
FFMpegImage.PosterWithAudio(inputPath, inputAudioPath, outputPath);
|
||||||
// or
|
// or
|
||||||
var image = Image.FromFile(inputImagePath);
|
var image = Image.FromFile(inputImagePath);
|
||||||
image.AddAudio(inputAudioPath, outputPath);
|
image.AddAudio(inputAudioPath, outputPath);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace FFMpegCore.Extend
|
namespace FFMpegCore.Extensions.System.Drawing.Common
|
||||||
{
|
{
|
||||||
public static class BitmapExtensions
|
public static class BitmapExtensions
|
||||||
{
|
{
|
|
@ -7,7 +7,7 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FFMpegCore.Pipes;
|
using FFMpegCore.Pipes;
|
||||||
|
|
||||||
namespace FFMpegCore.Extend
|
namespace FFMpegCore.Extensions.System.Drawing.Common
|
||||||
{
|
{
|
||||||
public class BitmapVideoFrameWrapper : IVideoFrame, IDisposable
|
public class BitmapVideoFrameWrapper : IVideoFrame, IDisposable
|
||||||
{
|
{
|
|
@ -0,0 +1,28 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<NeutralLanguage>en</NeutralLanguage>
|
||||||
|
<RepositoryUrl>https://github.com/rosenbjerg/FFMpegCore</RepositoryUrl>
|
||||||
|
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl>
|
||||||
|
<Copyright></Copyright>
|
||||||
|
<Description>Image extension for FFMpegCore, using System.Common.Drawing</Description>
|
||||||
|
<PackageReleaseNotes>
|
||||||
|
</PackageReleaseNotes>
|
||||||
|
<LangVersion>8</LangVersion>
|
||||||
|
<AssemblyVersion>4.0.0.0</AssemblyVersion>
|
||||||
|
<PackageVersion>4.0.0</PackageVersion>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<Authors>Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev</Authors>
|
||||||
|
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
|
||||||
|
<RepositoryType>GitHub</RepositoryType>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FFMpegCore" Version="4.7.0" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
134
FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs
Normal file
134
FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FFMpegCore.Enums;
|
||||||
|
using FFMpegCore.Helpers;
|
||||||
|
using FFMpegCore.Pipes;
|
||||||
|
|
||||||
|
namespace FFMpegCore.Extensions.System.Drawing.Common
|
||||||
|
{
|
||||||
|
public static class FFMpegImage
|
||||||
|
{
|
||||||
|
public static void ConversionSizeExceptionCheck(Image image)
|
||||||
|
=> FFMpegHelper.ConversionSizeExceptionCheck(image.Size.Width, image.Size.Height);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an image sequence to a video.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="output">Output video file.</param>
|
||||||
|
/// <param name="frameRate">FPS</param>
|
||||||
|
/// <param name="images">Image sequence collection</param>
|
||||||
|
/// <returns>Output video information.</returns>
|
||||||
|
public static bool JoinImageSequence(string output, double frameRate = 30, params ImageInfo[] images)
|
||||||
|
{
|
||||||
|
var tempFolderName = Path.Combine(GlobalFFOptions.Current.TemporaryFilesFolder, Guid.NewGuid().ToString());
|
||||||
|
var temporaryImageFiles = images.Select((imageInfo, index) =>
|
||||||
|
{
|
||||||
|
using var image = Image.FromFile(imageInfo.FullName);
|
||||||
|
FFMpegHelper.ConversionSizeExceptionCheck(image);
|
||||||
|
var destinationPath = Path.Combine(tempFolderName, $"{index.ToString().PadLeft(9, '0')}{imageInfo.Extension}");
|
||||||
|
Directory.CreateDirectory(tempFolderName);
|
||||||
|
File.Copy(imageInfo.FullName, destinationPath);
|
||||||
|
return destinationPath;
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
var firstImage = images.First();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return FFMpegArguments
|
||||||
|
.FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false)
|
||||||
|
.OutputToFile(output, true, options => options
|
||||||
|
.Resize(firstImage.Width, firstImage.Height)
|
||||||
|
.WithFramerate(frameRate))
|
||||||
|
.ProcessSynchronously();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Cleanup(temporaryImageFiles);
|
||||||
|
Directory.Delete(tempFolderName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a poster image to an audio file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">Source image file.</param>
|
||||||
|
/// <param name="audio">Source audio file.</param>
|
||||||
|
/// <param name="output">Output video file.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool PosterWithAudio(string image, string audio, string output)
|
||||||
|
{
|
||||||
|
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4);
|
||||||
|
using (var img = Image.FromFile(image))
|
||||||
|
FFMpegHelper.ConversionSizeExceptionCheck(img);
|
||||||
|
|
||||||
|
return FFMpegArguments
|
||||||
|
.FromFileInput(image, false, options => options
|
||||||
|
.Loop(1))
|
||||||
|
.AddFileInput(audio)
|
||||||
|
.OutputToFile(output, true, options => options
|
||||||
|
.WithVideoCodec(VideoCodec.LibX264)
|
||||||
|
.CopyChannel()
|
||||||
|
.WithConstantRateFactor(21)
|
||||||
|
.WithAudioBitrate(AudioQuality.Normal)
|
||||||
|
.UsingShortest())
|
||||||
|
.ProcessSynchronously();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Saves a 'png' thumbnail to an in-memory bitmap
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Source video file.</param>
|
||||||
|
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
|
||||||
|
/// <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>
|
||||||
|
/// <returns>Bitmap with the requested snapshot.</returns>
|
||||||
|
public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
|
||||||
|
{
|
||||||
|
var source = FFProbe.Analyse(input);
|
||||||
|
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
|
||||||
|
arguments
|
||||||
|
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
|
||||||
|
.ForceFormat("rawvideo")))
|
||||||
|
.ProcessSynchronously();
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
using var bitmap = new Bitmap(ms);
|
||||||
|
return bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Saves a 'png' thumbnail to an in-memory bitmap
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Source video file.</param>
|
||||||
|
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
|
||||||
|
/// <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>
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
|
||||||
|
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
|
||||||
|
await arguments
|
||||||
|
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
|
||||||
|
.ForceFormat("rawvideo")))
|
||||||
|
.ProcessAsynchronously();
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
return new Bitmap(ms);
|
||||||
|
}
|
||||||
|
private static void Cleanup(IEnumerable<string> pathList)
|
||||||
|
{
|
||||||
|
foreach (var path in pathList)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using FFMpegCore.Extensions.System.Drawing.Common;
|
||||||
|
|
||||||
namespace FFMpegCore.Test
|
namespace FFMpegCore.Test
|
||||||
{
|
{
|
||||||
|
@ -68,7 +69,7 @@ public void Audio_Add()
|
||||||
public void Image_AddAudio()
|
public void Image_AddAudio()
|
||||||
{
|
{
|
||||||
using var outputFile = new TemporaryFile("out.mp4");
|
using var outputFile = new TemporaryFile("out.mp4");
|
||||||
FFMpeg.PosterWithAudio(TestResources.PngImage, TestResources.Mp3Audio, outputFile);
|
FFMpegImage.PosterWithAudio(TestResources.PngImage, TestResources.Mp3Audio, outputFile);
|
||||||
var analysis = FFProbe.Analyse(TestResources.Mp3Audio);
|
var analysis = FFProbe.Analyse(TestResources.Mp3Audio);
|
||||||
Assert.IsTrue(analysis.Duration.TotalSeconds > 0);
|
Assert.IsTrue(analysis.Duration.TotalSeconds > 0);
|
||||||
Assert.IsTrue(File.Exists(outputFile));
|
Assert.IsTrue(File.Exists(outputFile));
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FFMpegCore.Extensions.System.Drawing.Common\FFMpegCore.Extensions.System.Drawing.Common.csproj" />
|
||||||
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj" />
|
<ProjectReference Include="..\FFMpegCore\FFMpegCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using FFMpegCore.Extend;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using FFMpegCore.Extensions.System.Drawing.Common;
|
||||||
using FFMpegCore.Pipes;
|
using FFMpegCore.Pipes;
|
||||||
|
|
||||||
namespace FFMpegCore.Test
|
namespace FFMpegCore.Test
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using FFMpegCore.Extensions.System.Drawing.Common;
|
||||||
|
|
||||||
namespace FFMpegCore.Test
|
namespace FFMpegCore.Test
|
||||||
{
|
{
|
||||||
|
@ -402,7 +403,7 @@ public void Video_ToMP4_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixe
|
||||||
public void Video_Snapshot_InMemory()
|
public void Video_Snapshot_InMemory()
|
||||||
{
|
{
|
||||||
var input = FFProbe.Analyse(TestResources.Mp4Video);
|
var input = FFProbe.Analyse(TestResources.Mp4Video);
|
||||||
using var bitmap = FFMpeg.Snapshot(TestResources.Mp4Video);
|
using var bitmap = FFMpegImage.Snapshot(TestResources.Mp4Video);
|
||||||
|
|
||||||
Assert.AreEqual(input.PrimaryVideoStream!.Width, bitmap.Width);
|
Assert.AreEqual(input.PrimaryVideoStream!.Width, bitmap.Width);
|
||||||
Assert.AreEqual(input.PrimaryVideoStream.Height, bitmap.Height);
|
Assert.AreEqual(input.PrimaryVideoStream.Height, bitmap.Height);
|
||||||
|
@ -460,8 +461,8 @@ public void Video_Join_Image_Sequence()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
using var outputFile = new TemporaryFile("out.mp4");
|
var outputFile = new TemporaryFile("out.mp4");
|
||||||
var success = FFMpeg.JoinImageSequence(outputFile, images: imageSet.ToArray());
|
var success = FFMpegImage.JoinImageSequence(outputFile, images: imageSet.ToArray());
|
||||||
Assert.IsTrue(success);
|
Assert.IsTrue(success);
|
||||||
var result = FFProbe.Analyse(outputFile);
|
var result = FFProbe.Analyse(outputFile);
|
||||||
Assert.AreEqual(3, result.Duration.Seconds);
|
Assert.AreEqual(3, result.Duration.Seconds);
|
||||||
|
|
|
@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Test", "FFMpegCo
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Examples", "FFMpegCore.Examples\FFMpegCore.Examples.csproj", "{3125CF91-FFBD-4E4E-8930-247116AFE772}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Examples", "FFMpegCore.Examples\FFMpegCore.Examples.csproj", "{3125CF91-FFBD-4E4E-8930-247116AFE772}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore.Extensions.System.Drawing.Common", "FFMpegCore.Extensions.System.Drawing.Common\FFMpegCore.Extensions.System.Drawing.Common.csproj", "{9C1A4930-9369-4A18-AD98-929A2A510D80}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -27,6 +29,10 @@ Global
|
||||||
{3125CF91-FFBD-4E4E-8930-247116AFE772}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3125CF91-FFBD-4E4E-8930-247116AFE772}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3125CF91-FFBD-4E4E-8930-247116AFE772}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3125CF91-FFBD-4E4E-8930-247116AFE772}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3125CF91-FFBD-4E4E-8930-247116AFE772}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3125CF91-FFBD-4E4E-8930-247116AFE772}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9C1A4930-9369-4A18-AD98-929A2A510D80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9C1A4930-9369-4A18-AD98-929A2A510D80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9C1A4930-9369-4A18-AD98-929A2A510D80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9C1A4930-9369-4A18-AD98-929A2A510D80}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -59,54 +59,6 @@ public static async Task<bool> SnapshotAsync(string input, string output, Size?
|
||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves a 'png' thumbnail to an in-memory bitmap
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input">Source video file.</param>
|
|
||||||
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
|
|
||||||
/// <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>
|
|
||||||
/// <returns>Bitmap with the requested snapshot.</returns>
|
|
||||||
public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
|
|
||||||
{
|
|
||||||
var source = FFProbe.Analyse(input);
|
|
||||||
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
|
|
||||||
using var ms = new MemoryStream();
|
|
||||||
|
|
||||||
arguments
|
|
||||||
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
|
|
||||||
.ForceFormat("rawvideo")))
|
|
||||||
.ProcessSynchronously();
|
|
||||||
|
|
||||||
ms.Position = 0;
|
|
||||||
using var bitmap = new Bitmap(ms);
|
|
||||||
return bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Saves a 'png' thumbnail to an in-memory bitmap
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input">Source video file.</param>
|
|
||||||
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
|
|
||||||
/// <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>
|
|
||||||
/// <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)
|
|
||||||
{
|
|
||||||
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
|
|
||||||
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
|
|
||||||
using var ms = new MemoryStream();
|
|
||||||
|
|
||||||
await arguments
|
|
||||||
.OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options
|
|
||||||
.ForceFormat("rawvideo")))
|
|
||||||
.ProcessAsynchronously();
|
|
||||||
|
|
||||||
ms.Position = 0;
|
|
||||||
return new Bitmap(ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(
|
private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(
|
||||||
string input,
|
string input,
|
||||||
IMediaAnalysis source,
|
IMediaAnalysis source,
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Instances" Version="3.0.0" />
|
<PackageReference Include="Instances" Version="3.0.0" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
|
||||||
<PackageReference Include="System.Text.Json" Version="7.0.1" />
|
<PackageReference Include="System.Text.Json" Version="7.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,10 @@ public static class FFMpegHelper
|
||||||
{
|
{
|
||||||
private static bool _ffmpegVerified;
|
private static bool _ffmpegVerified;
|
||||||
|
|
||||||
public static void ConversionSizeExceptionCheck(Image image)
|
|
||||||
=> ConversionSizeExceptionCheck(image.Size.Width, image.Size.Height);
|
|
||||||
|
|
||||||
public static void ConversionSizeExceptionCheck(IMediaAnalysis info)
|
public static void ConversionSizeExceptionCheck(IMediaAnalysis info)
|
||||||
=> ConversionSizeExceptionCheck(info.PrimaryVideoStream!.Width, info.PrimaryVideoStream.Height);
|
=> ConversionSizeExceptionCheck(info.PrimaryVideoStream!.Width, info.PrimaryVideoStream.Height);
|
||||||
|
|
||||||
private static void ConversionSizeExceptionCheck(int width, int height)
|
public static void ConversionSizeExceptionCheck(int width, int height)
|
||||||
{
|
{
|
||||||
if (height % 2 != 0 || width % 2 != 0 )
|
if (height % 2 != 0 || width % 2 != 0 )
|
||||||
throw new ArgumentException("FFMpeg yuv420p encoding requires the width and height to be a multiple of 2!");
|
throw new ArgumentException("FFMpeg yuv420p encoding requires the width and height to be a multiple of 2!");
|
||||||
|
|
Loading…
Reference in a new issue