diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6d3529..d00c29b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: - FFMpegCore.Test/** pull_request: branches: - - master + - main - release paths: - .github/workflows/ci.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0c7725b..00a1ea7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,8 +16,8 @@ jobs: dotnet-version: '7.0.x' - name: Build solution - run: dotnet pack FFMpegCore.sln --output build -c Release + run: dotnet pack FFMpegCore.sln -c Release - name: Publish NuGet package - run: dotnet nuget push build/*.nupkg --source nuget.org --api-key ${{ secrets.NUGET_TOKEN }} + run: dotnet nuget push nupkg/*.nupkg --skip-duplicate --source nuget.org --api-key ${{ secrets.NUGET_TOKEN }} diff --git a/FFMpegCore.Examples/FFMpegCore.Examples.csproj b/FFMpegCore.Examples/FFMpegCore.Examples.csproj index db3c66e..555f4b7 100644 --- a/FFMpegCore.Examples/FFMpegCore.Examples.csproj +++ b/FFMpegCore.Examples/FFMpegCore.Examples.csproj @@ -1,14 +1,14 @@ - - Exe - net6.0 - false - + + Exe + net6.0 + false + - - - - + + + + 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 aafb577..13cdc1a 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,21 +1,22 @@ - - true - Image extension for FFMpegCore using System.Common.Drawing - 5.0.0 - - - ffmpeg ffprobe convert video audio mediafile resize analyze muxing - Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev - + + true + Image extension for FFMpegCore using System.Common.Drawing + 5.0.0 + ../nupkg + + + ffmpeg ffprobe convert video audio mediafile resize analyze muxing + Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev + - - - + + + - - - + + + diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index def07d2..0243372 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -12,19 +12,19 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + - - + + diff --git a/FFMpegCore.Test/Resources/TestResources.cs b/FFMpegCore.Test/Resources/TestResources.cs index de84080..b958b80 100644 --- a/FFMpegCore.Test/Resources/TestResources.cs +++ b/FFMpegCore.Test/Resources/TestResources.cs @@ -1,15 +1,5 @@ namespace FFMpegCore.Test.Resources { - public enum AudioType - { - Mp3 - } - - public enum ImageType - { - Png - } - public static class TestResources { public static readonly string Mp4Video = "./Resources/input_3sec.mp4"; diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index e3e4b6b..fec8386 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -413,7 +413,7 @@ public void Video_Snapshot_InMemory() [TestMethod, Timeout(10000)] public void Video_Snapshot_PersistSnapshot() { - var outputPath = new TemporaryFile("out.png"); + using var outputPath = new TemporaryFile("out.png"); var input = FFProbe.Analyse(TestResources.Mp4Video); FFMpeg.Snapshot(TestResources.Mp4Video, outputPath); @@ -427,10 +427,10 @@ public void Video_Snapshot_PersistSnapshot() [TestMethod, Timeout(10000)] public void Video_Join() { - var inputCopy = new TemporaryFile("copy-input.mp4"); + using var inputCopy = new TemporaryFile("copy-input.mp4"); File.Copy(TestResources.Mp4Video, inputCopy); - var outputPath = new TemporaryFile("out.mp4"); + using var outputPath = new TemporaryFile("out.mp4"); var input = FFProbe.Analyse(TestResources.Mp4Video); var success = FFMpeg.Join(outputPath, TestResources.Mp4Video, inputCopy); Assert.IsTrue(success); @@ -461,7 +461,7 @@ public void Video_Join_Image_Sequence() }); var imageAnalysis = FFProbe.Analyse(imageSet.First()); - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var success = FFMpeg.JoinImageSequence(outputFile, frameRate: 10, images: imageSet.ToArray()); Assert.IsTrue(success); var result = FFProbe.Analyse(outputFile); @@ -484,7 +484,7 @@ public void Video_With_Only_Audio_Should_Extract_Metadata() public void Video_Duration() { var video = FFProbe.Analyse(TestResources.Mp4Video); - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); FFMpegArguments .FromFileInput(TestResources.Mp4Video) @@ -503,7 +503,7 @@ public void Video_Duration() [TestMethod, Timeout(10000)] public void Video_UpdatesProgress() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var percentageDone = 0.0; var timeDone = TimeSpan.Zero; @@ -544,7 +544,7 @@ void OnTimeProgess(TimeSpan time) [TestMethod, Timeout(10000)] public void Video_OutputsData() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var dataReceived = false; GlobalFFOptions.Configure(opt => opt.Encoding = Encoding.UTF8); @@ -604,7 +604,7 @@ public void Video_TranscodeToMemory() [TestMethod, Timeout(10000)] public async Task Video_Cancel_Async() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var task = FFMpegArguments .FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args @@ -628,7 +628,7 @@ public async Task Video_Cancel_Async() [TestMethod, Timeout(10000)] public void Video_Cancel() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var task = FFMpegArguments .FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args .WithCustomArgument("-re") @@ -649,7 +649,7 @@ public void Video_Cancel() [TestMethod, Timeout(10000)] public async Task Video_Cancel_Async_With_Timeout() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var task = FFMpegArguments .FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args @@ -679,7 +679,7 @@ public async Task Video_Cancel_Async_With_Timeout() [TestMethod, Timeout(10000)] public async Task Video_Cancel_CancellationToken_Async() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var cts = new CancellationTokenSource(); @@ -704,7 +704,7 @@ public async Task Video_Cancel_CancellationToken_Async() [TestMethod, Timeout(10000)] public async Task Video_Cancel_CancellationToken_Async_Throws() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var cts = new CancellationTokenSource(); @@ -727,7 +727,7 @@ public async Task Video_Cancel_CancellationToken_Async_Throws() [TestMethod, Timeout(10000)] public void Video_Cancel_CancellationToken_Throws() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var cts = new CancellationTokenSource(); @@ -749,7 +749,7 @@ public void Video_Cancel_CancellationToken_Throws() [TestMethod, Timeout(10000)] public async Task Video_Cancel_CancellationToken_Async_With_Timeout() { - var outputFile = new TemporaryFile("out.mp4"); + using var outputFile = new TemporaryFile("out.mp4"); var cts = new CancellationTokenSource(); diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 3939502..362a865 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -66,25 +66,34 @@ public static async Task SnapshotAsync(string input, string output, Size? /// Output video information. public static bool JoinImageSequence(string output, double frameRate = 30, params string[] images) { - int? width = null, height = null; - var tempFolderName = Path.Combine(GlobalFFOptions.Current.TemporaryFilesFolder, Guid.NewGuid().ToString()); - var temporaryImageFiles = images.Select((imagePath, index) => + var fileExtensions = images.Select(Path.GetExtension).Distinct().ToArray(); + if (fileExtensions.Length != 1) { - var analysis = FFProbe.Analyse(imagePath); - FFMpegHelper.ConversionSizeExceptionCheck(analysis.PrimaryVideoStream!.Width, analysis.PrimaryVideoStream!.Height); - width ??= analysis.PrimaryVideoStream.Width; - height ??= analysis.PrimaryVideoStream.Height; + throw new ArgumentException("All images must have the same extension", nameof(images)); + } - var destinationPath = Path.Combine(tempFolderName, $"{index.ToString().PadLeft(9, '0')}{Path.GetExtension(imagePath)}"); - Directory.CreateDirectory(tempFolderName); - File.Copy(imagePath, destinationPath); - return destinationPath; - }).ToArray(); + var fileExtension = fileExtensions[0].ToLowerInvariant(); + int? width = null, height = null; + + var tempFolderName = Path.Combine(GlobalFFOptions.Current.TemporaryFilesFolder, Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempFolderName); try { + var index = 0; + foreach (var imagePath in images) + { + var analysis = FFProbe.Analyse(imagePath); + FFMpegHelper.ConversionSizeExceptionCheck(analysis.PrimaryVideoStream!.Width, analysis.PrimaryVideoStream!.Height); + width ??= analysis.PrimaryVideoStream.Width; + height ??= analysis.PrimaryVideoStream.Height; + + var destinationPath = Path.Combine(tempFolderName, $"{index++.ToString().PadLeft(9, '0')}{fileExtension}"); + File.Copy(imagePath, destinationPath); + } + return FFMpegArguments - .FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false) + .FromFileInput(Path.Combine(tempFolderName, $"%09d{fileExtension}"), false) .OutputToFile(output, true, options => options .ForcePixelFormat("yuv420p") .Resize(width!.Value, height!.Value) @@ -93,8 +102,7 @@ public static bool JoinImageSequence(string output, double frameRate = 30, param } finally { - Cleanup(temporaryImageFiles); - Directory.Delete(tempFolderName); + Directory.Delete(tempFolderName, true); } } diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index 7c3f7bb..843bdba 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -1,23 +1,24 @@  - - 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 - + + true + A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications + 5.0.1 + ../nupkg + + + ffmpeg ffprobe convert video audio mediafile resize analyze muxing + Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev + README.md + - - - + + + - - - - + + + +