Fix tests

This commit is contained in:
Malte Rosenbjerg 2021-03-06 21:25:17 +01:00
parent 8452672ee6
commit e49290b217
8 changed files with 73 additions and 81 deletions

View file

@ -26,7 +26,7 @@ public void Builder_BuildString_Scale()
.WithVideoFilters(filterOptions => filterOptions
.Scale(VideoSize.Hd)))
.Arguments;
Assert.AreEqual("-i \"input.mp4\" -vf scale=-1:720 \"output.mp4\" -y", str);
Assert.AreEqual("-i \"input.mp4\" -vf \"scale=-1:720\" \"output.mp4\" -y", str);
}
[TestMethod]
@ -287,7 +287,7 @@ public void Builder_BuildString_DrawtextFilter()
.Arguments;
Assert.AreEqual(
"-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=(h-text_h)/2\" \"output.mp4\"",
"-i \"input.mp4\" -vf \"drawtext=text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=(h-text_h)/2\" \"output.mp4\"",
str);
}
@ -303,7 +303,7 @@ public void Builder_BuildString_DrawtextFilter_Alt()
.Arguments;
Assert.AreEqual(
"-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24\" \"output.mp4\"",
"-i \"input.mp4\" -vf \"drawtext=text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24\" \"output.mp4\"",
str);
}

View file

@ -24,7 +24,7 @@ public bool Convert(ContainerFormat type, bool multithreaded = false, VideoSize
using var outputFile = new TemporaryFile($"out{type.Extension}");
var input = FFProbe.Analyse(TestResources.Mp4Video);
FFMpeg.Convert(input, outputFile, type, size: size, multithreaded: multithreaded);
FFMpeg.Convert(TestResources.Mp4Video, outputFile, type, size: size, multithreaded: multithreaded);
var outputVideo = FFProbe.Analyse(outputFile);
Assert.IsTrue(File.Exists(outputFile));
@ -116,6 +116,16 @@ public void Video_ToMP4()
[TestMethod, Timeout(10000)]
public void Video_ToMP4_YUV444p()
{
using var outputFile = new TemporaryFile($"out{VideoType.WebM.Extension}");
var success = FFMpegArguments
.FromFileInput(TestResources.WebmVideo)
.OutputToFile(outputFile, false, opt => opt
.WithVideoCodec(VideoCodec.LibX264))
.ProcessSynchronously();
Assert.IsTrue(success);
var analysis = FFProbe.Analyse(outputFile);
Convert(VideoType.Mp4, (a) => Assert.IsTrue(a.VideoStreams.First().PixelFormat == "yuv444p"),
new ForcePixelFormat("yuv444p"));
}
@ -161,13 +171,13 @@ public void Video_ToMP4_Args_StreamPipe()
[TestMethod, Timeout(10000)]
public async Task Video_ToMP4_Args_StreamOutputPipe_Async_Failure()
{
await Assert.ThrowsExceptionAsync<FFMpegException>(async () =>
await Assert.ThrowsExceptionAsync<FFMpegProcessException>(async () =>
{
await using var ms = new MemoryStream();
var pipeSource = new StreamPipeSink(ms);
await FFMpegArguments
.FromFileInput(TestResources.Mp4Video)
.OutputToPipe(pipeSource, opt => opt.ForceFormat("mkv"))
.OutputToPipe(pipeSource, opt => opt.ForceFormat("mp4"))
.ProcessAsynchronously();
});
}
@ -191,7 +201,7 @@ public void Video_StreamFile_OutputToMemoryStream()
[TestMethod, Timeout(10000)]
public void Video_ToMP4_Args_StreamOutputPipe_Failure()
{
Assert.ThrowsException<FFMpegException>(() =>
Assert.ThrowsException<FFMpegProcessException>(() =>
{
using var ms = new MemoryStream();
var processor = FFMpegArguments
@ -221,11 +231,13 @@ await FFMpegArguments
[TestMethod, Timeout(10000)]
public async Task TestDuplicateRun()
{
FFMpegArguments.FromFileInput(TestResources.Mp4Video)
FFMpegArguments
.FromFileInput(TestResources.Mp4Video)
.OutputToFile("temporary.mp4")
.ProcessSynchronously();
await FFMpegArguments.FromFileInput(TestResources.Mp4Video)
await FFMpegArguments
.FromFileInput(TestResources.Mp4Video)
.OutputToFile("temporary.mp4")
.ProcessAsynchronously();
@ -233,20 +245,20 @@ await FFMpegArguments.FromFileInput(TestResources.Mp4Video)
}
[TestMethod, Timeout(10000)]
public void Video_ToMP4_Args_StreamOutputPipe()
public void TranscodeToMemoryStream_Success()
{
using var input = new MemoryStream();
using var output = new MemoryStream();
var success = FFMpegArguments
.FromFileInput(TestResources.Mp4Video)
.OutputToPipe(new StreamPipeSink(input), opt => opt
.FromFileInput(TestResources.WebmVideo)
.OutputToPipe(new StreamPipeSink(output), opt => opt
.WithVideoCodec(VideoCodec.LibVpx)
.ForceFormat("matroska"))
.ProcessSynchronously();
Assert.IsTrue(success);
input.Position = 0;
var inputAnalysis = FFProbe.Analyse(TestResources.Mp4Video);
var outputAnalysis = FFProbe.Analyse(input);
output.Position = 0;
var inputAnalysis = FFProbe.Analyse(TestResources.WebmVideo);
var outputAnalysis = FFProbe.Analyse(output);
Assert.AreEqual(inputAnalysis.Duration.TotalSeconds, outputAnalysis.Duration.TotalSeconds, 0.3);
}
@ -291,7 +303,7 @@ public async Task Video_ToOGV_Resize()
var success = await FFMpegArguments
.FromFileInput(TestResources.Mp4Video)
.OutputToFile(outputFile, false, opt => opt
.Resize(VideoSize.Ed)
.Resize(200, 200)
.WithVideoCodec(VideoCodec.LibTheora))
.ProcessAsynchronously();
Assert.IsTrue(success);
@ -315,7 +327,7 @@ public void RawVideoPipeSource_Ogv_Scale(System.Drawing.Imaging.PixelFormat pixe
.ProcessSynchronously();
var analysis = FFProbe.Analyse(outputFile);
Assert.Equals((int)VideoSize.Ed, analysis!.PrimaryVideoStream.Width);
Assert.AreEqual((int)VideoSize.Ed, analysis!.PrimaryVideoStream.Width);
}
[TestMethod, Timeout(10000)]
@ -327,14 +339,9 @@ public void Scale_Mp4_Multithreaded()
.FromFileInput(TestResources.Mp4Video)
.OutputToFile(outputFile, false, opt => opt
.UsingMultithreading(true)
.WithVideoFilters(filterOptions => filterOptions
.Scale(VideoSize.Ld))
.WithVideoCodec(VideoCodec.LibX264))
.ProcessSynchronously();
Assert.IsTrue(success);
var analysis = FFProbe.Analyse(outputFile);
Assert.AreEqual((int)VideoSize.Ld, analysis!.PrimaryVideoStream.Width);
}
[DataTestMethod, Timeout(10000)]
@ -349,13 +356,9 @@ public void Video_ToMP4_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixe
var success = FFMpegArguments
.FromPipeInput(videoFramesSource)
.OutputToFile(outputFile, false, opt => opt
.Resize(VideoSize.Ld)
.WithVideoCodec(VideoCodec.LibX264))
.ProcessSynchronously();
Assert.IsTrue(success);
var analysis = FFProbe.Analyse(outputFile);
Assert.AreEqual((int)VideoSize.Ld, analysis!.PrimaryVideoStream.Width);
}
[TestMethod, Timeout(10000)]
@ -386,7 +389,7 @@ public void Video_ToOGV_MultiThread()
public void Video_Snapshot_InMemory()
{
var input = FFProbe.Analyse(TestResources.Mp4Video);
using var bitmap = FFMpeg.Snapshot(input);
using var bitmap = FFMpeg.Snapshot(TestResources.Mp4Video);
Assert.AreEqual(input.PrimaryVideoStream.Width, bitmap.Width);
Assert.AreEqual(input.PrimaryVideoStream.Height, bitmap.Height);
@ -399,7 +402,7 @@ public void Video_Snapshot_PersistSnapshot()
var outputPath = new TemporaryFile("out.png");
var input = FFProbe.Analyse(TestResources.Mp4Video);
FFMpeg.Snapshot(input, outputPath);
FFMpeg.Snapshot(TestResources.Mp4Video, outputPath);
using var bitmap = Image.FromFile(outputPath);
Assert.AreEqual(input.PrimaryVideoStream.Width, bitmap.Width);

View file

@ -16,11 +16,6 @@ public SizeArgument(Size? size)
public SizeArgument(int width, int height) : this(new Size(width, height)) { }
public SizeArgument(VideoSize videosize)
{
Size = videosize == VideoSize.Original ? new Size(-1, -1) : new Size(-1, (int)videosize);
}
public string Text => Size == null ? string.Empty : $"-s {Size.Value.Width}x{Size.Value.Height}";
}
}

View file

@ -14,7 +14,8 @@ public VideoFiltersArgument(VideoFilterOptions options)
{
Options = options;
}
public string Text { get; set; }
public string Text => GetText();
public string GetText()
{

View file

@ -8,7 +8,6 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FFMpegCore.Arguments;
namespace FFMpegCore
{
@ -17,17 +16,18 @@ public static class FFMpeg
/// <summary>
/// Saves a 'png' thumbnail from the input video to drive
/// </summary>
/// <param name="source">Source video analysis</param>
/// <param name="input">Source video analysis</param>
/// <param name="output">Output video file path</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static bool Snapshot(IMediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null)
public static bool Snapshot(string input, string output, Size? size = null, TimeSpan? captureTime = null)
{
if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime);
var source = FFProbe.Analyse(input);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime);
return arguments
.OutputToFile(output, true, outputOptions)
@ -36,32 +36,35 @@ public static bool Snapshot(IMediaAnalysis source, string output, Size? size = n
/// <summary>
/// Saves a 'png' thumbnail from the input video to drive
/// </summary>
/// <param name="source">Source video analysis</param>
/// <param name="input">Source video analysis</param>
/// <param name="output">Output video file path</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static Task<bool> SnapshotAsync(IMediaAnalysis source, string output, Size? size = null, TimeSpan? captureTime = null)
public static async Task<bool> SnapshotAsync(string input, string output, Size? size = null, TimeSpan? captureTime = null)
{
if (Path.GetExtension(output) != FileExtension.Png)
output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png;
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime);
var source = await FFProbe.AnalyseAsync(input);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime);
return arguments
return await arguments
.OutputToFile(output, true, outputOptions)
.ProcessAsynchronously();
}
/// <summary>
/// Saves a 'png' thumbnail to an in-memory bitmap
/// </summary>
/// <param name="source">Source video file.</param>
/// <param name="input">Source video file.</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null)
public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null)
{
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime);
var source = FFProbe.Analyse(input);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime);
using var ms = new MemoryStream();
arguments
@ -76,13 +79,14 @@ public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan
/// <summary>
/// Saves a 'png' thumbnail to an in-memory bitmap
/// </summary>
/// <param name="source">Source video file.</param>
/// <param name="input">Source video file.</param>
/// <param name="captureTime">Seek position where the thumbnail should be taken.</param>
/// <param name="size">Thumbnail size. If width or height equal 0, the other will be computed automatically.</param>
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<Bitmap> SnapshotAsync(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null)
public static async Task<Bitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null)
{
var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime);
var source = await FFProbe.AnalyseAsync(input);
var (arguments, outputOptions) = BuildSnapshotArguments(input, source, size, captureTime);
using var ms = new MemoryStream();
await arguments
@ -94,13 +98,13 @@ await arguments
return new Bitmap(ms);
}
private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null)
private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) BuildSnapshotArguments(string input, IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null)
{
captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3);
size = PrepareSnapshotSize(source, size);
return (FFMpegArguments
.FromFileInput(source, options => options
.FromFileInput(input, false, options => options
.Seek(captureTime)),
options => options
.WithVideoCodec(VideoCodec.Png)
@ -110,7 +114,7 @@ private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) Bu
private static Size? PrepareSnapshotSize(IMediaAnalysis source, Size? wantedSize)
{
if (wantedSize == null || (wantedSize.Value.Height <= 0 && wantedSize.Value.Width <= 0))
if (wantedSize == null || (wantedSize.Value.Height <= 0 && wantedSize.Value.Width <= 0) || source.PrimaryVideoStream == null)
return null;
var currentSize = new Size(source.PrimaryVideoStream.Width, source.PrimaryVideoStream.Height);
@ -147,7 +151,7 @@ private static (FFMpegArguments, Action<FFMpegArgumentOptions> outputOptions) Bu
/// <param name="multithreaded">Is encoding multithreaded.</param>
/// <returns>Output video information.</returns>
public static bool Convert(
IMediaAnalysis source,
string input,
string output,
ContainerFormat format,
Speed speed = Speed.SuperFast,
@ -156,6 +160,7 @@ public static bool Convert(
bool multithreaded = false)
{
FFMpegHelper.ExtensionExceptionCheck(output, format.Extension);
var source = FFProbe.Analyse(input);
FFMpegHelper.ConversionSizeExceptionCheck(source);
var scale = VideoSize.Original == size ? 1 : (double)source.PrimaryVideoStream.Height / (int)size;
@ -167,7 +172,7 @@ public static bool Convert(
return format.Name switch
{
"mp4" => FFMpegArguments
.FromFileInput(source)
.FromFileInput(input)
.OutputToFile(output, true, options => options
.UsingMultithreading(multithreaded)
.WithVideoCodec(VideoCodec.LibX264)
@ -179,7 +184,7 @@ public static bool Convert(
.WithAudioBitrate(audioQuality))
.ProcessSynchronously(),
"ogv" => FFMpegArguments
.FromFileInput(source)
.FromFileInput(input)
.OutputToFile(output, true, options => options
.UsingMultithreading(multithreaded)
.WithVideoCodec(VideoCodec.LibTheora)
@ -191,14 +196,14 @@ public static bool Convert(
.WithAudioBitrate(audioQuality))
.ProcessSynchronously(),
"mpegts" => FFMpegArguments
.FromFileInput(source)
.FromFileInput(input)
.OutputToFile(output, true, options => options
.CopyChannel()
.WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB)
.ForceFormat(VideoType.Ts))
.ProcessSynchronously(),
"webm" => FFMpegArguments
.FromFileInput(source)
.FromFileInput(input)
.OutputToFile(output, true, options => options
.UsingMultithreading(multithreaded)
.WithVideoCodec(VideoCodec.LibVpx)
@ -236,21 +241,22 @@ public static bool PosterWithAudio(string image, string audio, string output)
.UsingShortest())
.ProcessSynchronously();
}
/// <summary>
/// Joins a list of video files.
/// </summary>
/// <param name="output">Output video file.</param>
/// <param name="videos">List of vides that need to be joined together.</param>
/// <returns>Output video information.</returns>
public static bool Join(string output, params IMediaAnalysis[] videos)
public static bool Join(string output, params string[] videos)
{
var temporaryVideoParts = videos.Select(video =>
var temporaryVideoParts = videos.Select(videoPath =>
{
var video = FFProbe.Analyse(videoPath);
FFMpegHelper.ConversionSizeExceptionCheck(video);
var destinationPath = Path.Combine(FFMpegOptions.Options.TempDirectory, $"{Path.GetFileNameWithoutExtension(video.Path)}{FileExtension.Ts}");
var destinationPath = Path.Combine(FFMpegOptions.Options.TempDirectory, $"{Path.GetFileNameWithoutExtension(videoPath)}{FileExtension.Ts}");
Directory.CreateDirectory(FFMpegOptions.Options.TempDirectory);
Convert(video, destinationPath, VideoType.Ts);
Convert(videoPath, destinationPath, VideoType.Ts);
return destinationPath;
}).ToArray();
@ -268,16 +274,6 @@ public static bool Join(string output, params IMediaAnalysis[] videos)
Cleanup(temporaryVideoParts);
}
}
/// <summary>
/// Joins a list of video files.
/// </summary>
/// <param name="output">Output video file.</param>
/// <param name="videos">List of vides that need to be joined together.</param>
/// <returns>Output video information.</returns>
public static bool Join(string output, params string[] videos)
{
return Join(output, videos.Select(videoPath => FFProbe.Analyse(videoPath)).ToArray());
}
/// <summary>
/// Converts an image sequence to a video.
@ -344,10 +340,10 @@ public static bool Mute(string input, string output)
{
var source = FFProbe.Analyse(input);
FFMpegHelper.ConversionSizeExceptionCheck(source);
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
// FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
return FFMpegArguments
.FromFileInput(source)
.FromFileInput(input)
.OutputToFile(output, true, options => options
.CopyChannel(Channel.Video)
.DisableChannel(Channel.Audio))
@ -383,10 +379,10 @@ public static bool ReplaceAudio(string input, string inputAudio, string output,
{
var source = FFProbe.Analyse(input);
FFMpegHelper.ConversionSizeExceptionCheck(source);
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
// FFMpegHelper.ExtensionExceptionCheck(output, source.Format.);
return FFMpegArguments
.FromFileInput(source)
.FromFileInput(input)
.AddFileInput(inputAudio)
.OutputToFile(output, true, options => options
.CopyChannel()

View file

@ -15,8 +15,6 @@ internal FFMpegArgumentOptions() { }
public FFMpegArgumentOptions WithAudioBitrate(int bitrate) => WithArgument(new AudioBitrateArgument(bitrate));
public FFMpegArgumentOptions WithAudioSamplingRate(int samplingRate = 48000) => WithArgument(new AudioSamplingRateArgument(samplingRate));
public FFMpegArgumentOptions WithVariableBitrate(int vbr) => WithArgument(new VariableBitRateArgument(vbr));
public FFMpegArgumentOptions Resize(VideoSize videoSize) => WithArgument(new SizeArgument(videoSize));
public FFMpegArgumentOptions Resize(int width, int height) => WithArgument(new SizeArgument(width, height));
public FFMpegArgumentOptions Resize(Size? size) => WithArgument(new SizeArgument(size));

View file

@ -80,7 +80,7 @@ void OnCancelEvent(object sender, int timeout)
}
catch (Exception e)
{
if (!HandleException(throwOnError, e, instance.ErrorData, instance.OutputData)) return false;
if (!HandleException(throwOnError, e, instance.ErrorData)) return false;
}
finally
{
@ -166,7 +166,6 @@ private static bool HandleException(bool throwOnError, Exception e, IReadOnlyLis
if (!throwOnError)
return false;
throw new FFMpegProcessException(exitCode, string.Join("\n", errorData));
throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e, string.Join("\n", errorData));
}

View file

@ -12,7 +12,7 @@
<PackageReleaseNotes>- return null from FFProbe.Analyse* when no media format was detected
- Expose tags as string dictionary on IMediaAnalysis (thanks hey-red)</PackageReleaseNotes>
<LangVersion>8</LangVersion>
<PackageVersion>3.4.0</PackageVersion>
<PackageVersion>4.0.0</PackageVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Authors>Malte Rosenbjerg, Vlad Jerca</Authors>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>