From 3c3b11cec6b9032c905fc99d2009549c6306fd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=91=D0=B0=D0=B3?= =?UTF-8?q?=D1=80=D1=8F=D0=BD=D1=86=D0=B5=D0=B2?= Date: Mon, 27 Apr 2020 19:24:26 +0300 Subject: [PATCH] Added input piping tests --- FFMpegCore.Test/BitmapSources.cs | 219 +++++++++++++++++++++++++ FFMpegCore.Test/FFMpegCore.Test.csproj | 2 +- FFMpegCore.Test/VideoTest.cs | 133 +++++++++++++-- 3 files changed, 340 insertions(+), 14 deletions(-) create mode 100644 FFMpegCore.Test/BitmapSources.cs diff --git a/FFMpegCore.Test/BitmapSources.cs b/FFMpegCore.Test/BitmapSources.cs new file mode 100644 index 0000000..33c8035 --- /dev/null +++ b/FFMpegCore.Test/BitmapSources.cs @@ -0,0 +1,219 @@ +using FFMpegCore.Extend; +using FFMpegCore.FFMPEG.Pipes; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.Numerics; +using System.Text; + +namespace FFMpegCore.Test +{ + static class BitmapSource + { + public static IEnumerable CreateBitmaps(int count, PixelFormat fmt, int w, int h) + { + for (int i = 0; i < count; i++) + { + using (var frame = CreateVideoFrame(i, fmt, w, h, 0.025f, 0.025f * w * 0.03f)) + { + yield return frame; + } + } + } + + private static BitmapVideoFrameWrapper CreateVideoFrame(int index, PixelFormat fmt, int w, int h, float scaleNoise, float offset) + { + var bitmap = new Bitmap(w, h, fmt); + + offset = offset * index; + + for (int y = 0; y < h; y++) + for (int x = 0; x < w; x++) + { + var nx = x * scaleNoise + offset; + var ny = y * scaleNoise + offset; + + var value = (int)((Perlin.Noise(nx, ny) + 1.0f) / 2.0f * 255); + + var color = Color.FromArgb(value, value, value); + + bitmap.SetPixel(x, y, color); + } + + return new BitmapVideoFrameWrapper(bitmap); + } + + // + // Perlin noise generator for Unity + // Keijiro Takahashi, 2013, 2015 + // https://github.com/keijiro/PerlinNoise + // + // Based on the original implementation by Ken Perlin + // http://mrl.nyu.edu/~perlin/noise/ + // + static class Perlin + { + #region Noise functions + + public static float Noise(float x) + { + var X = (int)MathF.Floor(x) & 0xff; + x -= MathF.Floor(x); + var u = Fade(x); + return Lerp(u, Grad(perm[X], x), Grad(perm[X + 1], x - 1)) * 2; + } + + public static float Noise(float x, float y) + { + var X = (int)MathF.Floor(x) & 0xff; + var Y = (int)MathF.Floor(y) & 0xff; + x -= MathF.Floor(x); + y -= MathF.Floor(y); + var u = Fade(x); + var v = Fade(y); + var A = (perm[X] + Y) & 0xff; + var B = (perm[X + 1] + Y) & 0xff; + return Lerp(v, Lerp(u, Grad(perm[A], x, y), Grad(perm[B], x - 1, y)), + Lerp(u, Grad(perm[A + 1], x, y - 1), Grad(perm[B + 1], x - 1, y - 1))); + } + + public static float Noise(Vector2 coord) + { + return Noise(coord.X, coord.Y); + } + + public static float Noise(float x, float y, float z) + { + var X = (int)MathF.Floor(x) & 0xff; + var Y = (int)MathF.Floor(y) & 0xff; + var Z = (int)MathF.Floor(z) & 0xff; + x -= MathF.Floor(x); + y -= MathF.Floor(y); + z -= MathF.Floor(z); + var u = Fade(x); + var v = Fade(y); + var w = Fade(z); + var A = (perm[X] + Y) & 0xff; + var B = (perm[X + 1] + Y) & 0xff; + var AA = (perm[A] + Z) & 0xff; + var BA = (perm[B] + Z) & 0xff; + var AB = (perm[A + 1] + Z) & 0xff; + var BB = (perm[B + 1] + Z) & 0xff; + return Lerp(w, Lerp(v, Lerp(u, Grad(perm[AA], x, y, z), Grad(perm[BA], x - 1, y, z)), + Lerp(u, Grad(perm[AB], x, y - 1, z), Grad(perm[BB], x - 1, y - 1, z))), + Lerp(v, Lerp(u, Grad(perm[AA + 1], x, y, z - 1), Grad(perm[BA + 1], x - 1, y, z - 1)), + Lerp(u, Grad(perm[AB + 1], x, y - 1, z - 1), Grad(perm[BB + 1], x - 1, y - 1, z - 1)))); + } + + public static float Noise(Vector3 coord) + { + return Noise(coord.X, coord.Y, coord.Z); + } + + #endregion + + #region fBm functions + + public static float Fbm(float x, int octave) + { + var f = 0.0f; + var w = 0.5f; + for (var i = 0; i < octave; i++) + { + f += w * Noise(x); + x *= 2.0f; + w *= 0.5f; + } + return f; + } + + public static float Fbm(Vector2 coord, int octave) + { + var f = 0.0f; + var w = 0.5f; + for (var i = 0; i < octave; i++) + { + f += w * Noise(coord); + coord *= 2.0f; + w *= 0.5f; + } + return f; + } + + public static float Fbm(float x, float y, int octave) + { + return Fbm(new Vector2(x, y), octave); + } + + public static float Fbm(Vector3 coord, int octave) + { + var f = 0.0f; + var w = 0.5f; + for (var i = 0; i < octave; i++) + { + f += w * Noise(coord); + coord *= 2.0f; + w *= 0.5f; + } + return f; + } + + public static float Fbm(float x, float y, float z, int octave) + { + return Fbm(new Vector3(x, y, z), octave); + } + + #endregion + + #region Private functions + + static float Fade(float t) + { + return t * t * t * (t * (t * 6 - 15) + 10); + } + + static float Lerp(float t, float a, float b) + { + return a + t * (b - a); + } + + static float Grad(int hash, float x) + { + return (hash & 1) == 0 ? x : -x; + } + + static float Grad(int hash, float x, float y) + { + return ((hash & 1) == 0 ? x : -x) + ((hash & 2) == 0 ? y : -y); + } + + static float Grad(int hash, float x, float y, float z) + { + var h = hash & 15; + var u = h < 8 ? x : y; + var v = h < 4 ? y : (h == 12 || h == 14 ? x : z); + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + } + + static int[] perm = { + 151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, + 151 + }; + + #endregion + } + } +} diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index 8e749dd..1c729a9 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -12,7 +12,7 @@ - Always + PreserveNewest diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index eff0d44..5de10b1 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -1,6 +1,7 @@ using FFMpegCore.Enums; using FFMpegCore.FFMPEG.Argument; using FFMpegCore.FFMPEG.Enums; +using FFMpegCore.FFMPEG.Pipes; using FFMpegCore.Test.Resources; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; @@ -70,7 +71,7 @@ public void Convert(VideoType type, ArgumentContainer container) { var input = VideoInfo.FromFileInfo(Input); - var arguments = new ArgumentContainer {new InputArgument(input)}; + var arguments = new ArgumentContainer { new InputArgument(input) }; foreach (var arg in container) { arguments.Add(arg.Value); @@ -114,6 +115,64 @@ public void Convert(VideoType type, ArgumentContainer container) } } + public void ConvertFromPipe(VideoType type, ArgumentContainer container) + { + ConvertFromPipe(type, container, PixelFormat.Format24bppRgb); + ConvertFromPipe(type, container, PixelFormat.Format32bppArgb); + ConvertFromPipe(type, container, PixelFormat.Format48bppRgb); + } + + public void ConvertFromPipe(VideoType type, ArgumentContainer container, PixelFormat fmt) + { + var output = Input.OutputLocation(type); + + try + { + var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256)); + var arguments = new ArgumentContainer { new InputPipeArgument(videoFramesSource) }; + foreach (var arg in container) + { + arguments.Add(arg.Value); + } + arguments.Add(new OutputArgument(output)); + + var scaling = container.Find(); + + Encoder.Convert(arguments); + + var outputVideo = new VideoInfo(output.FullName); + + Assert.IsTrue(File.Exists(output.FullName)); + + if (scaling == null) + { + Assert.AreEqual(outputVideo.Width, videoFramesSource.Width); + Assert.AreEqual(outputVideo.Height, videoFramesSource.Height); + } + else + { + if (scaling.Value.Width != -1) + { + Assert.AreEqual(outputVideo.Width, scaling.Value.Width); + } + + if (scaling.Value.Height != -1) + { + Assert.AreEqual(outputVideo.Height, scaling.Value.Height); + } + + Assert.AreNotEqual(outputVideo.Width, videoFramesSource.Width); + Assert.AreNotEqual(outputVideo.Height, videoFramesSource.Height); + } + } + finally + { + if (File.Exists(output.FullName)) + File.Delete(output.FullName); + } + + } + [TestMethod] public void Video_ToMP4() { @@ -123,10 +182,17 @@ public void Video_ToMP4() [TestMethod] public void Video_ToMP4_Args() { - var container = new ArgumentContainer {new VideoCodecArgument(VideoCodec.LibX264)}; + var container = new ArgumentContainer { new VideoCodecArgument(VideoCodec.LibX264) }; Convert(VideoType.Mp4, container); } + [TestMethod] + public void Video_ToMP4_Args_Pipe() + { + var container = new ArgumentContainer { new VideoCodecArgument(VideoCodec.LibX264) }; + ConvertFromPipe(VideoType.Mp4, container); + } + [TestMethod] public void Video_ToTS() { @@ -145,6 +211,17 @@ public void Video_ToTS_Args() Convert(VideoType.Ts, container); } + [TestMethod] + public void Video_ToTS_Args_Pipe() + { + var container = new ArgumentContainer + { + new CopyArgument(), + new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB), + new ForceFormatArgument(VideoCodec.MpegTs) + }; + ConvertFromPipe(VideoType.Ts, container); + } [TestMethod] public void Video_ToOGV_Resize() @@ -157,12 +234,23 @@ public void Video_ToOGV_Resize_Args() { var container = new ArgumentContainer { - new ScaleArgument(VideoSize.Ed), + new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora) }; Convert(VideoType.Ogv, container); } + [TestMethod] + public void Video_ToOGV_Resize_Args_Pipe() + { + var container = new ArgumentContainer + { + new ScaleArgument(VideoSize.Ed), + new VideoCodecArgument(VideoCodec.LibTheora) + }; + ConvertFromPipe(VideoType.Ogv, container); + } + [TestMethod] public void Video_ToMP4_Resize() { @@ -174,12 +262,23 @@ public void Video_ToMP4_Resize_Args() { var container = new ArgumentContainer { - new ScaleArgument(VideoSize.Ld), + new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264) }; Convert(VideoType.Mp4, container); } + [TestMethod] + public void Video_ToMP4_Resize_Args_Pipe() + { + var container = new ArgumentContainer + { + new ScaleArgument(VideoSize.Ld), + new VideoCodecArgument(VideoCodec.LibX264) + }; + ConvertFromPipe(VideoType.Mp4, container); + } + [TestMethod] public void Video_ToOGV() { @@ -323,9 +422,10 @@ public void Video_With_Only_Audio_Should_Extract_Metadata() Assert.AreEqual(79.5, video.Duration.TotalSeconds, 0.5); Assert.AreEqual(1.25, video.Size); } - + [TestMethod] - public void Video_Duration() { + public void Video_Duration() + { var video = VideoInfo.FromFileInfo(VideoLibrary.LocalVideo); var output = Input.OutputLocation(VideoType.Mp4); @@ -336,7 +436,8 @@ public void Video_Duration() { new OutputArgument(output) }; - try { + try + { Encoder.Convert(arguments); Assert.IsTrue(File.Exists(output.FullName)); @@ -346,14 +447,17 @@ public void Video_Duration() { Assert.AreEqual(video.Duration.Hours, outputVideo.Duration.Hours); Assert.AreEqual(video.Duration.Minutes, outputVideo.Duration.Minutes); Assert.AreEqual(video.Duration.Seconds - 5, outputVideo.Duration.Seconds); - } finally { + } + finally + { if (File.Exists(output.FullName)) output.Delete(); } } - + [TestMethod] - public void Video_UpdatesProgress() { + public void Video_UpdatesProgress() + { var output = Input.OutputLocation(VideoType.Mp4); var percentageDone = 0.0; @@ -367,13 +471,16 @@ public void Video_UpdatesProgress() { new OutputArgument(output) }; - try { + try + { Encoder.Convert(arguments); Encoder.OnProgress -= OnProgess; - + Assert.IsTrue(File.Exists(output.FullName)); Assert.AreNotEqual(0.0, percentageDone); - } finally { + } + finally + { if (File.Exists(output.FullName)) output.Delete(); }