diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 994a569..a6fe6cb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,8 +2,8 @@ name: CI
on:
push:
- branches-ignore:
- - release
+ branches:
+ - master
pull_request:
branches:
- master
@@ -11,15 +11,19 @@ on:
jobs:
ci:
- runs-on: ubuntu-latest
- timeout-minutes: 7
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [windows-latest, ubuntu-latest]
+ timeout-minutes: 6
steps:
- - uses: actions/checkout@v1
- - name: Prepare FFMpeg
- run: sudo apt-get update && sudo apt-get install -y ffmpeg
- - name: Setup .NET Core
+ - name: Checkout
+ uses: actions/checkout@v1
+ - name: Prepare .NET
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 3.1.101
+ dotnet-version: '5.0.x'
+ - name: Prepare FFMpeg
+ uses: FedericoCarboni/setup-ffmpeg@v1-beta
- name: Test with dotnet
run: dotnet test --logger GitHubActions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5f7efcd..0f10b27 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,13 +7,14 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
- - name: Setup .NET Core
+ - name: Checkout
+ uses: actions/checkout@v1
+ - name: Prepare .NET
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 3.1
+ dotnet-version: '5.0.x'
- name: Build solution
- run: dotnet build --output build
+ run: dotnet build --output build -c Release
- name: Publish NuGet package
- run: dotnet nuget push "build/*.nupkg" --skip-duplicate --source nuget.org --api-key ${{ secrets.NUGET_TOKEN }}
+ run: dotnet nuget push "build/*.nupkg" --source nuget.org --api-key ${{ secrets.NUGET_TOKEN }}
diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs
index 4092ad6..f56c4d3 100644
--- a/FFMpegCore.Test/ArgumentBuilderTest.cs
+++ b/FFMpegCore.Test/ArgumentBuilderTest.cs
@@ -6,7 +6,7 @@
namespace FFMpegCore.Test
{
[TestClass]
- public class ArgumentBuilderTest : BaseTest
+ public class ArgumentBuilderTest
{
private readonly string[] _concatFiles = { "1.mp4", "2.mp4", "3.mp4", "4.mp4"};
diff --git a/FFMpegCore.Test/AudioTest.cs b/FFMpegCore.Test/AudioTest.cs
index 552ca24..bd53541 100644
--- a/FFMpegCore.Test/AudioTest.cs
+++ b/FFMpegCore.Test/AudioTest.cs
@@ -3,80 +3,72 @@
using FFMpegCore.Test.Resources;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using FFMpegCore.Pipes;
namespace FFMpegCore.Test
{
[TestClass]
- public class AudioTest : BaseTest
+ public class AudioTest
{
[TestMethod]
public void Audio_Remove()
{
- var output = Input.OutputLocation(VideoType.Mp4);
-
- try
- {
- FFMpeg.Mute(Input.FullName, output);
- Assert.IsTrue(File.Exists(output));
- }
- finally
- {
- if (File.Exists(output)) File.Delete(output);
- }
+ using var outputFile = new TemporaryFile("out.mp4");
+
+ FFMpeg.Mute(TestResources.Mp4Video, outputFile);
+ var analysis = FFProbe.Analyse(outputFile);
+
+ Assert.IsTrue(analysis.VideoStreams.Any());
+ Assert.IsTrue(!analysis.AudioStreams.Any());
}
[TestMethod]
public void Audio_Save()
{
- var output = Input.OutputLocation(AudioType.Mp3);
-
- try
- {
- FFMpeg.ExtractAudio(Input.FullName, output);
- Assert.IsTrue(File.Exists(output));
- }
- finally
- {
- if (File.Exists(output)) File.Delete(output);
- }
+ using var outputFile = new TemporaryFile("out.mp3");
+
+ FFMpeg.ExtractAudio(TestResources.Mp4Video, outputFile);
+ var analysis = FFProbe.Analyse(outputFile);
+
+ Assert.IsTrue(!analysis.VideoStreams.Any());
+ Assert.IsTrue(analysis.AudioStreams.Any());
}
-
+ [TestMethod]
+ public async Task Audio_FromRaw()
+ {
+ await using var file = File.Open(TestResources.RawAudio, FileMode.Open);
+ var memoryStream = new MemoryStream();
+ await FFMpegArguments
+ .FromPipeInput(new StreamPipeSource(file), options => options.ForceFormat("s16le"))
+ .OutputToPipe(new StreamPipeSink(memoryStream), options => options.ForceFormat("mp3"))
+ .ProcessAsynchronously();
+ }
+
[TestMethod]
public void Audio_Add()
{
- var output = Input.OutputLocation(VideoType.Mp4);
- try
- {
- var success = FFMpeg.ReplaceAudio(VideoLibrary.LocalVideoNoAudio.FullName, VideoLibrary.LocalAudio.FullName, output);
- Assert.IsTrue(success);
- var audioAnalysis = FFProbe.Analyse(VideoLibrary.LocalVideoNoAudio.FullName);
- var videoAnalysis = FFProbe.Analyse(VideoLibrary.LocalAudio.FullName);
- var outputAnalysis = FFProbe.Analyse(output);
- Assert.AreEqual(Math.Max(videoAnalysis.Duration.TotalSeconds, audioAnalysis.Duration.TotalSeconds), outputAnalysis.Duration.TotalSeconds, 0.15);
- Assert.IsTrue(File.Exists(output));
- }
- finally
- {
- if (File.Exists(output)) File.Delete(output);
- }
+ using var outputFile = new TemporaryFile("out.mp4");
+
+ var success = FFMpeg.ReplaceAudio(TestResources.Mp4WithoutAudio, TestResources.Mp3Audio, outputFile);
+ var videoAnalysis = FFProbe.Analyse(TestResources.Mp4WithoutAudio);
+ var audioAnalysis = FFProbe.Analyse(TestResources.Mp3Audio);
+ var outputAnalysis = FFProbe.Analyse(outputFile);
+
+ Assert.IsTrue(success);
+ Assert.AreEqual(Math.Max(videoAnalysis.Duration.TotalSeconds, audioAnalysis.Duration.TotalSeconds), outputAnalysis.Duration.TotalSeconds, 0.15);
+ Assert.IsTrue(File.Exists(outputFile));
}
[TestMethod]
public void Image_AddAudio()
{
- var output = Input.OutputLocation(VideoType.Mp4);
-
- try
- {
- FFMpeg.PosterWithAudio(VideoLibrary.LocalCover.FullName, VideoLibrary.LocalAudio.FullName, output);
- var analysis = FFProbe.Analyse(VideoLibrary.LocalAudio.FullName);
- Assert.IsTrue(analysis.Duration.TotalSeconds > 0);
- Assert.IsTrue(File.Exists(output));
- }
- finally
- {
- if (File.Exists(output)) File.Delete(output);
- }
+ using var outputFile = new TemporaryFile("out.mp4");
+ FFMpeg.PosterWithAudio(TestResources.PngImage, TestResources.Mp3Audio, outputFile);
+ var analysis = FFProbe.Analyse(TestResources.Mp3Audio);
+ Assert.IsTrue(analysis.Duration.TotalSeconds > 0);
+ Assert.IsTrue(File.Exists(outputFile));
}
}
}
\ No newline at end of file
diff --git a/FFMpegCore.Test/BaseTest.cs b/FFMpegCore.Test/BaseTest.cs
deleted file mode 100644
index 38a5bcc..0000000
--- a/FFMpegCore.Test/BaseTest.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using FFMpegCore.Test.Resources;
-using System.IO;
-
-namespace FFMpegCore.Test
-{
- public class BaseTest
- {
- protected FileInfo Input;
-
- public BaseTest()
- {
- Input = VideoLibrary.LocalVideo;
- }
- }
-}
\ No newline at end of file
diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj
index 971b098..2cd97bb 100644
--- a/FFMpegCore.Test/FFMpegCore.Test.csproj
+++ b/FFMpegCore.Test/FFMpegCore.Test.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net5.0
false
@@ -27,6 +27,9 @@
PreserveNewest
+
+ Always
+
@@ -36,8 +39,8 @@
-
-
+
+
diff --git a/FFMpegCore.Test/FFProbeTests.cs b/FFMpegCore.Test/FFProbeTests.cs
index 7fe928e..21d9d34 100644
--- a/FFMpegCore.Test/FFProbeTests.cs
+++ b/FFMpegCore.Test/FFProbeTests.cs
@@ -11,16 +11,26 @@ public class FFProbeTests
[TestMethod]
public void Probe_TooLongOutput()
{
- Assert.ThrowsException(() => FFProbe.Analyse(VideoLibrary.LocalVideo.FullName, 5));
+ Assert.ThrowsException(() => FFProbe.Analyse(TestResources.Mp4Video, 5));
+ }
+
+
+ [TestMethod]
+ public async Task Audio_FromStream_Duration()
+ {
+ var fileAnalysis = await FFProbe.AnalyseAsync(TestResources.WebmVideo);
+ await using var inputStream = File.OpenRead(TestResources.WebmVideo);
+ var streamAnalysis = await FFProbe.AnalyseAsync(inputStream);
+ Assert.IsTrue(fileAnalysis.Duration == streamAnalysis.Duration);
}
[TestMethod]
public void Probe_Success()
{
- var info = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
+ var info = FFProbe.Analyse(TestResources.Mp4Video);
Assert.AreEqual(3, info.Duration.Seconds);
Assert.AreEqual(".mp4", info.Extension);
- Assert.AreEqual(VideoLibrary.LocalVideo.FullName, info.Path);
+ Assert.AreEqual(TestResources.Mp4Video, info.Path);
Assert.AreEqual("5.1", info.PrimaryAudioStream.ChannelLayout);
Assert.AreEqual(6, info.PrimaryAudioStream.Channels);
@@ -47,14 +57,14 @@ public void Probe_Success()
[TestMethod, Timeout(10000)]
public async Task Probe_Async_Success()
{
- var info = await FFProbe.AnalyseAsync(VideoLibrary.LocalVideo.FullName);
+ var info = await FFProbe.AnalyseAsync(TestResources.Mp4Video);
Assert.AreEqual(3, info.Duration.Seconds);
}
[TestMethod, Timeout(10000)]
public void Probe_Success_FromStream()
{
- using var stream = File.OpenRead(VideoLibrary.LocalVideoWebm.FullName);
+ using var stream = File.OpenRead(TestResources.WebmVideo);
var info = FFProbe.Analyse(stream);
Assert.AreEqual(3, info.Duration.Seconds);
}
@@ -62,7 +72,7 @@ public void Probe_Success_FromStream()
[TestMethod, Timeout(10000)]
public async Task Probe_Success_FromStream_Async()
{
- await using var stream = File.OpenRead(VideoLibrary.LocalVideoWebm.FullName);
+ await using var stream = File.OpenRead(TestResources.WebmVideo);
var info = await FFProbe.AnalyseAsync(stream);
Assert.AreEqual(3, info.Duration.Seconds);
}
diff --git a/FFMpegCore.Test/Resources/TestResources.cs b/FFMpegCore.Test/Resources/TestResources.cs
new file mode 100644
index 0000000..f37ed0c
--- /dev/null
+++ b/FFMpegCore.Test/Resources/TestResources.cs
@@ -0,0 +1,28 @@
+using System;
+using System.IO;
+using FFMpegCore.Enums;
+
+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";
+ public static readonly string WebmVideo = "./Resources/input_3sec.webm";
+ public static readonly string Mp4WithoutVideo = "./Resources/input_audio_only_10sec.mp4";
+ public static readonly string Mp4WithoutAudio = "./Resources/input_video_only_3sec.mp4";
+ public static readonly string RawAudio = "./Resources/audio.raw";
+ public static readonly string Mp3Audio = "./Resources/audio.mp3";
+ public static readonly string PngImage = "./Resources/cover.png";
+ public static readonly string ImageCollection = "./Resources/images";
+ }
+}
diff --git a/FFMpegCore.Test/Resources/VideoLibrary.cs b/FFMpegCore.Test/Resources/VideoLibrary.cs
deleted file mode 100644
index 8bb0139..0000000
--- a/FFMpegCore.Test/Resources/VideoLibrary.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.IO;
-using FFMpegCore.Enums;
-
-namespace FFMpegCore.Test.Resources
-{
- public enum AudioType
- {
- Mp3
- }
-
- public enum ImageType
- {
- Png
- }
-
- public static class VideoLibrary
- {
- public static readonly FileInfo LocalVideo = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_3sec.mp4");
- public static readonly FileInfo LocalVideoWebm = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_3sec.webm");
- public static readonly FileInfo LocalVideoAudioOnly = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_audio_only_10sec.mp4");
- public static readonly FileInfo LocalVideoNoAudio = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}input_video_only_3sec.mp4");
- public static readonly FileInfo LocalAudio = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}audio.mp3");
- public static readonly FileInfo LocalCover = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}cover.png");
- public static readonly FileInfo ImageDirectory = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}images");
- public static readonly FileInfo ImageJoinOutput = new FileInfo($".{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}images{Path.DirectorySeparatorChar}output.mp4");
-
- public static string OutputLocation(this FileInfo file, ContainerFormat type)
- {
- return OutputLocation(file, type.Extension, "_converted");
- }
-
- public static string OutputLocation(this FileInfo file, AudioType type)
- {
- return OutputLocation(file, type.ToString(), "_audio");
- }
-
- public static string OutputLocation(this FileInfo file, ImageType type)
- {
- return OutputLocation(file, type.ToString(), "_screenshot");
- }
-
- public static string OutputLocation(this FileInfo file, string type, string keyword)
- {
- string originalLocation = file.Directory.FullName,
- outputFile = file.Name.Replace(file.Extension, keyword + "." + type.ToLowerInvariant());
-
- return $"{originalLocation}{Path.DirectorySeparatorChar}{Guid.NewGuid()}_{outputFile}";
- }
- }
-}
diff --git a/FFMpegCore.Test/Resources/audio.raw b/FFMpegCore.Test/Resources/audio.raw
new file mode 100644
index 0000000..e131095
Binary files /dev/null and b/FFMpegCore.Test/Resources/audio.raw differ
diff --git a/FFMpegCore.Test/TemporaryFile.cs b/FFMpegCore.Test/TemporaryFile.cs
new file mode 100644
index 0000000..f64f5fe
--- /dev/null
+++ b/FFMpegCore.Test/TemporaryFile.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace FFMpegCore.Test
+{
+ public class TemporaryFile : IDisposable
+ {
+ private readonly string _path;
+
+ public TemporaryFile(string filename)
+ {
+ _path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}-{filename}");
+ }
+
+ public static implicit operator string(TemporaryFile temporaryFile) => temporaryFile._path;
+ public void Dispose()
+ {
+ if (File.Exists(_path))
+ File.Delete(_path);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs
index e4f750d..b2bff02 100644
--- a/FFMpegCore.Test/VideoTest.cs
+++ b/FFMpegCore.Test/VideoTest.cs
@@ -15,105 +15,90 @@
namespace FFMpegCore.Test
{
[TestClass]
- public class VideoTest : BaseTest
+ public class VideoTest
{
public bool Convert(ContainerFormat type, bool multithreaded = false, VideoSize size = VideoSize.Original)
{
- var output = Input.OutputLocation(type);
+ using var outputFile = new TemporaryFile($"out{type.Extension}");
- try
+ var input = FFProbe.Analyse(TestResources.Mp4Video);
+ FFMpeg.Convert(input, outputFile, type, size: size, multithreaded: multithreaded);
+ var outputVideo = FFProbe.Analyse(outputFile);
+
+ Assert.IsTrue(File.Exists(outputFile));
+ Assert.AreEqual(outputVideo.Duration.TotalSeconds, input.Duration.TotalSeconds, 0.1);
+ if (size == VideoSize.Original)
{
- var input = FFProbe.Analyse(Input.FullName);
- FFMpeg.Convert(input, output, type, size: size, multithreaded: multithreaded);
- var outputVideo = FFProbe.Analyse(output);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
+ }
+ else
+ {
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, (int)size);
+ }
- Assert.IsTrue(File.Exists(output));
- Assert.AreEqual(outputVideo.Duration.TotalSeconds, input.Duration.TotalSeconds, 0.1);
- if (size == VideoSize.Original)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
- }
- else
- {
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, (int)size);
- }
- return File.Exists(output) &&
- outputVideo.Duration == input.Duration &&
+ return File.Exists(outputFile) &&
+ outputVideo.Duration == input.Duration &&
+ (
(
- (
size == VideoSize.Original &&
outputVideo.PrimaryVideoStream.Width == input.PrimaryVideoStream.Width &&
outputVideo.PrimaryVideoStream.Height == input.PrimaryVideoStream.Height
- ) ||
- (
+ ) ||
+ (
size != VideoSize.Original &&
outputVideo.PrimaryVideoStream.Width != input.PrimaryVideoStream.Width &&
outputVideo.PrimaryVideoStream.Height != input.PrimaryVideoStream.Height &&
outputVideo.PrimaryVideoStream.Height == (int)size
- )
- );
- }
- finally
- {
- if (File.Exists(output))
- File.Delete(output);
- }
+ )
+ );
}
private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] arguments)
{
- var output = Input.OutputLocation(type);
+ using var outputFile = new TemporaryFile($"out{type.Extension}");
+
+ var input = FFProbe.Analyse(TestResources.WebmVideo);
+ using var inputStream = File.OpenRead(input.Path);
+ var processor = FFMpegArguments
+ .FromPipeInput(new StreamPipeSource(inputStream))
+ .OutputToFile(outputFile, false, opt =>
+ {
+ foreach (var arg in arguments)
+ opt.WithArgument(arg);
+ });
- try
+ var scaling = arguments.OfType().FirstOrDefault();
+
+ var success = processor.ProcessSynchronously();
+
+ var outputVideo = FFProbe.Analyse(outputFile);
+
+ Assert.IsTrue(success);
+ Assert.IsTrue(File.Exists(outputFile));
+ Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
+
+ if (scaling?.Size == null)
{
- var input = FFProbe.Analyse(VideoLibrary.LocalVideoWebm.FullName);
- using var inputStream = File.OpenRead(input.Path);
- var processor = FFMpegArguments
- .FromPipeInput(new StreamPipeSource(inputStream))
- .OutputToFile(output, false, opt =>
- {
- foreach (var arg in arguments)
- opt.WithArgument(arg);
- });
-
- var scaling = arguments.OfType().FirstOrDefault();
-
- var success = processor.ProcessSynchronously();
-
- var outputVideo = FFProbe.Analyse(output);
-
- Assert.IsTrue(success);
- Assert.IsTrue(File.Exists(output));
- Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
-
- if (scaling?.Size == null)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
- }
- else
- {
- if (scaling.Size.Value.Width != -1)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
- }
-
- if (scaling.Size.Value.Height != -1)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
- }
-
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
- }
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
}
- finally
+ else
{
- if (File.Exists(output))
- File.Delete(output);
+ if (scaling.Size.Value.Width != -1)
+ {
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
+ }
+
+ if (scaling.Size.Value.Height != -1)
+ {
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
+ }
+
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
}
}
@@ -121,7 +106,7 @@ private void ConvertToStreamPipe(params IArgument[] arguments)
{
using var ms = new MemoryStream();
var processor = FFMpegArguments
- .FromFileInput(VideoLibrary.LocalVideo)
+ .FromFileInput(TestResources.Mp4Video)
.OutputToPipe(new StreamPipeSink(ms), opt =>
{
foreach (var arg in arguments)
@@ -135,7 +120,7 @@ private void ConvertToStreamPipe(params IArgument[] arguments)
ms.Position = 0;
var outputVideo = FFProbe.Analyse(ms);
- var input = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
+ var input = FFProbe.Analyse(TestResources.Mp4Video);
// Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
if (scaling?.Size == null)
@@ -162,53 +147,45 @@ private void ConvertToStreamPipe(params IArgument[] arguments)
public void Convert(ContainerFormat type, Action validationMethod, params IArgument[] arguments)
{
- var output = Input.OutputLocation(type);
+ using var outputFile = new TemporaryFile($"out{type.Extension}");
- try
- {
- var input = FFProbe.Analyse(Input.FullName);
+ var input = FFProbe.Analyse(TestResources.Mp4Video);
- var processor = FFMpegArguments
- .FromFileInput(VideoLibrary.LocalVideo)
- .OutputToFile(output, false, opt =>
+ var processor = FFMpegArguments
+ .FromFileInput(TestResources.Mp4Video)
+ .OutputToFile(outputFile, false, opt =>
{
foreach (var arg in arguments)
opt.WithArgument(arg);
});
- var scaling = arguments.OfType().FirstOrDefault();
- processor.ProcessSynchronously();
+ var scaling = arguments.OfType().FirstOrDefault();
+ processor.ProcessSynchronously();
- var outputVideo = FFProbe.Analyse(output);
+ var outputVideo = FFProbe.Analyse(outputFile);
- Assert.IsTrue(File.Exists(output));
- Assert.AreEqual(outputVideo.Duration.TotalSeconds, input.Duration.TotalSeconds, 0.1);
- validationMethod?.Invoke(outputVideo);
- if (scaling?.Size == null)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
- }
- else
- {
- if (scaling.Size.Value.Width != -1)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
- }
-
- if (scaling.Size.Value.Height != -1)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
- }
-
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
- }
- }
- finally
+ Assert.IsTrue(File.Exists(outputFile));
+ Assert.AreEqual(outputVideo.Duration.TotalSeconds, input.Duration.TotalSeconds, 0.1);
+ validationMethod?.Invoke(outputVideo);
+ if (scaling?.Size == null)
{
- if (File.Exists(output))
- File.Delete(output);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
+ }
+ else
+ {
+ if (scaling.Size.Value.Width != -1)
+ {
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
+ }
+
+ if (scaling.Size.Value.Height != -1)
+ {
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
+ }
+
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
}
}
@@ -219,50 +196,41 @@ public void Convert(ContainerFormat type, params IArgument[] inputArguments)
public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] arguments)
{
- var output = Input.OutputLocation(type);
+ using var outputFile = new TemporaryFile($"out{type.Extension}");
- try
+ var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256));
+ var processor = FFMpegArguments.FromPipeInput(videoFramesSource).OutputToFile(outputFile, false, opt =>
{
- var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256));
- var processor = FFMpegArguments.FromPipeInput(videoFramesSource).OutputToFile(output, false, opt =>
- {
- foreach (var arg in arguments)
- opt.WithArgument(arg);
- });
- var scaling = arguments.OfType().FirstOrDefault();
- processor.ProcessSynchronously();
+ foreach (var arg in arguments)
+ opt.WithArgument(arg);
+ });
+ var scaling = arguments.OfType().FirstOrDefault();
+ processor.ProcessSynchronously();
- var outputVideo = FFProbe.Analyse(output);
+ var outputVideo = FFProbe.Analyse(outputFile);
- Assert.IsTrue(File.Exists(output));
+ Assert.IsTrue(File.Exists(outputFile));
- if (scaling?.Size == null)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, videoFramesSource.Width);
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, videoFramesSource.Height);
- }
- else
- {
- if (scaling.Size.Value.Width != -1)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
- }
-
- if (scaling.Size.Value.Height != -1)
- {
- Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
- }
-
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, videoFramesSource.Width);
- Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, videoFramesSource.Height);
- }
- }
- finally
+ if (scaling?.Size == null)
{
- if (File.Exists(output))
- File.Delete(output);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, videoFramesSource.Width);
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, videoFramesSource.Height);
}
+ else
+ {
+ if (scaling.Size.Value.Width != -1)
+ {
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width);
+ }
+ if (scaling.Size.Value.Height != -1)
+ {
+ Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height);
+ }
+
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, videoFramesSource.Width);
+ Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, videoFramesSource.Height);
+ }
}
[TestMethod, Timeout(10000)]
@@ -287,7 +255,6 @@ public void Video_ToMP4_Args()
[DataTestMethod, Timeout(10000)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
- // [DataRow(PixelFormat.Format48bppRgb)]
public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
{
ConvertFromPipe(VideoType.Mp4, pixelFormat, new VideoCodecArgument(VideoCodec.LibX264));
@@ -307,11 +274,26 @@ await Assert.ThrowsExceptionAsync(async () =>
await using var ms = new MemoryStream();
var pipeSource = new StreamPipeSink(ms);
await FFMpegArguments
- .FromFileInput(VideoLibrary.LocalVideo)
+ .FromFileInput(TestResources.Mp4Video)
.OutputToPipe(pipeSource, opt => opt.ForceFormat("mkv"))
.ProcessAsynchronously();
});
}
+ [TestMethod, Timeout(10000)]
+ public void Video_StreamFile_OutputToMemoryStream()
+ {
+ var output = new MemoryStream();
+
+ FFMpegArguments
+ .FromPipeInput(new StreamPipeSource(File.OpenRead(TestResources.WebmVideo)), options => options.ForceFormat("webm"))
+ .OutputToPipe(new StreamPipeSink(output), options => options
+ .ForceFormat("mpegts"))
+ .ProcessSynchronously();
+
+ output.Position = 0;
+ var result = FFProbe.Analyse(output);
+ Console.WriteLine(result.Duration);
+ }
[TestMethod, Timeout(10000)]
public void Video_ToMP4_Args_StreamOutputPipe_Failure()
@@ -326,7 +308,7 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async()
using var ms = new MemoryStream();
var pipeSource = new StreamPipeSink(ms);
FFMpegArguments
- .FromFileInput(VideoLibrary.LocalVideo)
+ .FromFileInput(TestResources.Mp4Video)
.OutputToPipe(pipeSource, opt => opt
.WithVideoCodec(VideoCodec.LibX264)
.ForceFormat("matroska"))
@@ -337,11 +319,11 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async()
[TestMethod, Timeout(10000)]
public async Task TestDuplicateRun()
{
- FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo)
+ FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile("temporary.mp4")
.ProcessSynchronously();
- await FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo)
+ await FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile("temporary.mp4")
.ProcessAsynchronously();
@@ -372,7 +354,6 @@ public void Video_ToTS_Args()
[DataTestMethod, Timeout(10000)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
- // [DataRow(PixelFormat.Format48bppRgb)]
public void Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
{
ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts));
@@ -447,190 +428,126 @@ public void Video_ToOGV_MultiThread()
[TestMethod, Timeout(10000)]
public void Video_Snapshot_InMemory()
{
- var output = Input.OutputLocation(ImageType.Png);
-
- try
- {
- var input = FFProbe.Analyse(Input.FullName);
-
- 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);
- }
- finally
- {
- if (File.Exists(output))
- File.Delete(output);
- }
+ var input = FFProbe.Analyse(TestResources.Mp4Video);
+ 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);
}
[TestMethod, Timeout(10000)]
public void Video_Snapshot_PersistSnapshot()
{
- var output = Input.OutputLocation(ImageType.Png);
- try
- {
- var input = FFProbe.Analyse(Input.FullName);
+ var outputPath = new TemporaryFile("out.png");
+ var input = FFProbe.Analyse(TestResources.Mp4Video);
- FFMpeg.Snapshot(input, output);
+ FFMpeg.Snapshot(input, outputPath);
- 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);
- bitmap.Dispose();
- }
- finally
- {
- if (File.Exists(output))
- File.Delete(output);
- }
+ using var bitmap = Image.FromFile(outputPath);
+ Assert.AreEqual(input.PrimaryVideoStream.Width, bitmap.Width);
+ Assert.AreEqual(input.PrimaryVideoStream.Height, bitmap.Height);
+ Assert.AreEqual(bitmap.RawFormat, ImageFormat.Png);
}
[TestMethod, Timeout(10000)]
public void Video_Join()
{
- var output = Input.OutputLocation(VideoType.Mp4);
- var newInput = Input.OutputLocation(VideoType.Mp4.Name, "duplicate");
- try
- {
- var input = FFProbe.Analyse(Input.FullName);
- File.Copy(Input.FullName, newInput);
-
- var success = FFMpeg.Join(output, Input.FullName, newInput);
- Assert.IsTrue(success);
-
- Assert.IsTrue(File.Exists(output));
- var expectedDuration = input.Duration * 2;
- var result = FFProbe.Analyse(output);
- Assert.AreEqual(expectedDuration.Days, result.Duration.Days);
- Assert.AreEqual(expectedDuration.Hours, result.Duration.Hours);
- Assert.AreEqual(expectedDuration.Minutes, result.Duration.Minutes);
- Assert.AreEqual(expectedDuration.Seconds, result.Duration.Seconds);
- Assert.AreEqual(input.PrimaryVideoStream.Height, result.PrimaryVideoStream.Height);
- Assert.AreEqual(input.PrimaryVideoStream.Width, result.PrimaryVideoStream.Width);
- }
- finally
- {
- if (File.Exists(output))
- File.Delete(output);
-
- if (File.Exists(newInput))
- File.Delete(newInput);
- }
+ var inputCopy = new TemporaryFile("copy-input.mp4");
+ File.Copy(TestResources.Mp4Video, inputCopy);
+ var outputPath = new TemporaryFile("out.mp4");
+ var input = FFProbe.Analyse(TestResources.Mp4Video);
+ var success = FFMpeg.Join(outputPath, TestResources.Mp4Video, inputCopy);
+ Assert.IsTrue(success);
+ Assert.IsTrue(File.Exists(outputPath));
+
+ var expectedDuration = input.Duration * 2;
+ var result = FFProbe.Analyse(outputPath);
+ Assert.AreEqual(expectedDuration.Days, result.Duration.Days);
+ Assert.AreEqual(expectedDuration.Hours, result.Duration.Hours);
+ Assert.AreEqual(expectedDuration.Minutes, result.Duration.Minutes);
+ Assert.AreEqual(expectedDuration.Seconds, result.Duration.Seconds);
+ Assert.AreEqual(input.PrimaryVideoStream.Height, result.PrimaryVideoStream.Height);
+ Assert.AreEqual(input.PrimaryVideoStream.Width, result.PrimaryVideoStream.Width);
}
[TestMethod, Timeout(10000)]
public void Video_Join_Image_Sequence()
{
- try
- {
- var imageSet = new List();
- Directory.EnumerateFiles(VideoLibrary.ImageDirectory.FullName)
- .Where(file => file.ToLower().EndsWith(".png"))
- .ToList()
- .ForEach(file =>
- {
- for (var i = 0; i < 15; i++)
- {
- imageSet.Add(new ImageInfo(file));
- }
- });
-
- var success = FFMpeg.JoinImageSequence(VideoLibrary.ImageJoinOutput.FullName, images: imageSet.ToArray());
- Assert.IsTrue(success);
- var result = FFProbe.Analyse(VideoLibrary.ImageJoinOutput.FullName);
-
- VideoLibrary.ImageJoinOutput.Refresh();
-
- Assert.IsTrue(VideoLibrary.ImageJoinOutput.Exists);
- Assert.AreEqual(3, result.Duration.Seconds);
- Assert.AreEqual(imageSet.First().Width, result.PrimaryVideoStream.Width);
- Assert.AreEqual(imageSet.First().Height, result.PrimaryVideoStream.Height);
- }
- finally
- {
- VideoLibrary.ImageJoinOutput.Refresh();
- if (VideoLibrary.ImageJoinOutput.Exists)
+ var imageSet = new List();
+ Directory.EnumerateFiles(TestResources.ImageCollection)
+ .Where(file => file.ToLower().EndsWith(".png"))
+ .ToList()
+ .ForEach(file =>
{
- VideoLibrary.ImageJoinOutput.Delete();
- }
- }
+ for (var i = 0; i < 15; i++)
+ {
+ imageSet.Add(new ImageInfo(file));
+ }
+ });
+
+ var outputFile = new TemporaryFile("out.mp4");
+ var success = FFMpeg.JoinImageSequence(outputFile, images: imageSet.ToArray());
+ Assert.IsTrue(success);
+ var result = FFProbe.Analyse(outputFile);
+ Assert.AreEqual(3, result.Duration.Seconds);
+ Assert.AreEqual(imageSet.First().Width, result.PrimaryVideoStream.Width);
+ Assert.AreEqual(imageSet.First().Height, result.PrimaryVideoStream.Height);
}
[TestMethod, Timeout(10000)]
public void Video_With_Only_Audio_Should_Extract_Metadata()
{
- var video = FFProbe.Analyse(VideoLibrary.LocalVideoAudioOnly.FullName);
+ var video = FFProbe.Analyse(TestResources.Mp4WithoutVideo);
Assert.AreEqual(null, video.PrimaryVideoStream);
Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName);
Assert.AreEqual(10, video.Duration.TotalSeconds, 0.5);
- // Assert.AreEqual(1.25, video.Size);
}
[TestMethod, Timeout(10000)]
public void Video_Duration()
{
- var video = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
- var output = Input.OutputLocation(VideoType.Mp4);
+ var video = FFProbe.Analyse(TestResources.Mp4Video);
+ var outputFile = new TemporaryFile("out.mp4");
- try
- {
- FFMpegArguments
- .FromFileInput(VideoLibrary.LocalVideo)
- .OutputToFile(output, false, opt => opt.WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 2)))
- .ProcessSynchronously();
+ FFMpegArguments
+ .FromFileInput(TestResources.Mp4Video)
+ .OutputToFile(outputFile, false, opt => opt.WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 2)))
+ .ProcessSynchronously();
- Assert.IsTrue(File.Exists(output));
- var outputVideo = FFProbe.Analyse(output);
+ Assert.IsTrue(File.Exists(outputFile));
+ var outputVideo = FFProbe.Analyse(outputFile);
- Assert.AreEqual(video.Duration.Days, outputVideo.Duration.Days);
- Assert.AreEqual(video.Duration.Hours, outputVideo.Duration.Hours);
- Assert.AreEqual(video.Duration.Minutes, outputVideo.Duration.Minutes);
- Assert.AreEqual(video.Duration.Seconds - 2, outputVideo.Duration.Seconds);
- }
- finally
- {
- if (File.Exists(output))
- File.Delete(output);
- }
+ Assert.AreEqual(video.Duration.Days, outputVideo.Duration.Days);
+ Assert.AreEqual(video.Duration.Hours, outputVideo.Duration.Hours);
+ Assert.AreEqual(video.Duration.Minutes, outputVideo.Duration.Minutes);
+ Assert.AreEqual(video.Duration.Seconds - 2, outputVideo.Duration.Seconds);
}
[TestMethod, Timeout(10000)]
public void Video_UpdatesProgress()
{
- var output = Input.OutputLocation(VideoType.Mp4);
+ var outputFile = new TemporaryFile("out.mp4");
var percentageDone = 0.0;
var timeDone = TimeSpan.Zero;
void OnPercentageProgess(double percentage) => percentageDone = percentage;
void OnTimeProgess(TimeSpan time) => timeDone = time;
- var analysis = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName);
+ var analysis = FFProbe.Analyse(TestResources.Mp4Video);
+ var success = FFMpegArguments
+ .FromFileInput(TestResources.Mp4Video)
+ .OutputToFile(outputFile, false, opt => opt
+ .WithDuration(TimeSpan.FromSeconds(2)))
+ .NotifyOnProgress(OnPercentageProgess, analysis.Duration)
+ .NotifyOnProgress(OnTimeProgess)
+ .ProcessSynchronously();
-
- try
- {
- var success = FFMpegArguments
- .FromFileInput(VideoLibrary.LocalVideo)
- .OutputToFile(output, false, opt => opt
- .WithDuration(TimeSpan.FromSeconds(2)))
- .NotifyOnProgress(OnPercentageProgess, analysis.Duration)
- .NotifyOnProgress(OnTimeProgess)
- .ProcessSynchronously();
-
- Assert.IsTrue(success);
- Assert.IsTrue(File.Exists(output));
- Assert.AreNotEqual(0.0, percentageDone);
- Assert.AreNotEqual(TimeSpan.Zero, timeDone);
- }
- finally
- {
- if (File.Exists(output))
- File.Delete(output);
- }
+ Assert.IsTrue(success);
+ Assert.IsTrue(File.Exists(outputFile));
+ Assert.AreNotEqual(0.0, percentageDone);
+ Assert.AreNotEqual(TimeSpan.Zero, timeDone);
}
[TestMethod, Timeout(10000)]
@@ -656,11 +573,11 @@ public void Video_TranscodeInMemory()
[TestMethod, Timeout(10000)]
public async Task Video_Cancel_Async()
{
- var output = Input.OutputLocation(VideoType.Mp4);
+ var outputFile = new TemporaryFile("out.mp4");
var task = FFMpegArguments
- .FromFileInput(VideoLibrary.LocalVideo)
- .OutputToFile(output, false, opt => opt
+ .FromFileInput(TestResources.Mp4Video)
+ .OutputToFile(outputFile, false, opt => opt
.Resize(new Size(1000, 1000))
.WithAudioCodec(AudioCodec.Aac)
.WithVideoCodec(VideoCodec.LibX264)
@@ -670,19 +587,11 @@ public async Task Video_Cancel_Async()
.CancellableThrough(out var cancel)
.ProcessAsynchronously(false);
- try
- {
- await Task.Delay(300);
- cancel();
+ await Task.Delay(300);
+ cancel();
- var result = await task;
- Assert.IsFalse(result);
- }
- finally
- {
- if (File.Exists(output))
- File.Delete(output);
- }
+ var result = await task;
+ Assert.IsFalse(result);
}
}
}
diff --git a/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs b/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs
index 17d0372..479fa90 100644
--- a/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs
+++ b/FFMpegCore/FFMpeg/Arguments/InputPipeArgument.cs
@@ -17,14 +17,14 @@ public InputPipeArgument(IPipeSource writer) : base(PipeDirection.Out)
Writer = writer;
}
- public override string Text => $"-y {Writer.GetFormat()} -i \"{PipePath}\"";
+ public override string Text => $"-y {Writer.GetStreamArguments()} -i \"{PipePath}\"";
protected override async Task ProcessDataAsync(CancellationToken token)
{
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
if (!Pipe.IsConnected)
throw new TaskCanceledException();
- await Writer.CopyAsync(Pipe, token).ConfigureAwait(false);
+ await Writer.WriteAsync(Pipe, token).ConfigureAwait(false);
}
}
}
diff --git a/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs b/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs
index ebf1e7f..f089a1e 100644
--- a/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs
+++ b/FFMpegCore/FFMpeg/Arguments/OutputPipeArgument.cs
@@ -21,7 +21,7 @@ protected override async Task ProcessDataAsync(CancellationToken token)
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
if (!Pipe.IsConnected)
throw new TaskCanceledException();
- await Reader.CopyAsync(Pipe, token).ConfigureAwait(false);
+ await Reader.ReadAsync(Pipe, token).ConfigureAwait(false);
}
}
}
diff --git a/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs b/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs
index 4a6113a..428f21b 100644
--- a/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs
+++ b/FFMpegCore/FFMpeg/Arguments/PipeArgument.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
@@ -30,6 +31,7 @@ public void Pre()
public void Post()
{
+ Debug.WriteLine($"Disposing NamedPipeServerStream on {GetType().Name}");
Pipe?.Dispose();
Pipe = null!;
}
@@ -39,11 +41,13 @@ public async Task During(CancellationToken cancellationToken = default)
try
{
await ProcessDataAsync(cancellationToken);
+ Debug.WriteLine($"Disconnecting NamedPipeServerStream on {GetType().Name}");
+ Pipe?.Disconnect();
}
catch (TaskCanceledException)
{
+ Debug.WriteLine($"ProcessDataAsync on {GetType().Name} cancelled");
}
- Pipe.Disconnect();
}
protected abstract Task ProcessDataAsync(CancellationToken token);
diff --git a/FFMpegCore/FFMpeg/Enums/ContainerFormat.cs b/FFMpegCore/FFMpeg/Enums/ContainerFormat.cs
index 8c046ac..37c2bda 100644
--- a/FFMpegCore/FFMpeg/Enums/ContainerFormat.cs
+++ b/FFMpegCore/FFMpeg/Enums/ContainerFormat.cs
@@ -4,7 +4,7 @@ namespace FFMpegCore.Enums
{
public class ContainerFormat
{
- private static readonly Regex _formatRegex = new Regex(@"([D ])([E ])\s+([a-z0-9_]+)\s+(.+)");
+ private static readonly Regex FormatRegex = new Regex(@"([D ])([E ])\s+([a-z0-9_]+)\s+(.+)");
public string Name { get; private set; }
public bool DemuxingSupported { get; private set; }
@@ -27,17 +27,19 @@ internal ContainerFormat(string name)
internal static bool TryParse(string line, out ContainerFormat fmt)
{
- var match = _formatRegex.Match(line);
+ var match = FormatRegex.Match(line);
if (!match.Success)
{
fmt = null!;
return false;
}
- fmt = new ContainerFormat(match.Groups[3].Value);
- fmt.DemuxingSupported = match.Groups[1].Value == " ";
- fmt.MuxingSupported = match.Groups[2].Value == " ";
- fmt.Description = match.Groups[4].Value;
+ fmt = new ContainerFormat(match.Groups[3].Value)
+ {
+ DemuxingSupported = match.Groups[1].Value == " ",
+ MuxingSupported = match.Groups[2].Value == " ",
+ Description = match.Groups[4].Value
+ };
return true;
}
}
diff --git a/FFMpegCore/FFMpeg/Exceptions/FFMpegException.cs b/FFMpegCore/FFMpeg/Exceptions/FFMpegException.cs
index 6bd608d..fc154ac 100644
--- a/FFMpegCore/FFMpeg/Exceptions/FFMpegException.cs
+++ b/FFMpegCore/FFMpeg/Exceptions/FFMpegException.cs
@@ -13,15 +13,16 @@ public enum FFMpegExceptionType
public class FFMpegException : Exception
{
-
- public FFMpegException(FFMpegExceptionType type, string? message = null, Exception? innerException = null, string ffMpegErrorOutput = "")
+ public FFMpegException(FFMpegExceptionType type, string? message = null, Exception? innerException = null, string ffmpegErrorOutput = "", string ffmpegOutput = "")
: base(message, innerException)
{
- FFMpegErrorOutput = ffMpegErrorOutput;
+ FfmpegOutput = ffmpegOutput;
+ FfmpegErrorOutput = ffmpegErrorOutput;
Type = type;
}
public FFMpegExceptionType Type { get; }
- public string FFMpegErrorOutput { get; }
+ public string FfmpegOutput { get; }
+ public string FfmpegErrorOutput { get; }
}
}
\ No newline at end of file
diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs
index 8a442ed..cc611e3 100644
--- a/FFMpegCore/FFMpeg/FFMpeg.cs
+++ b/FFMpegCore/FFMpeg/FFMpeg.cs
@@ -72,7 +72,8 @@ public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan
.ProcessSynchronously();
ms.Position = 0;
- return new Bitmap(ms);
+ using var bitmap = new Bitmap(ms);
+ return bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat);
}
///
/// Saves a 'png' thumbnail to an in-memory bitmap
diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs
index 92fa6fe..5961ed3 100644
--- a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs
+++ b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs
@@ -70,20 +70,20 @@ void OnCancelEvent(object sender, EventArgs args)
}
catch (Exception e)
{
- if (!HandleException(throwOnError, e, instance.ErrorData)) return false;
+ if (!HandleException(throwOnError, e, instance.ErrorData, instance.OutputData)) return false;
}
finally
{
CancelEvent -= OnCancelEvent;
}
- return HandleCompletion(throwOnError, errorCode, instance.ErrorData);
+ return HandleCompletion(throwOnError, errorCode, instance.ErrorData, instance.OutputData);
}
- private bool HandleCompletion(bool throwOnError, int errorCode, IReadOnlyList errorData)
+ private bool HandleCompletion(bool throwOnError, int errorCode, IReadOnlyList errorData, IReadOnlyList outputData)
{
if (throwOnError && errorCode != 0)
- throw new FFMpegException(FFMpegExceptionType.Conversion, string.Join("\n", errorData));
+ throw new FFMpegException(FFMpegExceptionType.Conversion, "FFMpeg exited with non-zero exitcode.", null, string.Join("\n", errorData), string.Join("\n", outputData));
_onPercentageProgress?.Invoke(100.0);
if (_totalTimespan.HasValue) _onTimeProgress?.Invoke(_totalTimespan.Value);
@@ -98,7 +98,7 @@ public async Task ProcessAsynchronously(bool throwOnError = true)
void OnCancelEvent(object sender, EventArgs args)
{
- instance?.SendInput("q");
+ instance.SendInput("q");
cancellationTokenSource.Cancel();
instance.Started = false;
}
@@ -116,14 +116,14 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
}
catch (Exception e)
{
- if (!HandleException(throwOnError, e, instance.ErrorData)) return false;
+ if (!HandleException(throwOnError, e, instance.ErrorData, instance.OutputData)) return false;
}
finally
{
CancelEvent -= OnCancelEvent;
}
- return HandleCompletion(throwOnError, errorCode, instance.ErrorData);
+ return HandleCompletion(throwOnError, errorCode, instance.ErrorData, instance.OutputData);
}
private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSource)
@@ -140,13 +140,12 @@ private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSo
}
- private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList errorData)
+ private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList errorData, IReadOnlyList outputData)
{
if (!throwOnError)
return false;
- throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e,
- string.Join("\n", errorData));
+ throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e, string.Join("\n", errorData), string.Join("\n", outputData));
}
private void OutputData(object sender, (DataType Type, string Data) msg)
diff --git a/FFMpegCore/FFMpeg/FFMpegOptions.cs b/FFMpegCore/FFMpeg/FFMpegOptions.cs
index 8a98a0a..947f942 100644
--- a/FFMpegCore/FFMpeg/FFMpegOptions.cs
+++ b/FFMpegCore/FFMpeg/FFMpegOptions.cs
@@ -32,7 +32,7 @@ static FFMpegOptions()
{
if (File.Exists(ConfigFile))
{
- Options = JsonSerializer.Deserialize(File.ReadAllText(ConfigFile));
+ Options = JsonSerializer.Deserialize(File.ReadAllText(ConfigFile))!;
foreach (var pair in DefaultExtensionsOverrides)
if (!Options.ExtensionOverrides.ContainsKey(pair.Key)) Options.ExtensionOverrides.Add(pair.Key, pair.Value);
}
diff --git a/FFMpegCore/FFMpeg/Pipes/IPipeSink.cs b/FFMpegCore/FFMpeg/Pipes/IPipeSink.cs
index 8010d87..875407e 100644
--- a/FFMpegCore/FFMpeg/Pipes/IPipeSink.cs
+++ b/FFMpegCore/FFMpeg/Pipes/IPipeSink.cs
@@ -5,7 +5,7 @@ namespace FFMpegCore.Pipes
{
public interface IPipeSink
{
- Task CopyAsync(System.IO.Stream inputStream, CancellationToken cancellationToken);
+ Task ReadAsync(System.IO.Stream inputStream, CancellationToken cancellationToken);
string GetFormat();
}
}
diff --git a/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs b/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs
index 35766d0..cdd5139 100644
--- a/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs
+++ b/FFMpegCore/FFMpeg/Pipes/IPipeSource.cs
@@ -8,7 +8,7 @@ namespace FFMpegCore.Pipes
///
public interface IPipeSource
{
- string GetFormat();
- Task CopyAsync(System.IO.Stream outputStream, CancellationToken cancellationToken);
+ string GetStreamArguments();
+ Task WriteAsync(System.IO.Stream outputStream, CancellationToken cancellationToken);
}
}
diff --git a/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs b/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs
index 8739a40..f61bb7c 100644
--- a/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs
+++ b/FFMpegCore/FFMpeg/Pipes/RawVideoPipeSource.cs
@@ -25,7 +25,7 @@ public RawVideoPipeSource(IEnumerator framesEnumerator)
public RawVideoPipeSource(IEnumerable framesEnumerator) : this(framesEnumerator.GetEnumerator()) { }
- public string GetFormat()
+ public string GetStreamArguments()
{
if (!_formatInitialized)
{
@@ -45,7 +45,7 @@ public string GetFormat()
return $"-f rawvideo -r {FrameRate} -pix_fmt {StreamFormat} -s {Width}x{Height}";
}
- public async Task CopyAsync(System.IO.Stream outputStream, CancellationToken cancellationToken)
+ public async Task WriteAsync(System.IO.Stream outputStream, CancellationToken cancellationToken)
{
if (_framesEnumerator.Current != null)
{
diff --git a/FFMpegCore/FFMpeg/Pipes/StreamPipeSink.cs b/FFMpegCore/FFMpeg/Pipes/StreamPipeSink.cs
index ca2246f..cd13f40 100644
--- a/FFMpegCore/FFMpeg/Pipes/StreamPipeSink.cs
+++ b/FFMpegCore/FFMpeg/Pipes/StreamPipeSink.cs
@@ -1,21 +1,27 @@
-using System.Threading;
+using System;
+using System.IO;
+using System.Threading;
using System.Threading.Tasks;
namespace FFMpegCore.Pipes
{
public class StreamPipeSink : IPipeSink
{
- public System.IO.Stream Destination { get; }
+ public Func Writer { get; }
public int BlockSize { get; set; } = 4096;
public string Format { get; set; } = string.Empty;
- public StreamPipeSink(System.IO.Stream destination)
+ public StreamPipeSink(Func writer)
{
- Destination = destination;
+ Writer = writer;
+ }
+ public StreamPipeSink(Stream destination)
+ {
+ Writer = (inputStream, cancellationToken) => inputStream.CopyToAsync(destination, BlockSize, cancellationToken);
}
- public Task CopyAsync(System.IO.Stream inputStream, CancellationToken cancellationToken) =>
- inputStream.CopyToAsync(Destination, BlockSize, cancellationToken);
+ public Task ReadAsync(System.IO.Stream inputStream, CancellationToken cancellationToken)
+ => Writer(inputStream, cancellationToken);
public string GetFormat() => Format;
}
diff --git a/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs b/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs
index db41eb7..404029f 100644
--- a/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs
+++ b/FFMpegCore/FFMpeg/Pipes/StreamPipeSource.cs
@@ -17,8 +17,8 @@ public StreamPipeSource(System.IO.Stream source)
Source = source;
}
- public Task CopyAsync(System.IO.Stream outputStream, CancellationToken cancellationToken) => Source.CopyToAsync(outputStream, BlockSize, cancellationToken);
+ public string GetStreamArguments() => StreamFormat;
- public string GetFormat() => StreamFormat;
+ public Task WriteAsync(System.IO.Stream outputStream, CancellationToken cancellationToken) => Source.CopyToAsync(outputStream, BlockSize, cancellationToken);
}
}
diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj
index 057f605..43247f7 100644
--- a/FFMpegCore/FFMpegCore.csproj
+++ b/FFMpegCore/FFMpegCore.csproj
@@ -9,10 +9,10 @@
3.0.0.0
3.0.0.0
3.0.0.0
- - Fix hanging pipes on unix sockets
-- Internal API cleanup
+ - Also include ffmpeg output data on non-zero exit code
8
- 3.1.0
+ 3.2.4
+ MIT
Malte Rosenbjerg, Vlad Jerca
ffmpeg ffprobe convert video audio mediafile resize analyze muxing
GitHub
@@ -29,8 +29,8 @@
-
-
+
+
diff --git a/FFMpegCore/FFMpegCore.csproj.DotSettings b/FFMpegCore/FFMpegCore.csproj.DotSettings
new file mode 100644
index 0000000..69be7ec
--- /dev/null
+++ b/FFMpegCore/FFMpegCore.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs
index f650371..076bbfd 100644
--- a/FFMpegCore/FFProbe/FFProbe.cs
+++ b/FFMpegCore/FFProbe/FFProbe.cs
@@ -46,7 +46,7 @@ public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.Max
}
var exitCode = task.ConfigureAwait(false).GetAwaiter().GetResult();
if (exitCode != 0)
- throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}: {string.Join("\n", instance.OutputData)} {string.Join("\n", instance.ErrorData)}");
+ throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}", null, string.Join("\n", instance.ErrorData), string.Join("\n", instance.OutputData));
return ParseOutput(pipeArgument.PipePath, instance);
}
@@ -86,7 +86,7 @@ public static async Task AnalyseAsync(Stream stream, int outputC
}
var exitCode = await task;
if (exitCode != 0)
- throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}: {string.Join("\n", instance.OutputData)} {string.Join("\n", instance.ErrorData)}");
+ throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}", null, string.Join("\n", instance.ErrorData), string.Join("\n", instance.OutputData));
pipeArgument.Post();
return ParseOutput(pipeArgument.PipePath, instance);
@@ -98,7 +98,7 @@ private static IMediaAnalysis ParseOutput(string filePath, Instance instance)
var ffprobeAnalysis = JsonSerializer.Deserialize(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
- });
+ })!;
return new MediaAnalysis(filePath, ffprobeAnalysis);
}
@@ -106,7 +106,7 @@ private static Instance PrepareInstance(string filePath, int outputCapacity)
{
FFProbeHelper.RootExceptionCheck();
FFProbeHelper.VerifyFFProbeExists();
- var arguments = $"-print_format json -show_format -sexagesimal -show_streams \"{filePath}\"";
+ var arguments = $"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"";
var instance = new Instance(FFMpegOptions.Options.FFProbeBinary(), arguments) {DataBufferCapacity = outputCapacity};
return instance;
}