From 5f5db69bcd8d868d301fc5424825db1639f4cdaf Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Sun, 29 Jan 2023 22:17:40 +0100 Subject: [PATCH] Add Directory.Build.props Former-commit-id: 8e9b7df4deb5a9f150409e9e2b0b2fdd38b004ed --- Directory.Build.props | 15 ++ FFMpegCore.Examples/Program.cs | 4 +- .../BitmapExtensions.cs | 2 +- ...re.Extensions.System.Drawing.Common.csproj | 21 +-- .../FFMpegImage.cs | 15 +- FFMpegCore.Test/FFMpegCore.Test.csproj | 75 +++----- .../FFMpeg/Arguments/AudioFiltersArgument.cs | 4 +- FFMpegCore/FFMpeg/FFMpeg.cs | 177 ++++++------------ FFMpegCore/FFMpegCore.csproj | 56 ++---- 9 files changed, 135 insertions(+), 234 deletions(-) create mode 100644 Directory.Build.props diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..caefd5d --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,15 @@ + + + netstandard2.0 + en + 5.0.0.0 + default + enable + + GitHub + https://github.com/rosenbjerg/FFMpegCore + https://github.com/rosenbjerg/FFMpegCore + MIT + en + + \ No newline at end of file diff --git a/FFMpegCore.Examples/Program.cs b/FFMpegCore.Examples/Program.cs index ea343f2..e7b93e2 100644 --- a/FFMpegCore.Examples/Program.cs +++ b/FFMpegCore.Examples/Program.cs @@ -84,8 +84,8 @@ await FFMpegArguments var inputImagePath = "/path/to/input/image"; { FFMpegImage.PosterWithAudio(inputPath, inputAudioPath, outputPath); - // or - var image = Image.FromFile(inputImagePath); + // or + using var image = Image.FromFile(inputImagePath); image.AddAudio(inputAudioPath, outputPath); } diff --git a/FFMpegCore.Extensions.System.Drawing.Common/BitmapExtensions.cs b/FFMpegCore.Extensions.System.Drawing.Common/BitmapExtensions.cs index e549580..6633f69 100644 --- a/FFMpegCore.Extensions.System.Drawing.Common/BitmapExtensions.cs +++ b/FFMpegCore.Extensions.System.Drawing.Common/BitmapExtensions.cs @@ -12,7 +12,7 @@ public static bool AddAudio(this Image poster, string audio, string output) poster.Save(destination); try { - return FFMpeg.PosterWithAudio(destination, audio, output); + return FFMpegImage.PosterWithAudio(destination, audio, output); } finally { diff --git a/FFMpegCore.Extensions.System.Drawing.Common/FFMpegCore.Extensions.System.Drawing.Common.csproj b/FFMpegCore.Extensions.System.Drawing.Common/FFMpegCore.Extensions.System.Drawing.Common.csproj index c2575f7..aafb577 100644 --- a/FFMpegCore.Extensions.System.Drawing.Common/FFMpegCore.Extensions.System.Drawing.Common.csproj +++ b/FFMpegCore.Extensions.System.Drawing.Common/FFMpegCore.Extensions.System.Drawing.Common.csproj @@ -1,28 +1,17 @@ - en - https://github.com/rosenbjerg/FFMpegCore - https://github.com/rosenbjerg/FFMpegCore - - Image extension for FFMpegCore, using System.Common.Drawing + true + Image extension for FFMpegCore using System.Common.Drawing + 5.0.0 - 8 - 4.0.0.0 - 4.0.0 - MIT - Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev ffmpeg ffprobe convert video audio mediafile resize analyze muxing - GitHub - true - true - enable - netstandard2.0 + Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev - + diff --git a/FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs b/FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs index 13052a8..467fe6a 100644 --- a/FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs +++ b/FFMpegCore.Extensions.System.Drawing.Common/FFMpegImage.cs @@ -41,6 +41,7 @@ public static bool JoinImageSequence(string output, double frameRate = 30, param return FFMpegArguments .FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false) .OutputToFile(output, true, options => options + .ForcePixelFormat("yuv420p") .Resize(firstImage.Width, firstImage.Height) .WithFramerate(frameRate)) .ProcessSynchronously(); @@ -62,22 +63,22 @@ public static bool PosterWithAudio(string image, string audio, string output) { FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4); using (var img = Image.FromFile(image)) - FFMpegHelper.ConversionSizeExceptionCheck(img); + FFMpegHelper.ConversionSizeExceptionCheck(img.Width, img.Height); return FFMpegArguments .FromFileInput(image, false, options => options - .Loop(1)) + .Loop(1) + .ForceFormat("image2")) .AddFileInput(audio) .OutputToFile(output, true, options => options + .ForcePixelFormat("yuv420p") .WithVideoCodec(VideoCodec.LibX264) - .CopyChannel() .WithConstantRateFactor(21) .WithAudioBitrate(AudioQuality.Normal) .UsingShortest()) .ProcessSynchronously(); - - } + /// /// Saves a 'png' thumbnail to an in-memory bitmap /// @@ -90,7 +91,7 @@ public static bool PosterWithAudio(string image, string audio, string output) 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); + var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); using var ms = new MemoryStream(); arguments @@ -114,7 +115,7 @@ public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? capture public static async Task 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); + var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); using var ms = new MemoryStream(); await arguments diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index c84f3fe..d606d6b 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -2,42 +2,11 @@ net6.0 - false - disable - default - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - Always - - - - - - PreserveNewest - - - @@ -55,46 +24,60 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest - Always + PreserveNewest PreserveNewest - - - - diff --git a/FFMpegCore/FFMpeg/Arguments/AudioFiltersArgument.cs b/FFMpegCore/FFMpeg/Arguments/AudioFiltersArgument.cs index 50b26b3..dac7d15 100644 --- a/FFMpegCore/FFMpeg/Arguments/AudioFiltersArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/AudioFiltersArgument.cs @@ -34,8 +34,8 @@ private string GetText() public interface IAudioFilterArgument { - public string Key { get; } - public string Value { get; } + string Key { get; } + string Value { get; } } public class AudioFilterOptions diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 165ec31..909a96a 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -1,7 +1,6 @@ using FFMpegCore.Enums; using FFMpegCore.Exceptions; using FFMpegCore.Helpers; -using FFMpegCore.Pipes; using System; using System.Collections.Generic; using System.Drawing; @@ -12,54 +11,9 @@ namespace FFMpegCore { - public static class FFMpeg + public static class SnapshotArgumentBuilder { - /// - /// Saves a 'png' thumbnail from the input video to drive - /// - /// 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. - /// Selected video stream index. - /// Input file index - /// Bitmap with the requested snapshot. - public static bool Snapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) - { - if (Path.GetExtension(output) != FileExtension.Png) - output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; - - var source = FFProbe.Analyse(input); - var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); - - return arguments - .OutputToFile(output, true, outputOptions) - .ProcessSynchronously(); - } - /// - /// Saves a 'png' thumbnail from the input video to drive - /// - /// 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. - /// Selected video stream index. - /// Input file index - /// Bitmap with the requested snapshot. - public static async Task SnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) - { - if (Path.GetExtension(output) != FileExtension.Png) - output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; - - var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false); - var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); - - return await arguments - .OutputToFile(output, true, outputOptions) - .ProcessAsynchronously(); - } - - private static (FFMpegArguments, Action outputOptions) BuildSnapshotArguments( + public static (FFMpegArguments, Action outputOptions) BuildSnapshotArguments( string input, IMediaAnalysis source, Size? size = null, @@ -109,13 +63,61 @@ private static (FFMpegArguments, Action outputOptions) Bu return null; } + } + public static class FFMpeg + { + /// + /// Saves a 'png' thumbnail from the input video to drive + /// + /// 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. + /// Selected video stream index. + /// Input file index + /// Bitmap with the requested snapshot. + public static bool Snapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) + { + if (Path.GetExtension(output) != FileExtension.Png) + output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; + + var source = FFProbe.Analyse(input); + var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); + + return arguments + .OutputToFile(output, true, outputOptions) + .ProcessSynchronously(); + } + /// + /// Saves a 'png' thumbnail from the input video to drive + /// + /// 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. + /// Selected video stream index. + /// Input file index + /// Bitmap with the requested snapshot. + public static async Task SnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) + { + if (Path.GetExtension(output) != FileExtension.Png) + output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; + + var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false); + var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); + + return await arguments + .OutputToFile(output, true, outputOptions) + .ProcessAsynchronously(); + } + /// /// Convert a video do a different format. /// - /// Input video source. + /// Input video source. /// Output information. - /// Target conversion video type. + /// Target conversion video format. /// Conversion target speed/quality (faster speed = lower quality). /// Video size. /// Conversion target audio quality. @@ -189,35 +191,6 @@ public static bool Convert( }; } - /// - /// Adds a poster image to an audio file. - /// - /// Source image file. - /// Source audio file. - /// Output video file. - /// - public static bool PosterWithAudio(string image, string audio, string output) - { - FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp4); - using (var imageFile = Image.FromFile(image)) - { - FFMpegHelper.ConversionSizeExceptionCheck(imageFile); - } - - return FFMpegArguments - .FromFileInput(image, false, options => options - .Loop(1) - .ForceFormat("image2")) - .AddFileInput(audio) - .OutputToFile(output, true, options => options - .ForcePixelFormat("yuv420p") - .WithVideoCodec(VideoCodec.LibX264) - .WithConstantRateFactor(21) - .WithAudioBitrate(AudioQuality.Normal) - .UsingShortest()) - .ProcessSynchronously(); - } - /// /// Joins a list of video files. /// @@ -251,44 +224,6 @@ public static bool Join(string output, params string[] videos) } } - /// - /// Converts an image sequence to a video. - /// - /// Output video file. - /// FPS - /// Image sequence collection - /// Output video information. - 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 - .ForcePixelFormat("yuv420p") - .Resize(firstImage.Width, firstImage.Height) - .WithFramerate(frameRate)) - .ProcessSynchronously(); - } - finally - { - Cleanup(temporaryImageFiles); - Directory.Delete(tempFolderName); - } - } - /// /// Records M3U8 streams to the specified output. /// @@ -397,15 +332,15 @@ public static IReadOnlyList GetPixelFormats() return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly(); } - public static bool TryGetPixelFormat(string name, out PixelFormat fmt) + public static bool TryGetPixelFormat(string name, out PixelFormat format) { if (!GlobalFFOptions.Current.UseCache) { - fmt = GetPixelFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim()); - return fmt != null; + format = GetPixelFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim()); + return format != null; } else - return FFMpegCache.PixelFormats.TryGetValue(name, out fmt); + return FFMpegCache.PixelFormats.TryGetValue(name, out format); } public static PixelFormat GetPixelFormat(string name) diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index e537ab6..ecd0b85 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -1,45 +1,23 @@  - - en - https://github.com/rosenbjerg/FFMpegCore - https://github.com/rosenbjerg/FFMpegCore - - A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications - 4.0.0.0 - README.md - - Fixes for `MetaDataArgument` (thanks @JKamsker) -- Support for Audible `aaxc` (thanks @JKamsker) -- Include errordata in `IMediaAnalysis` (thanks @JKamsker) -- Pass `FFOptions` properly when using ffprobe (thanks @Notheisz57) -- CancellationToken support for `AnalyseAsync` -- Case-insensitive dictionaries for `Tags` and `Disposition` -- Fix for `PosterWithAudio` -- Fix for `JoinImageSequence` -- Updates to dependendies -- A lot of bug fixes - 8 - 4.8.0 - MIT - Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev - ffmpeg ffprobe convert video audio mediafile resize analyze muxing - GitHub - true - true - enable - netstandard2.0 - + + true + A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications + 5.0.0 + + + ffmpeg ffprobe convert video audio mediafile resize analyze muxing + Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev + README.md + - - - Always - - - + + + - - - - + + + +