From 6416cea390835fa613089c4c4f2c9094dfeb724b 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: Tue, 12 May 2020 17:44:14 +0300 Subject: [PATCH 1/5] Added Codec, PixelFormat and ContainerFormat classes Former-commit-id: bbc9305e2b177c6f0aa151ea71a935c525411d9c --- FFMpegCore.Test/ArgumentBuilderTest.cs | 16 +- FFMpegCore.Test/PixelFormatTests.cs | 44 ++++ FFMpegCore.Test/Resources/VideoLibrary.cs | 12 +- FFMpegCore.Test/VideoTest.cs | 40 ++-- FFMpegCore/Enums/FileExtension.cs | 37 ---- FFMpegCore/Enums/VideoType.cs | 10 - .../FFMpeg/Arguments/AudioCodecArgument.cs | 14 +- .../FFMpeg/Arguments/ForceFormatArgument.cs | 5 +- .../FFMpeg/Arguments/ForcePixelFormat.cs | 20 ++ .../FFMpeg/Arguments/VideoCodecArgument.cs | 11 +- FFMpegCore/FFMpeg/Enums/Codec.cs | 46 ++-- FFMpegCore/FFMpeg/Enums/ContainerFormat.cs | 47 +++++ FFMpegCore/FFMpeg/Enums/FileExtension.cs | 26 +++ FFMpegCore/FFMpeg/Enums/PixelFormat.cs | 56 +++++ FFMpegCore/FFMpeg/Enums/VideoCodec.cs | 154 ++++++++++++++ FFMpegCore/FFMpeg/FFMpeg.cs | 199 ++++++++++++++++-- FFMpegCore/FFMpeg/FFMpegArguments.cs | 13 +- FFMpegCore/FFMpeg/FFMpegCache.cs | 57 +++++ FFMpegCore/FFMpeg/FFMpegOptions.cs | 13 ++ FFMpegCore/FFMpegCore.csproj | 5 + FFMpegCore/FFProbe/MediaStream.cs | 5 +- FFMpegCore/FFProbe/VideoStream.cs | 6 +- 22 files changed, 729 insertions(+), 107 deletions(-) create mode 100644 FFMpegCore.Test/PixelFormatTests.cs delete mode 100644 FFMpegCore/Enums/FileExtension.cs delete mode 100644 FFMpegCore/Enums/VideoType.cs create mode 100644 FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs create mode 100644 FFMpegCore/FFMpeg/Enums/ContainerFormat.cs create mode 100644 FFMpegCore/FFMpeg/Enums/FileExtension.cs create mode 100644 FFMpegCore/FFMpeg/Enums/PixelFormat.cs create mode 100644 FFMpegCore/FFMpeg/Enums/VideoCodec.cs create mode 100644 FFMpegCore/FFMpeg/FFMpegCache.cs diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs index 0b5688b..6667624 100644 --- a/FFMpegCore.Test/ArgumentBuilderTest.cs +++ b/FFMpegCore.Test/ArgumentBuilderTest.cs @@ -169,8 +169,8 @@ public void Builder_BuildString_CpuSpeed() [TestMethod] public void Builder_BuildString_ForceFormat() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").ForceFormat(VideoCodec.LibX264).OutputToFile("output.mp4").Arguments; - Assert.AreEqual("-i \"input.mp4\" -f libx264 \"output.mp4\"", str); + var str = FFMpegArguments.FromInputFiles(true, "input.mp4").ForceFormat(VideoType.Mp4).OutputToFile("output.mp4").Arguments; + Assert.AreEqual("-i \"input.mp4\" -f mp4 \"output.mp4\"", str); } [TestMethod] @@ -278,13 +278,13 @@ public void Builder_BuildString_Threads_2() public void Builder_BuildString_Codec() { var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVideoCodec(VideoCodec.LibX264).OutputToFile("output.mp4").Arguments; - Assert.AreEqual("-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p \"output.mp4\"", str); + Assert.AreEqual("-i \"input.mp4\" -c:v libx264 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Codec_Override() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVideoCodec(VideoCodec.LibX264).OutputToFile("output.mp4", true).Arguments; + var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVideoCodec(VideoCodec.LibX264).ForcePixelFormat("yuv420p").OutputToFile("output.mp4", true).Arguments; Assert.AreEqual("-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p \"output.mp4\" -y", str); } @@ -305,5 +305,13 @@ public void Builder_BuildString_Raw() str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithCustomArgument("-acodec copy").OutputToFile("output.mp4").Arguments; Assert.AreEqual("-i \"input.mp4\" -acodec copy \"output.mp4\"", str); } + + + [TestMethod] + public void Builder_BuildString_ForcePixelFormat() + { + var str = FFMpegArguments.FromInputFiles(true, "input.mp4").ForcePixelFormat("yuv444p").OutputToFile("output.mp4").Arguments; + Assert.AreEqual("-i \"input.mp4\" -pix_fmt yuv444p \"output.mp4\"", str); + } } } \ No newline at end of file diff --git a/FFMpegCore.Test/PixelFormatTests.cs b/FFMpegCore.Test/PixelFormatTests.cs new file mode 100644 index 0000000..3e7e841 --- /dev/null +++ b/FFMpegCore.Test/PixelFormatTests.cs @@ -0,0 +1,44 @@ +using FFMpegCore.Exceptions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FFMpegCore.Test +{ + [TestClass] + public class PixelFormatTests + { + [TestMethod] + public void PixelFormats_Enumerate() + { + var formats = FFMpeg.GetPixelFormats(); + Assert.IsTrue(formats.Count > 0); + } + + [TestMethod] + public void PixelFormats_TryGetExisting() + { + Assert.IsTrue(FFMpeg.TryGetPixelFormat("yuv420p", out _)); + } + + [TestMethod] + public void PixelFormats_TryGetNotExisting() + { + Assert.IsFalse(FFMpeg.TryGetPixelFormat("yuv420pppUnknown", out _)); + } + + [TestMethod] + public void PixelFormats_GetExisting() + { + var fmt = FFMpeg.GetPixelFormat("yuv420p"); + Assert.IsTrue(fmt.Components == 3 && fmt.BitsPerPixel == 12); + } + + [TestMethod] + public void PixelFormats_GetNotExisting() + { + Assert.ThrowsException(() => FFMpeg.GetPixelFormat("yuv420pppUnknown")); + } + } +} diff --git a/FFMpegCore.Test/Resources/VideoLibrary.cs b/FFMpegCore.Test/Resources/VideoLibrary.cs index 90e809d..3b0c4e0 100644 --- a/FFMpegCore.Test/Resources/VideoLibrary.cs +++ b/FFMpegCore.Test/Resources/VideoLibrary.cs @@ -25,25 +25,25 @@ public static class VideoLibrary 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, VideoType type) + public static string OutputLocation(this FileInfo file, ContainerFormat type) { - return OutputLocation(file, type, "_converted"); + return OutputLocation(file, type.Extension, "_converted"); } public static string OutputLocation(this FileInfo file, AudioType type) { - return OutputLocation(file, type, "_audio"); + return OutputLocation(file, type.ToString(), "_audio"); } public static string OutputLocation(this FileInfo file, ImageType type) { - return OutputLocation(file, type, "_screenshot"); + return OutputLocation(file, type.ToString(), "_screenshot"); } - public static string OutputLocation(this FileInfo file, Enum type, string keyword) + public static string OutputLocation(this FileInfo file, string type, string keyword) { string originalLocation = file.Directory.FullName, - outputFile = file.Name.Replace(file.Extension, keyword + "." + type.ToString().ToLowerInvariant()); + outputFile = file.Name.Replace(file.Extension, keyword + "." + type.ToLowerInvariant()); return $"{originalLocation}{Path.DirectorySeparatorChar}{outputFile}"; } diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index 069db9c..358634d 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -15,7 +15,7 @@ namespace FFMpegCore.Test [TestClass] public class VideoTest : BaseTest { - public bool Convert(VideoType 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); @@ -61,7 +61,7 @@ public bool Convert(VideoType type, bool multithreaded = false, VideoSize size = } } - private void ConvertFromStreamPipe(VideoType type, params IArgument[] inputArguments) + private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] inputArguments) { var output = Input.OutputLocation(type); @@ -81,7 +81,7 @@ private void ConvertFromStreamPipe(VideoType type, params IArgument[] inputArgum 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); @@ -157,7 +157,7 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments) } } - public void Convert(VideoType type, params IArgument[] inputArguments) + public void Convert(ContainerFormat type, Action validationMethod, params IArgument[] inputArguments) { var output = Input.OutputLocation(type); @@ -178,7 +178,7 @@ public void Convert(VideoType type, params IArgument[] inputArguments) 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); @@ -207,7 +207,12 @@ public void Convert(VideoType type, params IArgument[] inputArguments) } } - public void ConvertFromPipe(VideoType type, PixelFormat fmt, params IArgument[] inputArguments) + public void Convert(ContainerFormat type, params IArgument[] inputArguments) + { + Convert(type, null, inputArguments); + } + + public void ConvertFromPipe(ContainerFormat type, PixelFormat fmt, params IArgument[] inputArguments) { var output = Input.OutputLocation(type); @@ -218,7 +223,7 @@ public void ConvertFromPipe(VideoType type, PixelFormat fmt, params IArgument[] foreach (var arg in inputArguments) arguments.WithArgument(arg); var processor = arguments.OutputToFile(output); - + var scaling = arguments.Find(); processor.ProcessSynchronously(); @@ -261,6 +266,13 @@ public void Video_ToMP4() Convert(VideoType.Mp4); } + [TestMethod] + public void Video_ToMP4_YUV444p() + { + Convert(VideoType.Mp4, (a) => Assert.IsTrue(a.VideoStreams.First().PixelFormat == "yuv444p"), + new ForcePixelFormat("yuv444p")); + } + [TestMethod] public void Video_ToMP4_Args() { @@ -339,10 +351,10 @@ public void Video_ToTS() [TestMethod] public void Video_ToTS_Args() { - Convert(VideoType.Ts, + Convert(VideoType.Ts, new CopyArgument(), new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB), - new ForceFormatArgument(VideoCodec.MpegTs)); + new ForceFormatArgument(VideoType.MpegTs)); } [DataTestMethod] @@ -351,7 +363,7 @@ public void Video_ToTS_Args() // [DataRow(PixelFormat.Format48bppRgb)] public void Video_ToTS_Args_Pipe(PixelFormat pixelFormat) { - ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoCodec.MpegTs)); + ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts)); } [TestMethod] @@ -466,7 +478,7 @@ public void Video_Snapshot_PersistSnapshot() public void Video_Join() { var output = Input.OutputLocation(VideoType.Mp4); - var newInput = Input.OutputLocation(VideoType.Mp4, "duplicate"); + var newInput = Input.OutputLocation(VideoType.Mp4.Name, "duplicate"); try { var input = FFProbe.Analyse(Input.FullName); @@ -475,7 +487,7 @@ public void Video_Join() var success = FFMpeg.Join(output, input, input2); Assert.IsTrue(success); - + Assert.IsTrue(File.Exists(output)); var expectedDuration = input.Duration * 2; var result = FFProbe.Analyse(output); @@ -549,7 +561,7 @@ public void Video_Duration() { var video = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName); var output = Input.OutputLocation(VideoType.Mp4); - + try { FFMpegArguments @@ -584,7 +596,7 @@ public void Video_UpdatesProgress() void OnTimeProgess(TimeSpan time) => timeDone = time; var analysis = FFProbe.Analyse(VideoLibrary.LocalVideo.FullName); - + try { diff --git a/FFMpegCore/Enums/FileExtension.cs b/FFMpegCore/Enums/FileExtension.cs deleted file mode 100644 index 8661c7d..0000000 --- a/FFMpegCore/Enums/FileExtension.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace FFMpegCore.Enums -{ - public static class FileExtension - { - public static string Extension(this VideoType type) - { - return type switch - { - VideoType.Mp4 => Mp4, - VideoType.Ogv => Ogv, - VideoType.Ts => Ts, - VideoType.WebM => WebM, - _ => throw new Exception("The extension for this video type is not defined.") - }; - } - public static string Extension(this VideoCodec type) - { - return type switch - { - VideoCodec.LibX264 => Mp4, - VideoCodec.LibVpx => WebM, - VideoCodec.LibTheora => Ogv, - VideoCodec.MpegTs => Ts, - VideoCodec.Png => Png, - _ => throw new Exception("The extension for this video type is not defined.") - }; - } - public static readonly string Mp4 = ".mp4"; - public static readonly string Mp3 = ".mp3"; - public static readonly string Ts = ".ts"; - public static readonly string Ogv = ".ogv"; - public static readonly string Png = ".png"; - public static readonly string WebM = ".webm"; - } -} diff --git a/FFMpegCore/Enums/VideoType.cs b/FFMpegCore/Enums/VideoType.cs deleted file mode 100644 index 582330d..0000000 --- a/FFMpegCore/Enums/VideoType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace FFMpegCore.Enums -{ - public enum VideoType - { - Mp4, - Ogv, - Ts, - WebM - } -} \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/Arguments/AudioCodecArgument.cs b/FFMpegCore/FFMpeg/Arguments/AudioCodecArgument.cs index 76fac85..273bb02 100644 --- a/FFMpegCore/FFMpeg/Arguments/AudioCodecArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/AudioCodecArgument.cs @@ -1,4 +1,5 @@ using FFMpegCore.Enums; +using FFMpegCore.Exceptions; namespace FFMpegCore.Arguments { @@ -7,8 +8,17 @@ namespace FFMpegCore.Arguments /// public class AudioCodecArgument : IArgument { - public readonly AudioCodec AudioCodec; - public AudioCodecArgument(AudioCodec audioCodec) + public readonly string AudioCodec; + + public AudioCodecArgument(Codec audioCodec) + { + if (audioCodec.Type != CodecType.Audio) + throw new FFMpegException(FFMpegExceptionType.Operation, $"Codec \"{audioCodec.Name}\" is not an audio codec"); + + AudioCodec = audioCodec.Name; + } + + public AudioCodecArgument(string audioCodec) { AudioCodec = audioCodec; } diff --git a/FFMpegCore/FFMpeg/Arguments/ForceFormatArgument.cs b/FFMpegCore/FFMpeg/Arguments/ForceFormatArgument.cs index 3367f99..9524698 100644 --- a/FFMpegCore/FFMpeg/Arguments/ForceFormatArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/ForceFormatArgument.cs @@ -13,7 +13,10 @@ public ForceFormatArgument(string format) _format = format; } - public ForceFormatArgument(VideoCodec value) : this(value.ToString().ToLowerInvariant()) { } + public ForceFormatArgument(ContainerFormat format) + { + _format = format.Name; + } public string Text => $"-f {_format}"; } diff --git a/FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs b/FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs new file mode 100644 index 0000000..04b84ad --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs @@ -0,0 +1,20 @@ +using FFMpegCore.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FFMpegCore.Arguments +{ + public class ForcePixelFormat : IArgument + { + public string PixelFormat { get; private set; } + public string Text => $"-pix_fmt {PixelFormat}"; + + public ForcePixelFormat(string format) + { + PixelFormat = format; + } + + public ForcePixelFormat(PixelFormat format) : this(format.Name) { } + } +} diff --git a/FFMpegCore/FFMpeg/Arguments/VideoCodecArgument.cs b/FFMpegCore/FFMpeg/Arguments/VideoCodecArgument.cs index 7c47592..9386822 100644 --- a/FFMpegCore/FFMpeg/Arguments/VideoCodecArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/VideoCodecArgument.cs @@ -1,4 +1,5 @@ using FFMpegCore.Enums; +using FFMpegCore.Exceptions; namespace FFMpegCore.Arguments { @@ -14,8 +15,14 @@ public VideoCodecArgument(string codec) Codec = codec; } - public VideoCodecArgument(VideoCodec value) : this(value.ToString().ToLowerInvariant()) { } + public VideoCodecArgument(Codec value) + { + if (value.Type != CodecType.Video) + throw new FFMpegException(FFMpegExceptionType.Operation, $"Codec \"{value.Name}\" is not a video codec"); - public string Text => $"-c:v {Codec} -pix_fmt yuv420p"; + Codec = value.Name; + } + + public string Text => $"-c:v {Codec}"; } } diff --git a/FFMpegCore/FFMpeg/Enums/Codec.cs b/FFMpegCore/FFMpeg/Enums/Codec.cs index 7576501..2bbab5f 100644 --- a/FFMpegCore/FFMpeg/Enums/Codec.cs +++ b/FFMpegCore/FFMpeg/Enums/Codec.cs @@ -1,22 +1,42 @@ namespace FFMpegCore.Enums { - public enum VideoCodec + public enum CodecType { - LibX264, - LibVpx, - LibTheora, - Png, - MpegTs + Unknown = 0, + Video = 1 << 1, + Audio = 1 << 2, + Subtitle = 1 << 3, + Data = 1 << 4, } - public enum AudioCodec + public static class VideoCodec { - Aac, - LibVorbis, - LibFdk_Aac, - Ac3, - Eac3, - LibMp3Lame + public static Codec LibX264 => FFMpeg.GetCodec("libx264"); + public static Codec LibVpx => FFMpeg.GetCodec("libvpx"); + public static Codec LibTheora => FFMpeg.GetCodec("libtheora"); + public static Codec Png => FFMpeg.GetCodec("png"); + public static Codec MpegTs => FFMpeg.GetCodec("mpegts"); + } + + public static class AudioCodec + { + public static Codec Aac => FFMpeg.GetCodec("aac"); + public static Codec LibVorbis => FFMpeg.GetCodec("libvorbis"); + public static Codec LibFdk_Aac => FFMpeg.GetCodec("libfdk_aac"); + public static Codec Ac3 => FFMpeg.GetCodec("ac3"); + public static Codec Eac3 => FFMpeg.GetCodec("eac3"); + public static Codec LibMp3Lame => FFMpeg.GetCodec("libmp3lame"); + } + + public static class VideoType + { + public static ContainerFormat MpegTs => FFMpeg.GetContinerFormat("mpegts"); + public static ContainerFormat Ts => FFMpeg.GetContinerFormat("mpegts"); + public static ContainerFormat Mp4 => FFMpeg.GetContinerFormat("mp4"); + public static ContainerFormat Mov => FFMpeg.GetContinerFormat("mov"); + public static ContainerFormat Avi => FFMpeg.GetContinerFormat("avi"); + public static ContainerFormat Ogv => FFMpeg.GetContinerFormat("ogv"); + public static ContainerFormat WebM => FFMpeg.GetContinerFormat("webm"); } public enum Filter diff --git a/FFMpegCore/FFMpeg/Enums/ContainerFormat.cs b/FFMpegCore/FFMpeg/Enums/ContainerFormat.cs new file mode 100644 index 0000000..f15e5a7 --- /dev/null +++ b/FFMpegCore/FFMpeg/Enums/ContainerFormat.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace FFMpegCore.Enums +{ + public class ContainerFormat + { + 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; } + public bool MuxingSupported { get; private set; } + public string Description { get; private set; } = null!; + public string Extension + { + get + { + if (FFMpegOptions.Options.ExtensionOverrides.ContainsKey(Name)) + return FFMpegOptions.Options.ExtensionOverrides[Name]; + return "." + Name; + } + } + + internal ContainerFormat(string name) + { + Name = name; + } + + internal static bool TryParse(string line, out ContainerFormat fmt) + { + 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; + return true; + } + } +} diff --git a/FFMpegCore/FFMpeg/Enums/FileExtension.cs b/FFMpegCore/FFMpeg/Enums/FileExtension.cs new file mode 100644 index 0000000..d2e4a63 --- /dev/null +++ b/FFMpegCore/FFMpeg/Enums/FileExtension.cs @@ -0,0 +1,26 @@ +using System; + +namespace FFMpegCore.Enums +{ + public static class FileExtension + { + public static string Extension(this Codec type) + { + return type.Name switch + { + "libx264" => Mp4, + "libxvpx" => WebM, + "libxtheora" => Ogv, + "mpegts" => Ts, + "png" => Png, + _ => throw new Exception("The extension for this video type is not defined.") + }; + } + public static readonly string Mp4 = ".mp4"; + public static readonly string Mp3 = ".mp3"; + public static readonly string Ts = ".ts"; + public static readonly string Ogv = ".ogv"; + public static readonly string Png = ".png"; + public static readonly string WebM = ".webm"; + } +} diff --git a/FFMpegCore/FFMpeg/Enums/PixelFormat.cs b/FFMpegCore/FFMpeg/Enums/PixelFormat.cs new file mode 100644 index 0000000..dad0a6e --- /dev/null +++ b/FFMpegCore/FFMpeg/Enums/PixelFormat.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace FFMpegCore.Models +{ + public class PixelFormat + { + private static readonly Regex _formatRegex = new Regex(@"([I\.])([O\.])([H\.])([P\.])([B\.])\s+(\S+)\s+([0-9]+)\s+([0-9]+)"); + + public bool InputConversionSupported { get; private set; } + public bool OutputConversionSupported { get; private set; } + public bool HardwareAccelerationSupported { get; private set; } + public bool IsPaletted { get; private set; } + public bool IsBitstream { get; private set; } + public string Name { get; private set; } + public int Components { get; private set; } + public int BitsPerPixel { get; private set; } + + public bool CanConvertTo(PixelFormat other) + { + return InputConversionSupported && other.OutputConversionSupported; + } + + internal PixelFormat(string name) + { + Name = name; + } + + internal static bool TryParse(string line, out PixelFormat fmt) + { + var match = _formatRegex.Match(line); + if (!match.Success) + { + fmt = null!; + return false; + } + + fmt = new PixelFormat(match.Groups[6].Value); + fmt.InputConversionSupported = match.Groups[1].Value != "."; + fmt.OutputConversionSupported = match.Groups[2].Value != "."; + fmt.HardwareAccelerationSupported = match.Groups[3].Value != "."; + fmt.IsPaletted = match.Groups[4].Value != "."; + fmt.IsBitstream = match.Groups[5].Value != "."; + if (!int.TryParse(match.Groups[7].Value, out var nbComponents)) + return false; + fmt.Components = nbComponents; + if (!int.TryParse(match.Groups[8].Value, out var bpp)) + return false; + fmt.BitsPerPixel = bpp; + + return true; + } + } +} diff --git a/FFMpegCore/FFMpeg/Enums/VideoCodec.cs b/FFMpegCore/FFMpeg/Enums/VideoCodec.cs new file mode 100644 index 0000000..1c4ce31 --- /dev/null +++ b/FFMpegCore/FFMpeg/Enums/VideoCodec.cs @@ -0,0 +1,154 @@ +using FFMpegCore.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace FFMpegCore.Enums +{ + public enum FeatureStatus + { + Unknown, + NotSupported, + Supported, + } + + public class Codec + { + private static readonly Regex _codecsFormatRegex = new Regex(@"([D\.])([E\.])([VASD\.])([I\.])([L\.])([S\.])\s+([a-z0-9_-]+)\s+(.+)"); + private static readonly Regex _decodersEncodersFormatRegex = new Regex(@"([VASD\.])([F\.])([S\.])([X\.])([B\.])([D\.])\s+([a-z0-9_-]+)\s+(.+)"); + + public class FeatureLevel + { + public bool IsExperimental { get; internal set; } + public FeatureStatus SupportsFrameLevelMultithreading { get; internal set; } + public FeatureStatus SupportsSliceLevelMultithreading { get; internal set; } + public FeatureStatus SupportsDrawHorizBand { get; internal set; } + public FeatureStatus SupportsDirectRendering { get; internal set; } + + internal void Merge(FeatureLevel other) + { + IsExperimental |= other.IsExperimental; + SupportsFrameLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsFrameLevelMultithreading, (int)other.SupportsFrameLevelMultithreading); + SupportsSliceLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsSliceLevelMultithreading, (int)other.SupportsSliceLevelMultithreading); + SupportsDrawHorizBand = (FeatureStatus)Math.Max((int)SupportsDrawHorizBand, (int)other.SupportsDrawHorizBand); + SupportsDirectRendering = (FeatureStatus)Math.Max((int)SupportsDirectRendering, (int)other.SupportsDirectRendering); + } + } + + public string Name { get; private set; } + public CodecType Type { get; private set; } + public bool DecodingSupported { get; private set; } + public bool EncodingSupported { get; private set; } + public bool IsIntraFrameOnly { get; private set; } + public bool IsLossy { get; private set; } + public bool IsLossless { get; private set; } + public string Description { get; private set; } = null!; + + public FeatureLevel EncoderFeatureLevel { get; private set; } + public FeatureLevel DecoderFeatureLevel { get; private set; } + + internal Codec(string name, CodecType type) + { + EncoderFeatureLevel = new FeatureLevel(); + DecoderFeatureLevel = new FeatureLevel(); + Name = name; + Type = type; + } + + internal static bool TryParseFromCodecs(string line, out Codec codec) + { + var match = _codecsFormatRegex.Match(line); + if (!match.Success) + { + codec = null!; + return false; + } + + var name = match.Groups[7].Value; + var type = match.Groups[3].Value switch + { + "V" => CodecType.Video, + "A" => CodecType.Audio, + "D" => CodecType.Data, + "S" => CodecType.Subtitle, + _ => CodecType.Unknown + }; + + if(type == CodecType.Unknown) + { + codec = null!; + return false; + } + + codec = new Codec(name, type); + + codec.DecodingSupported = match.Groups[1].Value != "."; + codec.EncodingSupported = match.Groups[2].Value != "."; + codec.IsIntraFrameOnly = match.Groups[4].Value != "."; + codec.IsLossy = match.Groups[5].Value != "."; + codec.IsLossless = match.Groups[6].Value != "."; + codec.Description = match.Groups[8].Value; + + return true; + } + internal static bool TryParseFromEncodersDecoders(string line, out Codec codec, bool isEncoder) + { + var match = _decodersEncodersFormatRegex.Match(line); + if (!match.Success) + { + codec = null!; + return false; + } + + var name = match.Groups[7].Value; + var type = match.Groups[1].Value switch + { + "V" => CodecType.Video, + "A" => CodecType.Audio, + "D" => CodecType.Data, + "S" => CodecType.Subtitle, + _ => CodecType.Unknown + }; + + if (type == CodecType.Unknown) + { + codec = null!; + return false; + } + + codec = new Codec(name, type); + + var featureLevel = isEncoder ? codec.EncoderFeatureLevel : codec.DecoderFeatureLevel; + + codec.DecodingSupported = !isEncoder; + codec.EncodingSupported = isEncoder; + featureLevel.SupportsFrameLevelMultithreading = match.Groups[2].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + featureLevel.SupportsSliceLevelMultithreading = match.Groups[3].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + featureLevel.IsExperimental = match.Groups[4].Value != "."; + featureLevel.SupportsDrawHorizBand = match.Groups[5].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + featureLevel.SupportsDirectRendering = match.Groups[6].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + codec.Description = match.Groups[8].Value; + + return true; + } + internal void Merge(Codec other) + { + if (Name != other.Name) + throw new FFMpegException(FFMpegExceptionType.Operation, "different codecs enable to merge"); + + Type |= other.Type; + DecodingSupported |= other.DecodingSupported; + EncodingSupported |= other.EncodingSupported; + IsIntraFrameOnly |= other.IsIntraFrameOnly; + IsLossy |= other.IsLossy; + IsLossless |= other.IsLossless; + + EncoderFeatureLevel.Merge(other.EncoderFeatureLevel); + DecoderFeatureLevel.Merge(other.DecoderFeatureLevel); + + if (Description != other.Description) + Description += "\r\n" + other.Description; + } + } +} diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 0d9786a..8bc235c 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -5,7 +5,9 @@ using System.IO; using System.Linq; using FFMpegCore.Enums; +using FFMpegCore.Exceptions; using FFMpegCore.Helpers; +using FFMpegCore.Models; namespace FFMpegCore { @@ -57,8 +59,8 @@ public static Bitmap Snapshot(MediaAnalysis source, string output, Size? size = .Seek(captureTime) .OutputToFile(output) .ProcessSynchronously(); - - + + if (!success) throw new OperationCanceledException("Could not take snapshot!"); @@ -90,13 +92,13 @@ public static Bitmap Snapshot(MediaAnalysis source, string output, Size? size = public static bool Convert( MediaAnalysis source, string output, - VideoType type = VideoType.Mp4, + ContainerFormat format, Speed speed = Speed.SuperFast, VideoSize size = VideoSize.Original, AudioQuality audioQuality = AudioQuality.Normal, bool multithreaded = false) { - FFMpegHelper.ExtensionExceptionCheck(output, type.Extension()); + FFMpegHelper.ExtensionExceptionCheck(output, format.Extension); FFMpegHelper.ConversionSizeExceptionCheck(source); var scale = VideoSize.Original == size ? 1 : (double)source.PrimaryVideoStream.Height / (int)size; @@ -105,9 +107,9 @@ public static bool Convert( if (outputSize.Width % 2 != 0) outputSize.Width += 1; - return type switch + return format.Name switch { - VideoType.Mp4 => FFMpegArguments + "mp4" => FFMpegArguments .FromInputFiles(true, source.Path) .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibX264) @@ -118,7 +120,7 @@ public static bool Convert( .WithAudioBitrate(audioQuality) .OutputToFile(output) .ProcessSynchronously(), - VideoType.Ogv => FFMpegArguments + "ogv" => FFMpegArguments .FromInputFiles(true, source.Path) .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibTheora) @@ -129,14 +131,14 @@ public static bool Convert( .WithAudioBitrate(audioQuality) .OutputToFile(output) .ProcessSynchronously(), - VideoType.Ts => FFMpegArguments + "mpegts" => FFMpegArguments .FromInputFiles(true, source.Path) .CopyChannel() .WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB) - .ForceFormat(VideoCodec.MpegTs) + .ForceFormat(VideoType.Ts) .OutputToFile(output) .ProcessSynchronously(), - VideoType.WebM => FFMpegArguments + "webm" => FFMpegArguments .FromInputFiles(true, source.Path) .UsingMultithreading(multithreaded) .WithVideoCodec(VideoCodec.LibVpx) @@ -147,7 +149,7 @@ public static bool Convert( .WithAudioBitrate(audioQuality) .OutputToFile(output) .ProcessSynchronously(), - _ => throw new ArgumentOutOfRangeException(nameof(type)) + _ => throw new ArgumentOutOfRangeException(nameof(format)) }; } @@ -322,7 +324,180 @@ public static bool ReplaceAudio(string input, string inputAudio, string output, .OutputToFile(output) .ProcessSynchronously(); } - + + #region PixelFormats + internal static IReadOnlyList GetPixelFormatsInternal() + { + FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); + + var list = new List(); + using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary, "-pix_fmts"); + instance.DataReceived += (e, args) => + { + if (Models.PixelFormat.TryParse(args.Data, out var fmt)) + list.Add(fmt); + }; + + var exitCode = instance.BlockUntilFinished(); + if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData)); + + return list.AsReadOnly(); + } + + public static IReadOnlyList GetPixelFormats() + { + if (!FFMpegOptions.Options.UseCache) + return GetPixelFormatsInternal(); + return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly(); + } + + public static bool TryGetPixelFormat(string name, out Models.PixelFormat fmt) + { + if (!FFMpegOptions.Options.UseCache) + { + fmt = GetPixelFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim()); + return fmt != null; + } + else + return FFMpegCache.PixelFormats.TryGetValue(name, out fmt); + } + + public static Models.PixelFormat GetPixelFormat(string name) + { + if (TryGetPixelFormat(name, out var fmt)) + return fmt; + throw new FFMpegException(FFMpegExceptionType.Operation, $"Pixel format \"{name}\" not supported"); + } + #endregion + + #region Codecs + internal static void ParsePartOfCodecs(Dictionary codecs, string arguments, Func parser) + { + FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); + + using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary, arguments); + instance.DataReceived += (e, args) => + { + var codec = parser(args.Data); + if(codec != null) + if (codecs.TryGetValue(codec.Name, out var parentCodec)) + parentCodec.Merge(codec); + else + codecs.Add(codec.Name, codec); + }; + + var exitCode = instance.BlockUntilFinished(); + if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData)); + } + + internal static Dictionary GetCodecsInternal() + { + var res = new Dictionary(); + ParsePartOfCodecs(res, "-codecs", (s) => + { + if (Codec.TryParseFromCodecs(s, out var codec)) + return codec; + return null; + }); + ParsePartOfCodecs(res, "-encoders", (s) => + { + if (Codec.TryParseFromEncodersDecoders(s, out var codec, true)) + return codec; + return null; + }); + ParsePartOfCodecs(res, "-decoders", (s) => + { + if (Codec.TryParseFromEncodersDecoders(s, out var codec, false)) + return codec; + return null; + }); + + return res; + } + + public static IReadOnlyList GetCodecs() + { + if (!FFMpegOptions.Options.UseCache) + return GetCodecsInternal().Values.ToList().AsReadOnly(); + return FFMpegCache.Codecs.Values.ToList().AsReadOnly(); + } + + public static IReadOnlyList GetCodecs(CodecType type) + { + if (!FFMpegOptions.Options.UseCache) + return GetCodecsInternal().Values.Where(x => x.Type == type).ToList().AsReadOnly(); + return FFMpegCache.Codecs.Values.Where(x=>x.Type == type).ToList().AsReadOnly(); + } + + public static IReadOnlyList GetVideoCodecs() => GetCodecs(CodecType.Video); + public static IReadOnlyList GetAudioCodecs() => GetCodecs(CodecType.Audio); + public static IReadOnlyList GetSubtitleCodecs() => GetCodecs(CodecType.Subtitle); + public static IReadOnlyList GetDataCodecs() => GetCodecs(CodecType.Data); + + public static bool TryGetCodec(string name, out Codec codec) + { + if (!FFMpegOptions.Options.UseCache) + { + codec = GetCodecsInternal().Values.FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim()); + return codec != null; + } + else + return FFMpegCache.Codecs.TryGetValue(name, out codec); + } + + public static Codec GetCodec(string name) + { + if (TryGetCodec(name, out var codec) && codec != null) + return codec; + throw new FFMpegException(FFMpegExceptionType.Operation, $"Codec \"{name}\" not supported"); + } + #endregion + + #region ContainerFormats + internal static IReadOnlyList GetContainersFormatsInternal() + { + FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); + + var list = new List(); + using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary, "-formats"); + instance.DataReceived += (e, args) => + { + if (ContainerFormat.TryParse(args.Data, out var fmt)) + list.Add(fmt); + }; + + var exitCode = instance.BlockUntilFinished(); + if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData)); + + return list.AsReadOnly(); + } + + public static IReadOnlyList GetContainerFormats() + { + if (!FFMpegOptions.Options.UseCache) + return GetContainersFormatsInternal(); + return FFMpegCache.ContainerFormats.Values.ToList().AsReadOnly(); + } + + public static bool TryGetContainerFormat(string name, out ContainerFormat fmt) + { + if (!FFMpegOptions.Options.UseCache) + { + fmt = GetContainersFormatsInternal().FirstOrDefault(x => x.Name == name.ToLowerInvariant().Trim()); + return fmt != null; + } + else + return FFMpegCache.ContainerFormats.TryGetValue(name, out fmt); + } + + public static ContainerFormat GetContinerFormat(string name) + { + if (TryGetContainerFormat(name, out var fmt)) + return fmt; + throw new FFMpegException(FFMpegExceptionType.Operation, $"Container format \"{name}\" not supported"); + } + #endregion + private static void Cleanup(IEnumerable pathList) { foreach (var path in pathList) diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs index 887dfd6..4cef99d 100644 --- a/FFMpegCore/FFMpeg/FFMpegArguments.cs +++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using FFMpegCore.Arguments; using FFMpegCore.Enums; +using FFMpegCore.Models; using FFMpegCore.Pipes; namespace FFMpegCore @@ -35,7 +36,8 @@ private FFMpegArguments(IInputArgument inputArgument) public static FFMpegArguments FromPipe(IPipeDataWriter writer) => new FFMpegArguments(new InputPipeArgument(writer)); - public FFMpegArguments WithAudioCodec(AudioCodec audioCodec) => WithArgument(new AudioCodecArgument(audioCodec)); + public FFMpegArguments WithAudioCodec(Codec audioCodec) => WithArgument(new AudioCodecArgument(audioCodec)); + public FFMpegArguments WithAudioCodec(string audioCodec) => WithArgument(new AudioCodecArgument(audioCodec)); public FFMpegArguments WithAudioBitrate(AudioQuality audioQuality) => WithArgument(new AudioBitrateArgument(audioQuality)); public FFMpegArguments WithAudioBitrate(int bitrate) => WithArgument(new AudioBitrateArgument(bitrate)); public FFMpegArguments WithAudioSamplingRate(int samplingRate = 48000) => WithArgument(new AudioSamplingRateArgument(samplingRate)); @@ -61,7 +63,7 @@ private FFMpegArguments(IInputArgument inputArgument) public FFMpegArguments UsingMultithreading(bool multithread) => WithArgument(new ThreadsArgument(multithread)); public FFMpegArguments UsingThreads(int threads) => WithArgument(new ThreadsArgument(threads)); - public FFMpegArguments WithVideoCodec(VideoCodec videoCodec) => WithArgument(new VideoCodecArgument(videoCodec)); + public FFMpegArguments WithVideoCodec(Codec videoCodec) => WithArgument(new VideoCodecArgument(videoCodec)); public FFMpegArguments WithVideoCodec(string videoCodec) => WithArgument(new VideoCodecArgument(videoCodec)); public FFMpegArguments WithVideoBitrate(int bitrate) => WithArgument(new VideoBitrateArgument(bitrate)); public FFMpegArguments WithFramerate(double framerate) => WithArgument(new FrameRateArgument(framerate)); @@ -77,8 +79,11 @@ private FFMpegArguments(IInputArgument inputArgument) public FFMpegArguments OverwriteExisting() => WithArgument(new OverwriteArgument()); public FFMpegArguments WithVerbosityLevel(VerbosityLevel verbosityLevel = VerbosityLevel.Error) => WithArgument(new VerbosityLevelArgument(verbosityLevel)); - public FFMpegArguments ForceFormat(VideoCodec videoCodec) => WithArgument(new ForceFormatArgument(videoCodec)); - public FFMpegArguments ForceFormat(string videoCodec) => WithArgument(new ForceFormatArgument(videoCodec)); + public FFMpegArguments ForceFormat(ContainerFormat format) => WithArgument(new ForceFormatArgument(format)); + public FFMpegArguments ForceFormat(string format) => WithArgument(new ForceFormatArgument(format)); + public FFMpegArguments ForcePixelFormat(string pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat)); + public FFMpegArguments ForcePixelFormat(PixelFormat pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat)); + public FFMpegArguments DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions)); public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = false) => ToProcessor(new OutputArgument(file, overwrite)); diff --git a/FFMpegCore/FFMpeg/FFMpegCache.cs b/FFMpegCore/FFMpeg/FFMpegCache.cs new file mode 100644 index 0000000..4c54271 --- /dev/null +++ b/FFMpegCore/FFMpeg/FFMpegCache.cs @@ -0,0 +1,57 @@ +using FFMpegCore.Enums; +using FFMpegCore.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FFMpegCore +{ + static class FFMpegCache + { + private static readonly object _syncObject = new object(); + private static Dictionary? _pixelFormats; + private static Dictionary? _codecs; + private static Dictionary? _containers; + + public static IReadOnlyDictionary PixelFormats + { + get + { + if (_pixelFormats == null) //First check not thread safe + lock (_syncObject) + if (_pixelFormats == null)//Second check thread safe + _pixelFormats = FFMpeg.GetPixelFormatsInternal().ToDictionary(x => x.Name); + + return _pixelFormats; + } + + } + public static IReadOnlyDictionary Codecs + { + get + { + if (_codecs == null) //First check not thread safe + lock (_syncObject) + if (_codecs == null)//Second check thread safe + _codecs = FFMpeg.GetCodecsInternal(); + + return _codecs; + } + + } + public static IReadOnlyDictionary ContainerFormats + { + get + { + if (_containers == null) //First check not thread safe + lock (_syncObject) + if (_containers == null)//Second check thread safe + _containers = FFMpeg.GetContainersFormatsInternal().ToDictionary(x => x.Name); + + return _containers; + } + + } + } +} diff --git a/FFMpegCore/FFMpeg/FFMpegOptions.cs b/FFMpegCore/FFMpeg/FFMpegOptions.cs index 175c011..82713f9 100644 --- a/FFMpegCore/FFMpeg/FFMpegOptions.cs +++ b/FFMpegCore/FFMpeg/FFMpegOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text.Json; @@ -9,6 +10,10 @@ public class FFMpegOptions { private static readonly string ConfigFile = Path.Combine(".", "ffmpeg.config.json"); private static readonly string DefaultRoot = Path.Combine(".", "FFMPEG", "bin"); + private static readonly Dictionary DefaultExtensionsOverrides = new Dictionary + { + { "mpegts", ".ts" }, + }; public static FFMpegOptions Options { get; private set; } = new FFMpegOptions(); @@ -25,7 +30,11 @@ public static void Configure(FFMpegOptions options) static FFMpegOptions() { if (File.Exists(ConfigFile)) + { Options = JsonSerializer.Deserialize(File.ReadAllText(ConfigFile)); + foreach (var kv in DefaultExtensionsOverrides) + if (!Options.ExtensionOverrides.ContainsKey(kv.Key)) Options.ExtensionOverrides.Add(kv.Key, kv.Value); + } } public string RootDirectory { get; set; } = DefaultRoot; @@ -34,6 +43,10 @@ static FFMpegOptions() public string FFProbeBinary => FFBinary("FFProbe"); + public Dictionary ExtensionOverrides { get; private set; } = new Dictionary(); + + public bool UseCache { get; set; } = true; + private static string FFBinary(string name) { var ffName = name.ToLowerInvariant(); diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index d9aaa89..4e0b2eb 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -32,4 +32,9 @@ + + + + + diff --git a/FFMpegCore/FFProbe/MediaStream.cs b/FFMpegCore/FFProbe/MediaStream.cs index bdc32e9..8532e51 100644 --- a/FFMpegCore/FFProbe/MediaStream.cs +++ b/FFMpegCore/FFProbe/MediaStream.cs @@ -1,4 +1,5 @@ -using System; +using FFMpegCore.Enums; +using System; namespace FFMpegCore { @@ -9,5 +10,7 @@ public class MediaStream public string CodecLongName { get; internal set; } = null!; public int BitRate { get; internal set; } public TimeSpan Duration { get; internal set; } + + public Codec GetCodecInfo() => FFMpeg.GetCodec(CodecName); } } \ No newline at end of file diff --git a/FFMpegCore/FFProbe/VideoStream.cs b/FFMpegCore/FFProbe/VideoStream.cs index 05b273c..1bf1ebd 100644 --- a/FFMpegCore/FFProbe/VideoStream.cs +++ b/FFMpegCore/FFProbe/VideoStream.cs @@ -1,4 +1,6 @@ -namespace FFMpegCore +using FFMpegCore.Models; + +namespace FFMpegCore { public class VideoStream : MediaStream { @@ -10,5 +12,7 @@ public class VideoStream : MediaStream public int Height { get; internal set; } public double FrameRate { get; internal set; } public string PixelFormat { get; internal set; } = null!; + + public PixelFormat GetPixelFormatInfo() => FFMpeg.GetPixelFormat(PixelFormat); } } \ No newline at end of file From fa7850b135cd7c52479d30d46c808c9e1d3037aa 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: Tue, 12 May 2020 17:53:52 +0300 Subject: [PATCH 2/5] Updated PixelFormat namespace Former-commit-id: 17fa6aafee0bca998a9987d23addc72a3148e8b4 --- FFMpegCore.Test/VideoTest.cs | 28 +++++++++---------- .../FFMpeg/Arguments/ForcePixelFormat.cs | 5 +--- FFMpegCore/FFMpeg/Enums/PixelFormat.cs | 2 +- FFMpegCore/FFMpeg/FFMpeg.cs | 13 ++++----- FFMpegCore/FFMpeg/FFMpegArguments.cs | 1 - FFMpegCore/FFMpeg/FFMpegCache.cs | 3 -- FFMpegCore/FFProbe/VideoStream.cs | 2 +- 7 files changed, 23 insertions(+), 31 deletions(-) diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index 358634d..6bffa89 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -212,7 +212,7 @@ public void Convert(ContainerFormat type, params IArgument[] inputArguments) Convert(type, null, inputArguments); } - public void ConvertFromPipe(ContainerFormat type, PixelFormat fmt, params IArgument[] inputArguments) + public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] inputArguments) { var output = Input.OutputLocation(type); @@ -280,10 +280,10 @@ public void Video_ToMP4_Args() } [DataTestMethod] - [DataRow(PixelFormat.Format24bppRgb)] - [DataRow(PixelFormat.Format32bppArgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)] // [DataRow(PixelFormat.Format48bppRgb)] - public void Video_ToMP4_Args_Pipe(PixelFormat pixelFormat) + public void Video_ToMP4_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat) { ConvertFromPipe(VideoType.Mp4, pixelFormat, new VideoCodecArgument(VideoCodec.LibX264)); } @@ -358,10 +358,10 @@ public void Video_ToTS_Args() } [DataTestMethod] - [DataRow(PixelFormat.Format24bppRgb)] - [DataRow(PixelFormat.Format32bppArgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)] // [DataRow(PixelFormat.Format48bppRgb)] - public void Video_ToTS_Args_Pipe(PixelFormat pixelFormat) + public void Video_ToTS_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat) { ConvertFromPipe(VideoType.Ts, pixelFormat, new ForceFormatArgument(VideoType.Ts)); } @@ -379,10 +379,10 @@ public void Video_ToOGV_Resize_Args() } [DataTestMethod] - [DataRow(PixelFormat.Format24bppRgb)] - [DataRow(PixelFormat.Format32bppArgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)] // [DataRow(PixelFormat.Format48bppRgb)] - public void Video_ToOGV_Resize_Args_Pipe(PixelFormat pixelFormat) + public void Video_ToOGV_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat) { ConvertFromPipe(VideoType.Ogv, pixelFormat, new ScaleArgument(VideoSize.Ed), new VideoCodecArgument(VideoCodec.LibTheora)); } @@ -400,10 +400,10 @@ public void Video_ToMP4_Resize_Args() } [DataTestMethod] - [DataRow(PixelFormat.Format24bppRgb)] - [DataRow(PixelFormat.Format32bppArgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format24bppRgb)] + [DataRow(System.Drawing.Imaging.PixelFormat.Format32bppArgb)] // [DataRow(PixelFormat.Format48bppRgb)] - public void Video_ToMP4_Resize_Args_Pipe(PixelFormat pixelFormat) + public void Video_ToMP4_Resize_Args_Pipe(System.Drawing.Imaging.PixelFormat pixelFormat) { ConvertFromPipe(VideoType.Mp4, pixelFormat, new ScaleArgument(VideoSize.Ld), new VideoCodecArgument(VideoCodec.LibX264)); } @@ -625,7 +625,7 @@ public void Video_TranscodeInMemory() { using var resStream = new MemoryStream(); var reader = new StreamPipeDataReader(resStream); - var writer = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(128, PixelFormat.Format24bppRgb, 128, 128)); + var writer = new RawVideoPipeDataWriter(BitmapSource.CreateBitmaps(128, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128)); FFMpegArguments .FromPipe(writer) diff --git a/FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs b/FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs index 04b84ad..7614ae3 100644 --- a/FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs +++ b/FFMpegCore/FFMpeg/Arguments/ForcePixelFormat.cs @@ -1,7 +1,4 @@ -using FFMpegCore.Models; -using System; -using System.Collections.Generic; -using System.Text; +using FFMpegCore.Enums; namespace FFMpegCore.Arguments { diff --git a/FFMpegCore/FFMpeg/Enums/PixelFormat.cs b/FFMpegCore/FFMpeg/Enums/PixelFormat.cs index dad0a6e..4262a74 100644 --- a/FFMpegCore/FFMpeg/Enums/PixelFormat.cs +++ b/FFMpegCore/FFMpeg/Enums/PixelFormat.cs @@ -3,7 +3,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace FFMpegCore.Models +namespace FFMpegCore.Enums { public class PixelFormat { diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 8bc235c..bfc7509 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -7,7 +7,6 @@ using FFMpegCore.Enums; using FFMpegCore.Exceptions; using FFMpegCore.Helpers; -using FFMpegCore.Models; namespace FFMpegCore { @@ -326,15 +325,15 @@ public static bool ReplaceAudio(string input, string inputAudio, string output, } #region PixelFormats - internal static IReadOnlyList GetPixelFormatsInternal() + internal static IReadOnlyList GetPixelFormatsInternal() { FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); - var list = new List(); + var list = new List(); using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary, "-pix_fmts"); instance.DataReceived += (e, args) => { - if (Models.PixelFormat.TryParse(args.Data, out var fmt)) + if (Enums.PixelFormat.TryParse(args.Data, out var fmt)) list.Add(fmt); }; @@ -344,14 +343,14 @@ public static bool ReplaceAudio(string input, string inputAudio, string output, return list.AsReadOnly(); } - public static IReadOnlyList GetPixelFormats() + public static IReadOnlyList GetPixelFormats() { if (!FFMpegOptions.Options.UseCache) return GetPixelFormatsInternal(); return FFMpegCache.PixelFormats.Values.ToList().AsReadOnly(); } - public static bool TryGetPixelFormat(string name, out Models.PixelFormat fmt) + public static bool TryGetPixelFormat(string name, out Enums.PixelFormat fmt) { if (!FFMpegOptions.Options.UseCache) { @@ -362,7 +361,7 @@ public static bool TryGetPixelFormat(string name, out Models.PixelFormat fmt) return FFMpegCache.PixelFormats.TryGetValue(name, out fmt); } - public static Models.PixelFormat GetPixelFormat(string name) + public static Enums.PixelFormat GetPixelFormat(string name) { if (TryGetPixelFormat(name, out var fmt)) return fmt; diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs index 4cef99d..bcfe28c 100644 --- a/FFMpegCore/FFMpeg/FFMpegArguments.cs +++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using FFMpegCore.Arguments; using FFMpegCore.Enums; -using FFMpegCore.Models; using FFMpegCore.Pipes; namespace FFMpegCore diff --git a/FFMpegCore/FFMpeg/FFMpegCache.cs b/FFMpegCore/FFMpeg/FFMpegCache.cs index 4c54271..0847202 100644 --- a/FFMpegCore/FFMpeg/FFMpegCache.cs +++ b/FFMpegCore/FFMpeg/FFMpegCache.cs @@ -1,9 +1,6 @@ using FFMpegCore.Enums; -using FFMpegCore.Models; -using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace FFMpegCore { diff --git a/FFMpegCore/FFProbe/VideoStream.cs b/FFMpegCore/FFProbe/VideoStream.cs index 1bf1ebd..18533bd 100644 --- a/FFMpegCore/FFProbe/VideoStream.cs +++ b/FFMpegCore/FFProbe/VideoStream.cs @@ -1,4 +1,4 @@ -using FFMpegCore.Models; +using FFMpegCore.Enums; namespace FFMpegCore { From 8c6d097305d06640e7e05c01982ce1266f7a2a67 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: Tue, 12 May 2020 17:54:39 +0300 Subject: [PATCH 3/5] Renamed files Former-commit-id: 18cc364fff087ece1e6e5654b72eba1826b8aff8 --- FFMpegCore/FFMpeg/Enums/Codec.cs | 188 ++++++++++++++++++++------ FFMpegCore/FFMpeg/Enums/Enums.cs | 54 ++++++++ FFMpegCore/FFMpeg/Enums/VideoCodec.cs | 154 --------------------- 3 files changed, 198 insertions(+), 198 deletions(-) create mode 100644 FFMpegCore/FFMpeg/Enums/Enums.cs delete mode 100644 FFMpegCore/FFMpeg/Enums/VideoCodec.cs diff --git a/FFMpegCore/FFMpeg/Enums/Codec.cs b/FFMpegCore/FFMpeg/Enums/Codec.cs index 2bbab5f..1c4ce31 100644 --- a/FFMpegCore/FFMpeg/Enums/Codec.cs +++ b/FFMpegCore/FFMpeg/Enums/Codec.cs @@ -1,54 +1,154 @@ -namespace FFMpegCore.Enums +using FFMpegCore.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace FFMpegCore.Enums { - public enum CodecType + public enum FeatureStatus { - Unknown = 0, - Video = 1 << 1, - Audio = 1 << 2, - Subtitle = 1 << 3, - Data = 1 << 4, + Unknown, + NotSupported, + Supported, } - public static class VideoCodec + public class Codec { - public static Codec LibX264 => FFMpeg.GetCodec("libx264"); - public static Codec LibVpx => FFMpeg.GetCodec("libvpx"); - public static Codec LibTheora => FFMpeg.GetCodec("libtheora"); - public static Codec Png => FFMpeg.GetCodec("png"); - public static Codec MpegTs => FFMpeg.GetCodec("mpegts"); - } + private static readonly Regex _codecsFormatRegex = new Regex(@"([D\.])([E\.])([VASD\.])([I\.])([L\.])([S\.])\s+([a-z0-9_-]+)\s+(.+)"); + private static readonly Regex _decodersEncodersFormatRegex = new Regex(@"([VASD\.])([F\.])([S\.])([X\.])([B\.])([D\.])\s+([a-z0-9_-]+)\s+(.+)"); - public static class AudioCodec - { - public static Codec Aac => FFMpeg.GetCodec("aac"); - public static Codec LibVorbis => FFMpeg.GetCodec("libvorbis"); - public static Codec LibFdk_Aac => FFMpeg.GetCodec("libfdk_aac"); - public static Codec Ac3 => FFMpeg.GetCodec("ac3"); - public static Codec Eac3 => FFMpeg.GetCodec("eac3"); - public static Codec LibMp3Lame => FFMpeg.GetCodec("libmp3lame"); - } + public class FeatureLevel + { + public bool IsExperimental { get; internal set; } + public FeatureStatus SupportsFrameLevelMultithreading { get; internal set; } + public FeatureStatus SupportsSliceLevelMultithreading { get; internal set; } + public FeatureStatus SupportsDrawHorizBand { get; internal set; } + public FeatureStatus SupportsDirectRendering { get; internal set; } - public static class VideoType - { - public static ContainerFormat MpegTs => FFMpeg.GetContinerFormat("mpegts"); - public static ContainerFormat Ts => FFMpeg.GetContinerFormat("mpegts"); - public static ContainerFormat Mp4 => FFMpeg.GetContinerFormat("mp4"); - public static ContainerFormat Mov => FFMpeg.GetContinerFormat("mov"); - public static ContainerFormat Avi => FFMpeg.GetContinerFormat("avi"); - public static ContainerFormat Ogv => FFMpeg.GetContinerFormat("ogv"); - public static ContainerFormat WebM => FFMpeg.GetContinerFormat("webm"); - } + internal void Merge(FeatureLevel other) + { + IsExperimental |= other.IsExperimental; + SupportsFrameLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsFrameLevelMultithreading, (int)other.SupportsFrameLevelMultithreading); + SupportsSliceLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsSliceLevelMultithreading, (int)other.SupportsSliceLevelMultithreading); + SupportsDrawHorizBand = (FeatureStatus)Math.Max((int)SupportsDrawHorizBand, (int)other.SupportsDrawHorizBand); + SupportsDirectRendering = (FeatureStatus)Math.Max((int)SupportsDirectRendering, (int)other.SupportsDirectRendering); + } + } - public enum Filter - { - H264_Mp4ToAnnexB, - Aac_AdtstoAsc - } + public string Name { get; private set; } + public CodecType Type { get; private set; } + public bool DecodingSupported { get; private set; } + public bool EncodingSupported { get; private set; } + public bool IsIntraFrameOnly { get; private set; } + public bool IsLossy { get; private set; } + public bool IsLossless { get; private set; } + public string Description { get; private set; } = null!; - public enum Channel - { - Audio, - Video, - Both + public FeatureLevel EncoderFeatureLevel { get; private set; } + public FeatureLevel DecoderFeatureLevel { get; private set; } + + internal Codec(string name, CodecType type) + { + EncoderFeatureLevel = new FeatureLevel(); + DecoderFeatureLevel = new FeatureLevel(); + Name = name; + Type = type; + } + + internal static bool TryParseFromCodecs(string line, out Codec codec) + { + var match = _codecsFormatRegex.Match(line); + if (!match.Success) + { + codec = null!; + return false; + } + + var name = match.Groups[7].Value; + var type = match.Groups[3].Value switch + { + "V" => CodecType.Video, + "A" => CodecType.Audio, + "D" => CodecType.Data, + "S" => CodecType.Subtitle, + _ => CodecType.Unknown + }; + + if(type == CodecType.Unknown) + { + codec = null!; + return false; + } + + codec = new Codec(name, type); + + codec.DecodingSupported = match.Groups[1].Value != "."; + codec.EncodingSupported = match.Groups[2].Value != "."; + codec.IsIntraFrameOnly = match.Groups[4].Value != "."; + codec.IsLossy = match.Groups[5].Value != "."; + codec.IsLossless = match.Groups[6].Value != "."; + codec.Description = match.Groups[8].Value; + + return true; + } + internal static bool TryParseFromEncodersDecoders(string line, out Codec codec, bool isEncoder) + { + var match = _decodersEncodersFormatRegex.Match(line); + if (!match.Success) + { + codec = null!; + return false; + } + + var name = match.Groups[7].Value; + var type = match.Groups[1].Value switch + { + "V" => CodecType.Video, + "A" => CodecType.Audio, + "D" => CodecType.Data, + "S" => CodecType.Subtitle, + _ => CodecType.Unknown + }; + + if (type == CodecType.Unknown) + { + codec = null!; + return false; + } + + codec = new Codec(name, type); + + var featureLevel = isEncoder ? codec.EncoderFeatureLevel : codec.DecoderFeatureLevel; + + codec.DecodingSupported = !isEncoder; + codec.EncodingSupported = isEncoder; + featureLevel.SupportsFrameLevelMultithreading = match.Groups[2].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + featureLevel.SupportsSliceLevelMultithreading = match.Groups[3].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + featureLevel.IsExperimental = match.Groups[4].Value != "."; + featureLevel.SupportsDrawHorizBand = match.Groups[5].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + featureLevel.SupportsDirectRendering = match.Groups[6].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; + codec.Description = match.Groups[8].Value; + + return true; + } + internal void Merge(Codec other) + { + if (Name != other.Name) + throw new FFMpegException(FFMpegExceptionType.Operation, "different codecs enable to merge"); + + Type |= other.Type; + DecodingSupported |= other.DecodingSupported; + EncodingSupported |= other.EncodingSupported; + IsIntraFrameOnly |= other.IsIntraFrameOnly; + IsLossy |= other.IsLossy; + IsLossless |= other.IsLossless; + + EncoderFeatureLevel.Merge(other.EncoderFeatureLevel); + DecoderFeatureLevel.Merge(other.DecoderFeatureLevel); + + if (Description != other.Description) + Description += "\r\n" + other.Description; + } } -} \ No newline at end of file +} diff --git a/FFMpegCore/FFMpeg/Enums/Enums.cs b/FFMpegCore/FFMpeg/Enums/Enums.cs new file mode 100644 index 0000000..2bbab5f --- /dev/null +++ b/FFMpegCore/FFMpeg/Enums/Enums.cs @@ -0,0 +1,54 @@ +namespace FFMpegCore.Enums +{ + public enum CodecType + { + Unknown = 0, + Video = 1 << 1, + Audio = 1 << 2, + Subtitle = 1 << 3, + Data = 1 << 4, + } + + public static class VideoCodec + { + public static Codec LibX264 => FFMpeg.GetCodec("libx264"); + public static Codec LibVpx => FFMpeg.GetCodec("libvpx"); + public static Codec LibTheora => FFMpeg.GetCodec("libtheora"); + public static Codec Png => FFMpeg.GetCodec("png"); + public static Codec MpegTs => FFMpeg.GetCodec("mpegts"); + } + + public static class AudioCodec + { + public static Codec Aac => FFMpeg.GetCodec("aac"); + public static Codec LibVorbis => FFMpeg.GetCodec("libvorbis"); + public static Codec LibFdk_Aac => FFMpeg.GetCodec("libfdk_aac"); + public static Codec Ac3 => FFMpeg.GetCodec("ac3"); + public static Codec Eac3 => FFMpeg.GetCodec("eac3"); + public static Codec LibMp3Lame => FFMpeg.GetCodec("libmp3lame"); + } + + public static class VideoType + { + public static ContainerFormat MpegTs => FFMpeg.GetContinerFormat("mpegts"); + public static ContainerFormat Ts => FFMpeg.GetContinerFormat("mpegts"); + public static ContainerFormat Mp4 => FFMpeg.GetContinerFormat("mp4"); + public static ContainerFormat Mov => FFMpeg.GetContinerFormat("mov"); + public static ContainerFormat Avi => FFMpeg.GetContinerFormat("avi"); + public static ContainerFormat Ogv => FFMpeg.GetContinerFormat("ogv"); + public static ContainerFormat WebM => FFMpeg.GetContinerFormat("webm"); + } + + public enum Filter + { + H264_Mp4ToAnnexB, + Aac_AdtstoAsc + } + + public enum Channel + { + Audio, + Video, + Both + } +} \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/Enums/VideoCodec.cs b/FFMpegCore/FFMpeg/Enums/VideoCodec.cs deleted file mode 100644 index 1c4ce31..0000000 --- a/FFMpegCore/FFMpeg/Enums/VideoCodec.cs +++ /dev/null @@ -1,154 +0,0 @@ -using FFMpegCore.Exceptions; -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace FFMpegCore.Enums -{ - public enum FeatureStatus - { - Unknown, - NotSupported, - Supported, - } - - public class Codec - { - private static readonly Regex _codecsFormatRegex = new Regex(@"([D\.])([E\.])([VASD\.])([I\.])([L\.])([S\.])\s+([a-z0-9_-]+)\s+(.+)"); - private static readonly Regex _decodersEncodersFormatRegex = new Regex(@"([VASD\.])([F\.])([S\.])([X\.])([B\.])([D\.])\s+([a-z0-9_-]+)\s+(.+)"); - - public class FeatureLevel - { - public bool IsExperimental { get; internal set; } - public FeatureStatus SupportsFrameLevelMultithreading { get; internal set; } - public FeatureStatus SupportsSliceLevelMultithreading { get; internal set; } - public FeatureStatus SupportsDrawHorizBand { get; internal set; } - public FeatureStatus SupportsDirectRendering { get; internal set; } - - internal void Merge(FeatureLevel other) - { - IsExperimental |= other.IsExperimental; - SupportsFrameLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsFrameLevelMultithreading, (int)other.SupportsFrameLevelMultithreading); - SupportsSliceLevelMultithreading = (FeatureStatus)Math.Max((int)SupportsSliceLevelMultithreading, (int)other.SupportsSliceLevelMultithreading); - SupportsDrawHorizBand = (FeatureStatus)Math.Max((int)SupportsDrawHorizBand, (int)other.SupportsDrawHorizBand); - SupportsDirectRendering = (FeatureStatus)Math.Max((int)SupportsDirectRendering, (int)other.SupportsDirectRendering); - } - } - - public string Name { get; private set; } - public CodecType Type { get; private set; } - public bool DecodingSupported { get; private set; } - public bool EncodingSupported { get; private set; } - public bool IsIntraFrameOnly { get; private set; } - public bool IsLossy { get; private set; } - public bool IsLossless { get; private set; } - public string Description { get; private set; } = null!; - - public FeatureLevel EncoderFeatureLevel { get; private set; } - public FeatureLevel DecoderFeatureLevel { get; private set; } - - internal Codec(string name, CodecType type) - { - EncoderFeatureLevel = new FeatureLevel(); - DecoderFeatureLevel = new FeatureLevel(); - Name = name; - Type = type; - } - - internal static bool TryParseFromCodecs(string line, out Codec codec) - { - var match = _codecsFormatRegex.Match(line); - if (!match.Success) - { - codec = null!; - return false; - } - - var name = match.Groups[7].Value; - var type = match.Groups[3].Value switch - { - "V" => CodecType.Video, - "A" => CodecType.Audio, - "D" => CodecType.Data, - "S" => CodecType.Subtitle, - _ => CodecType.Unknown - }; - - if(type == CodecType.Unknown) - { - codec = null!; - return false; - } - - codec = new Codec(name, type); - - codec.DecodingSupported = match.Groups[1].Value != "."; - codec.EncodingSupported = match.Groups[2].Value != "."; - codec.IsIntraFrameOnly = match.Groups[4].Value != "."; - codec.IsLossy = match.Groups[5].Value != "."; - codec.IsLossless = match.Groups[6].Value != "."; - codec.Description = match.Groups[8].Value; - - return true; - } - internal static bool TryParseFromEncodersDecoders(string line, out Codec codec, bool isEncoder) - { - var match = _decodersEncodersFormatRegex.Match(line); - if (!match.Success) - { - codec = null!; - return false; - } - - var name = match.Groups[7].Value; - var type = match.Groups[1].Value switch - { - "V" => CodecType.Video, - "A" => CodecType.Audio, - "D" => CodecType.Data, - "S" => CodecType.Subtitle, - _ => CodecType.Unknown - }; - - if (type == CodecType.Unknown) - { - codec = null!; - return false; - } - - codec = new Codec(name, type); - - var featureLevel = isEncoder ? codec.EncoderFeatureLevel : codec.DecoderFeatureLevel; - - codec.DecodingSupported = !isEncoder; - codec.EncodingSupported = isEncoder; - featureLevel.SupportsFrameLevelMultithreading = match.Groups[2].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; - featureLevel.SupportsSliceLevelMultithreading = match.Groups[3].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; - featureLevel.IsExperimental = match.Groups[4].Value != "."; - featureLevel.SupportsDrawHorizBand = match.Groups[5].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; - featureLevel.SupportsDirectRendering = match.Groups[6].Value != "." ? FeatureStatus.Supported : FeatureStatus.NotSupported; - codec.Description = match.Groups[8].Value; - - return true; - } - internal void Merge(Codec other) - { - if (Name != other.Name) - throw new FFMpegException(FFMpegExceptionType.Operation, "different codecs enable to merge"); - - Type |= other.Type; - DecodingSupported |= other.DecodingSupported; - EncodingSupported |= other.EncodingSupported; - IsIntraFrameOnly |= other.IsIntraFrameOnly; - IsLossy |= other.IsLossy; - IsLossless |= other.IsLossless; - - EncoderFeatureLevel.Merge(other.EncoderFeatureLevel); - DecoderFeatureLevel.Merge(other.DecoderFeatureLevel); - - if (Description != other.Description) - Description += "\r\n" + other.Description; - } - } -} From d47ef3b5b5cff595660391524fdd93f49e9e45fa 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: Tue, 12 May 2020 17:55:17 +0300 Subject: [PATCH 4/5] Removed empty Enums folder from project Former-commit-id: d76a059a36d4a7d5fa6cd1d116ce05904355bbf8 --- FFMpegCore/FFMpegCore.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index 4e0b2eb..996d76c 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -33,7 +33,6 @@ - From c613e0e715d4849c20ac5b2c37273dba35e1a4f3 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Tue, 12 May 2020 17:27:55 +0200 Subject: [PATCH 5/5] Update ci.yml Former-commit-id: 038d7d69ddc1440d559ab147944450b6a8d1ff70 --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61794e0..46718fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: jobs: ci: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v1 - name: Prepare FFMpeg