This commit is contained in:
Malte Rosenbjerg 2020-12-07 00:47:47 +01:00
parent 8b45a6b680
commit 3ad127a682
10 changed files with 313 additions and 446 deletions

View file

@ -6,7 +6,7 @@
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {
[TestClass] [TestClass]
public class ArgumentBuilderTest : BaseTest public class ArgumentBuilderTest
{ {
private readonly string[] _concatFiles = { "1.mp4", "2.mp4", "3.mp4", "4.mp4"}; private readonly string[] _concatFiles = { "1.mp4", "2.mp4", "3.mp4", "4.mp4"};

View file

@ -3,80 +3,60 @@
using FFMpegCore.Test.Resources; using FFMpegCore.Test.Resources;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO; using System.IO;
using System.Linq;
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {
[TestClass] [TestClass]
public class AudioTest : BaseTest public class AudioTest
{ {
[TestMethod] [TestMethod]
public void Audio_Remove() public void Audio_Remove()
{ {
var output = Input.OutputLocation(VideoType.Mp4); using var outputFile = new TemporaryFile("out.mp4");
try FFMpeg.Mute(TestResources.Mp4Video, outputFile);
{ var analysis = FFProbe.Analyse(outputFile);
FFMpeg.Mute(Input.FullName, output);
Assert.IsTrue(File.Exists(output)); Assert.IsTrue(analysis.VideoStreams.Any());
} Assert.IsTrue(!analysis.AudioStreams.Any());
finally
{
if (File.Exists(output)) File.Delete(output);
}
} }
[TestMethod] [TestMethod]
public void Audio_Save() public void Audio_Save()
{ {
var output = Input.OutputLocation(AudioType.Mp3); using var outputFile = new TemporaryFile("out.mp3");
try FFMpeg.ExtractAudio(TestResources.Mp4Video, outputFile);
{ var analysis = FFProbe.Analyse(outputFile);
FFMpeg.ExtractAudio(Input.FullName, output);
Assert.IsTrue(File.Exists(output)); Assert.IsTrue(!analysis.VideoStreams.Any());
} Assert.IsTrue(analysis.AudioStreams.Any());
finally
{
if (File.Exists(output)) File.Delete(output);
}
} }
[TestMethod] [TestMethod]
public void Audio_Add() public void Audio_Add()
{ {
var output = Input.OutputLocation(VideoType.Mp4); using var outputFile = new TemporaryFile("out.mp4");
try
{ var success = FFMpeg.ReplaceAudio(TestResources.Mp4WithoutAudio, TestResources.Mp3Audio, outputFile);
var success = FFMpeg.ReplaceAudio(VideoLibrary.LocalVideoNoAudio.FullName, VideoLibrary.LocalAudio.FullName, output); var videoAnalysis = FFProbe.Analyse(TestResources.Mp4WithoutAudio);
Assert.IsTrue(success); var audioAnalysis = FFProbe.Analyse(TestResources.Mp3Audio);
var audioAnalysis = FFProbe.Analyse(VideoLibrary.LocalVideoNoAudio.FullName); var outputAnalysis = FFProbe.Analyse(outputFile);
var videoAnalysis = FFProbe.Analyse(VideoLibrary.LocalAudio.FullName);
var outputAnalysis = FFProbe.Analyse(output); Assert.IsTrue(success);
Assert.AreEqual(Math.Max(videoAnalysis.Duration.TotalSeconds, audioAnalysis.Duration.TotalSeconds), outputAnalysis.Duration.TotalSeconds, 0.15); Assert.AreEqual(Math.Max(videoAnalysis.Duration.TotalSeconds, audioAnalysis.Duration.TotalSeconds), outputAnalysis.Duration.TotalSeconds, 0.15);
Assert.IsTrue(File.Exists(output)); Assert.IsTrue(File.Exists(outputFile));
}
finally
{
if (File.Exists(output)) File.Delete(output);
}
} }
[TestMethod] [TestMethod]
public void Image_AddAudio() public void Image_AddAudio()
{ {
var output = Input.OutputLocation(VideoType.Mp4); using var outputFile = new TemporaryFile("out.mp4");
FFMpeg.PosterWithAudio(TestResources.PngImage, TestResources.Mp3Audio, outputFile);
try var analysis = FFProbe.Analyse(TestResources.Mp3Audio);
{ Assert.IsTrue(analysis.Duration.TotalSeconds > 0);
FFMpeg.PosterWithAudio(VideoLibrary.LocalCover.FullName, VideoLibrary.LocalAudio.FullName, output); Assert.IsTrue(File.Exists(outputFile));
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);
}
} }
} }
} }

View file

@ -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;
}
}
}

View file

@ -11,16 +11,26 @@ public class FFProbeTests
[TestMethod] [TestMethod]
public void Probe_TooLongOutput() public void Probe_TooLongOutput()
{ {
Assert.ThrowsException<System.Text.Json.JsonException>(() => FFProbe.Analyse(VideoLibrary.LocalVideo.FullName, 5)); Assert.ThrowsException<System.Text.Json.JsonException>(() => FFProbe.Analyse(TestResources.Mp4Video, 5));
}
[TestMethod]
public async Task Audio_FromStream_Duration()
{
var fileAnalysis = await FFProbe.AnalyseAsync(TestResources.Mp4Video);
await using var inputStream = File.OpenRead(TestResources.Mp4Video);
var streamAnalysis = await FFProbe.AnalyseAsync(inputStream);
Assert.IsTrue(fileAnalysis.Duration == streamAnalysis.Duration);
} }
[TestMethod] [TestMethod]
public void Probe_Success() 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(3, info.Duration.Seconds);
Assert.AreEqual(".mp4", info.Extension); 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("5.1", info.PrimaryAudioStream.ChannelLayout);
Assert.AreEqual(6, info.PrimaryAudioStream.Channels); Assert.AreEqual(6, info.PrimaryAudioStream.Channels);
@ -47,14 +57,14 @@ public void Probe_Success()
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public async Task Probe_Async_Success() 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); Assert.AreEqual(3, info.Duration.Seconds);
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public void Probe_Success_FromStream() 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); var info = FFProbe.Analyse(stream);
Assert.AreEqual(3, info.Duration.Seconds); Assert.AreEqual(3, info.Duration.Seconds);
} }
@ -62,7 +72,7 @@ public void Probe_Success_FromStream()
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public async Task Probe_Success_FromStream_Async() 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); var info = await FFProbe.AnalyseAsync(stream);
Assert.AreEqual(3, info.Duration.Seconds); Assert.AreEqual(3, info.Duration.Seconds);
} }

View file

@ -0,0 +1,27 @@
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 Mp3Audio = "./Resources/audio.mp3";
public static readonly string PngImage = "./Resources/cover.png";
public static readonly string ImageCollection = "./Resources/images";
}
}

View file

@ -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}";
}
}
}

View file

@ -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);
}
}
}

View file

@ -15,105 +15,90 @@
namespace FFMpegCore.Test namespace FFMpegCore.Test
{ {
[TestClass] [TestClass]
public class VideoTest : BaseTest public class VideoTest
{ {
public bool Convert(ContainerFormat type, bool multithreaded = false, VideoSize size = VideoSize.Original) 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); Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
FFMpeg.Convert(input, output, type, size: size, multithreaded: multithreaded); Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
var outputVideo = FFProbe.Analyse(output); }
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)); return File.Exists(outputFile) &&
Assert.AreEqual(outputVideo.Duration.TotalSeconds, input.Duration.TotalSeconds, 0.1); outputVideo.Duration == input.Duration &&
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 &&
( (
(
size == VideoSize.Original && size == VideoSize.Original &&
outputVideo.PrimaryVideoStream.Width == input.PrimaryVideoStream.Width && outputVideo.PrimaryVideoStream.Width == input.PrimaryVideoStream.Width &&
outputVideo.PrimaryVideoStream.Height == input.PrimaryVideoStream.Height outputVideo.PrimaryVideoStream.Height == input.PrimaryVideoStream.Height
) || ) ||
( (
size != VideoSize.Original && size != VideoSize.Original &&
outputVideo.PrimaryVideoStream.Width != input.PrimaryVideoStream.Width && outputVideo.PrimaryVideoStream.Width != input.PrimaryVideoStream.Width &&
outputVideo.PrimaryVideoStream.Height != input.PrimaryVideoStream.Height && outputVideo.PrimaryVideoStream.Height != input.PrimaryVideoStream.Height &&
outputVideo.PrimaryVideoStream.Height == (int)size outputVideo.PrimaryVideoStream.Height == (int)size
) )
); );
}
finally
{
if (File.Exists(output))
File.Delete(output);
}
} }
private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] arguments) private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] arguments)
{ {
var output = Input.OutputLocation(type); using var outputFile = new TemporaryFile($"out{type.Extension}");
try 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);
});
var scaling = arguments.OfType<ScaleArgument>().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); Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
using var inputStream = File.OpenRead(input.Path); Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height);
var processor = FFMpegArguments
.FromPipeInput(new StreamPipeSource(inputStream))
.OutputToFile(output, false, opt =>
{
foreach (var arg in arguments)
opt.WithArgument(arg);
});
var scaling = arguments.OfType<ScaleArgument>().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);
}
} }
finally else
{ {
if (File.Exists(output)) if (scaling.Size.Value.Width != -1)
File.Delete(output); {
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(); using var ms = new MemoryStream();
var processor = FFMpegArguments var processor = FFMpegArguments
.FromFileInput(VideoLibrary.LocalVideo) .FromFileInput(TestResources.Mp4Video)
.OutputToPipe(new StreamPipeSink(ms), opt => .OutputToPipe(new StreamPipeSink(ms), opt =>
{ {
foreach (var arg in arguments) foreach (var arg in arguments)
@ -135,7 +120,7 @@ private void ConvertToStreamPipe(params IArgument[] arguments)
ms.Position = 0; ms.Position = 0;
var outputVideo = FFProbe.Analyse(ms); 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); // Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
if (scaling?.Size == null) if (scaling?.Size == null)
@ -162,53 +147,45 @@ private void ConvertToStreamPipe(params IArgument[] arguments)
public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] arguments) public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] arguments)
{ {
var output = Input.OutputLocation(type); using var outputFile = new TemporaryFile($"out{type.Extension}");
try var input = FFProbe.Analyse(TestResources.Mp4Video);
{
var input = FFProbe.Analyse(Input.FullName);
var processor = FFMpegArguments var processor = FFMpegArguments
.FromFileInput(VideoLibrary.LocalVideo) .FromFileInput(TestResources.Mp4Video)
.OutputToFile(output, false, opt => .OutputToFile(outputFile, false, opt =>
{ {
foreach (var arg in arguments) foreach (var arg in arguments)
opt.WithArgument(arg); opt.WithArgument(arg);
}); });
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault(); var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
processor.ProcessSynchronously(); processor.ProcessSynchronously();
var outputVideo = FFProbe.Analyse(output); var outputVideo = FFProbe.Analyse(outputFile);
Assert.IsTrue(File.Exists(output)); Assert.IsTrue(File.Exists(outputFile));
Assert.AreEqual(outputVideo.Duration.TotalSeconds, input.Duration.TotalSeconds, 0.1); Assert.AreEqual(outputVideo.Duration.TotalSeconds, input.Duration.TotalSeconds, 0.1);
validationMethod?.Invoke(outputVideo); validationMethod?.Invoke(outputVideo);
if (scaling?.Size == null) 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
{ {
if (File.Exists(output)) Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width);
File.Delete(output); 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) 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)); foreach (var arg in arguments)
var processor = FFMpegArguments.FromPipeInput(videoFramesSource).OutputToFile(output, false, opt => opt.WithArgument(arg);
{ });
foreach (var arg in arguments) var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
opt.WithArgument(arg); processor.ProcessSynchronously();
});
var scaling = arguments.OfType<ScaleArgument>().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) 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 (File.Exists(output)) Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, videoFramesSource.Width);
File.Delete(output); 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)] [TestMethod, Timeout(10000)]
@ -287,7 +255,6 @@ public void Video_ToMP4_Args()
[DataTestMethod, Timeout(10000)] [DataTestMethod, Timeout(10000)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)] [DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)] [DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
// [DataRow(PixelFormat.Format48bppRgb)]
public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat) public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
{ {
ConvertFromPipe(VideoType.Mp4, pixelFormat, new VideoCodecArgument(VideoCodec.LibX264)); ConvertFromPipe(VideoType.Mp4, pixelFormat, new VideoCodecArgument(VideoCodec.LibX264));
@ -307,7 +274,7 @@ await Assert.ThrowsExceptionAsync<FFMpegException>(async () =>
await using var ms = new MemoryStream(); await using var ms = new MemoryStream();
var pipeSource = new StreamPipeSink(ms); var pipeSource = new StreamPipeSink(ms);
await FFMpegArguments await FFMpegArguments
.FromFileInput(VideoLibrary.LocalVideo) .FromFileInput(TestResources.Mp4Video)
.OutputToPipe(pipeSource, opt => opt.ForceFormat("mkv")) .OutputToPipe(pipeSource, opt => opt.ForceFormat("mkv"))
.ProcessAsynchronously(); .ProcessAsynchronously();
}); });
@ -315,14 +282,12 @@ await FFMpegArguments
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public void Video_StreamFile_OutputToMemoryStream() public void Video_StreamFile_OutputToMemoryStream()
{ {
// using var input = File.OpenRead(VideoLibrary.LocalVideo.FullName);
var output = new MemoryStream(); var output = new MemoryStream();
FFMpegArguments FFMpegArguments
// .FromFileInput(VideoLibrary.LocalVideo.FullName) .FromPipeInput(new StreamPipeSource(File.OpenRead(TestResources.WebmVideo)), options => options.ForceFormat("webm"))
.FromPipeInput(new StreamPipeSource(File.OpenRead(VideoLibrary.LocalVideoWebm.FullName)), options => options.ForceFormat("webm"))
.OutputToPipe(new StreamPipeSink(output), options => options .OutputToPipe(new StreamPipeSink(output), options => options
.ForceFormat("mp4")) .ForceFormat("mpegts"))
.ProcessSynchronously(); .ProcessSynchronously();
output.Position = 0; output.Position = 0;
@ -343,7 +308,7 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async()
using var ms = new MemoryStream(); using var ms = new MemoryStream();
var pipeSource = new StreamPipeSink(ms); var pipeSource = new StreamPipeSink(ms);
FFMpegArguments FFMpegArguments
.FromFileInput(VideoLibrary.LocalVideo) .FromFileInput(TestResources.Mp4Video)
.OutputToPipe(pipeSource, opt => opt .OutputToPipe(pipeSource, opt => opt
.WithVideoCodec(VideoCodec.LibX264) .WithVideoCodec(VideoCodec.LibX264)
.ForceFormat("matroska")) .ForceFormat("matroska"))
@ -354,11 +319,11 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async()
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public async Task TestDuplicateRun() public async Task TestDuplicateRun()
{ {
FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo) FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile("temporary.mp4") .OutputToFile("temporary.mp4")
.ProcessSynchronously(); .ProcessSynchronously();
await FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo) await FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile("temporary.mp4") .OutputToFile("temporary.mp4")
.ProcessAsynchronously(); .ProcessAsynchronously();
@ -389,7 +354,6 @@ public void Video_ToTS_Args()
[DataTestMethod, Timeout(10000)] [DataTestMethod, Timeout(10000)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)] [DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)]
[DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)] [DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)]
// [DataRow(PixelFormat.Format48bppRgb)]
public void Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat) public void Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat)
{ {
ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts)); ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts));
@ -464,190 +428,126 @@ public void Video_ToOGV_MultiThread()
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public void Video_Snapshot_InMemory() public void Video_Snapshot_InMemory()
{ {
var output = Input.OutputLocation(ImageType.Png); var input = FFProbe.Analyse(TestResources.Mp4Video);
using var bitmap = FFMpeg.Snapshot(input);
try Assert.AreEqual(input.PrimaryVideoStream.Width, bitmap.Width);
{ Assert.AreEqual(input.PrimaryVideoStream.Height, bitmap.Height);
var input = FFProbe.Analyse(Input.FullName); Assert.AreEqual(bitmap.RawFormat, ImageFormat.Png);
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);
}
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public void Video_Snapshot_PersistSnapshot() public void Video_Snapshot_PersistSnapshot()
{ {
var output = Input.OutputLocation(ImageType.Png); var outputPath = new TemporaryFile("out.png");
try var input = FFProbe.Analyse(TestResources.Mp4Video);
{
var input = FFProbe.Analyse(Input.FullName);
FFMpeg.Snapshot(input, output); FFMpeg.Snapshot(input, outputPath);
var bitmap = Image.FromFile(output); using var bitmap = Image.FromFile(outputPath);
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);
Assert.AreEqual(bitmap.RawFormat, ImageFormat.Png); Assert.AreEqual(bitmap.RawFormat, ImageFormat.Png);
bitmap.Dispose();
}
finally
{
if (File.Exists(output))
File.Delete(output);
}
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public void Video_Join() public void Video_Join()
{ {
var output = Input.OutputLocation(VideoType.Mp4); var inputCopy = new TemporaryFile("copy-input.mp4");
var newInput = Input.OutputLocation(VideoType.Mp4.Name, "duplicate"); File.Copy(TestResources.Mp4Video, inputCopy);
try
{
var input = FFProbe.Analyse(Input.FullName);
File.Copy(Input.FullName, newInput);
var success = FFMpeg.Join(output, Input.FullName, newInput); var outputPath = new TemporaryFile("out.mp4");
Assert.IsTrue(success); var input = FFProbe.Analyse(TestResources.Mp4Video);
var success = FFMpeg.Join(outputPath, TestResources.Mp4Video, inputCopy);
Assert.IsTrue(File.Exists(output)); Assert.IsTrue(success);
var expectedDuration = input.Duration * 2; Assert.IsTrue(File.Exists(outputPath));
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 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)] [TestMethod, Timeout(10000)]
public void Video_Join_Image_Sequence() public void Video_Join_Image_Sequence()
{ {
try var imageSet = new List<ImageInfo>();
{ Directory.EnumerateFiles(TestResources.ImageCollection)
var imageSet = new List<ImageInfo>(); .Where(file => file.ToLower().EndsWith(".png"))
Directory.EnumerateFiles(VideoLibrary.ImageDirectory.FullName) .ToList()
.Where(file => file.ToLower().EndsWith(".png")) .ForEach(file =>
.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)
{ {
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)] [TestMethod, Timeout(10000)]
public void Video_With_Only_Audio_Should_Extract_Metadata() 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(null, video.PrimaryVideoStream);
Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName); Assert.AreEqual("aac", video.PrimaryAudioStream.CodecName);
Assert.AreEqual(10, video.Duration.TotalSeconds, 0.5); Assert.AreEqual(10, video.Duration.TotalSeconds, 0.5);
// Assert.AreEqual(1.25, video.Size);
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public void Video_Duration() public void Video_Duration()
{ {
var video = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName); var video = FFProbe.Analyse(TestResources.Mp4Video);
var output = Input.OutputLocation(VideoType.Mp4); var outputFile = new TemporaryFile("out.mp4");
try FFMpegArguments
{ .FromFileInput(TestResources.Mp4Video)
FFMpegArguments .OutputToFile(outputFile, false, opt => opt.WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 2)))
.FromFileInput(VideoLibrary.LocalVideo) .ProcessSynchronously();
.OutputToFile(output, false, opt => opt.WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 2)))
.ProcessSynchronously();
Assert.IsTrue(File.Exists(output)); Assert.IsTrue(File.Exists(outputFile));
var outputVideo = FFProbe.Analyse(output); var outputVideo = FFProbe.Analyse(outputFile);
Assert.AreEqual(video.Duration.Days, outputVideo.Duration.Days); Assert.AreEqual(video.Duration.Days, outputVideo.Duration.Days);
Assert.AreEqual(video.Duration.Hours, outputVideo.Duration.Hours); Assert.AreEqual(video.Duration.Hours, outputVideo.Duration.Hours);
Assert.AreEqual(video.Duration.Minutes, outputVideo.Duration.Minutes); Assert.AreEqual(video.Duration.Minutes, outputVideo.Duration.Minutes);
Assert.AreEqual(video.Duration.Seconds - 2, outputVideo.Duration.Seconds); Assert.AreEqual(video.Duration.Seconds - 2, outputVideo.Duration.Seconds);
}
finally
{
if (File.Exists(output))
File.Delete(output);
}
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public void Video_UpdatesProgress() public void Video_UpdatesProgress()
{ {
var output = Input.OutputLocation(VideoType.Mp4); var outputFile = new TemporaryFile("out.mp4");
var percentageDone = 0.0; var percentageDone = 0.0;
var timeDone = TimeSpan.Zero; var timeDone = TimeSpan.Zero;
void OnPercentageProgess(double percentage) => percentageDone = percentage; void OnPercentageProgess(double percentage) => percentageDone = percentage;
void OnTimeProgess(TimeSpan time) => timeDone = time; 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();
Assert.IsTrue(success);
try Assert.IsTrue(File.Exists(outputFile));
{ Assert.AreNotEqual(0.0, percentageDone);
var success = FFMpegArguments Assert.AreNotEqual(TimeSpan.Zero, timeDone);
.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);
}
} }
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
@ -673,11 +573,11 @@ public void Video_TranscodeInMemory()
[TestMethod, Timeout(10000)] [TestMethod, Timeout(10000)]
public async Task Video_Cancel_Async() public async Task Video_Cancel_Async()
{ {
var output = Input.OutputLocation(VideoType.Mp4); var outputFile = new TemporaryFile("out.mp4");
var task = FFMpegArguments var task = FFMpegArguments
.FromFileInput(VideoLibrary.LocalVideo) .FromFileInput(TestResources.Mp4Video)
.OutputToFile(output, false, opt => opt .OutputToFile(outputFile, false, opt => opt
.Resize(new Size(1000, 1000)) .Resize(new Size(1000, 1000))
.WithAudioCodec(AudioCodec.Aac) .WithAudioCodec(AudioCodec.Aac)
.WithVideoCodec(VideoCodec.LibX264) .WithVideoCodec(VideoCodec.LibX264)
@ -687,19 +587,11 @@ public async Task Video_Cancel_Async()
.CancellableThrough(out var cancel) .CancellableThrough(out var cancel)
.ProcessAsynchronously(false); .ProcessAsynchronously(false);
try await Task.Delay(300);
{ cancel();
await Task.Delay(300);
cancel();
var result = await task; var result = await task;
Assert.IsFalse(result); Assert.IsFalse(result);
}
finally
{
if (File.Exists(output))
File.Delete(output);
}
} }
} }
} }

View file

@ -4,7 +4,7 @@ namespace FFMpegCore.Enums
{ {
public class ContainerFormat 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 string Name { get; private set; }
public bool DemuxingSupported { 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) internal static bool TryParse(string line, out ContainerFormat fmt)
{ {
var match = _formatRegex.Match(line); var match = FormatRegex.Match(line);
if (!match.Success) if (!match.Success)
{ {
fmt = null!; fmt = null!;
return false; return false;
} }
fmt = new ContainerFormat(match.Groups[3].Value); fmt = new ContainerFormat(match.Groups[3].Value)
fmt.DemuxingSupported = match.Groups[1].Value == " "; {
fmt.MuxingSupported = match.Groups[2].Value == " "; DemuxingSupported = match.Groups[1].Value == " ",
fmt.Description = match.Groups[4].Value; MuxingSupported = match.Groups[2].Value == " ",
Description = match.Groups[4].Value
};
return true; return true;
} }
} }

View file

@ -65,7 +65,7 @@ void OnCancelEvent(object sender, EventArgs args)
{ {
errorCode = t.Result; errorCode = t.Result;
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
// _ffMpegArguments.Post(); _ffMpegArguments.Post();
}), _ffMpegArguments.During(cancellationTokenSource.Token).ContinueWith(t => _ffMpegArguments.Post())); }), _ffMpegArguments.During(cancellationTokenSource.Token).ContinueWith(t => _ffMpegArguments.Post()));
} }
catch (Exception e) catch (Exception e)
@ -111,7 +111,7 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t =>
{ {
errorCode = t.Result; errorCode = t.Result;
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
// _ffMpegArguments.Post(); _ffMpegArguments.Post();
}), _ffMpegArguments.During(cancellationTokenSource.Token).ContinueWith(t => _ffMpegArguments.Post())).ConfigureAwait(false); }), _ffMpegArguments.During(cancellationTokenSource.Token).ContinueWith(t => _ffMpegArguments.Post())).ConfigureAwait(false);
} }
catch (Exception e) catch (Exception e)