diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs index 7a058a2..6421560 100644 --- a/FFMpegCore.Test/ArgumentBuilderTest.cs +++ b/FFMpegCore.Test/ArgumentBuilderTest.cs @@ -15,210 +15,197 @@ public class ArgumentBuilderTest : BaseTest [TestMethod] public void Builder_BuildString_IO_1() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").OutputToFile("output.mp4").Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4").Arguments; Assert.AreEqual("-i \"input.mp4\" \"output.mp4\" -y", str); } [TestMethod] public void Builder_BuildString_Scale() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Scale(VideoSize.Hd).OutputToFile("output.mp4").Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.Scale(VideoSize.Hd)).Arguments; Assert.AreEqual("-i \"input.mp4\" -vf scale=-1:720 \"output.mp4\" -y", str); } [TestMethod] public void Builder_BuildString_AudioCodec() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioCodec(AudioCodec.Aac).OutputToFile("output.mp4").Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.WithAudioCodec(AudioCodec.Aac)).Arguments; Assert.AreEqual("-i \"input.mp4\" -c:a aac \"output.mp4\" -y", str); } [TestMethod] public void Builder_BuildString_AudioBitrate() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioBitrate(AudioQuality.Normal).OutputToFile("output.mp4").Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.WithAudioBitrate(AudioQuality.Normal)).Arguments; Assert.AreEqual("-i \"input.mp4\" -b:a 128k \"output.mp4\" -y", str); } [TestMethod] public void Builder_BuildString_Quiet() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVerbosityLevel().OutputToFile("output.mp4", false).Arguments; - Assert.AreEqual("-i \"input.mp4\" -hide_banner -loglevel error \"output.mp4\"", str); + var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithVerbosityLevel()).OutputToFile("output.mp4", false).Arguments; + Assert.AreEqual("-hide_banner -loglevel error -i \"input.mp4\" \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_AudioCodec_Fluent() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioCodec(AudioCodec.Aac).WithAudioBitrate(128).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithAudioCodec(AudioCodec.Aac).WithAudioBitrate(128)).Arguments; Assert.AreEqual("-i \"input.mp4\" -c:a aac -b:a 128k \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_BitStream() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithBitStreamFilter(Channel.Audio, Filter.H264_Mp4ToAnnexB).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithBitStreamFilter(Channel.Audio, Filter.H264_Mp4ToAnnexB)).Arguments; Assert.AreEqual("-i \"input.mp4\" -bsf:a h264_mp4toannexb \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Concat() { - var str = FFMpegArguments.FromConcatenation(_concatFiles).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromConcatInput(_concatFiles).OutputToFile("output.mp4", false).Arguments; Assert.AreEqual("-i \"concat:1.mp4|2.mp4|3.mp4|4.mp4\" \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Copy_Audio() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").CopyChannel(Channel.Audio).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.CopyChannel(Channel.Audio)).Arguments; Assert.AreEqual("-i \"input.mp4\" -c:a copy \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Copy_Video() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").CopyChannel(Channel.Video).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.CopyChannel(Channel.Video)).Arguments; Assert.AreEqual("-i \"input.mp4\" -c:v copy \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Copy_Both() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").CopyChannel().OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.CopyChannel()).Arguments; Assert.AreEqual("-i \"input.mp4\" -c copy \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_DisableChannel_Audio() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").DisableChannel(Channel.Audio).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.DisableChannel(Channel.Audio)).Arguments; Assert.AreEqual("-i \"input.mp4\" -an \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_DisableChannel_Video() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").DisableChannel(Channel.Video).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.DisableChannel(Channel.Video)).Arguments; Assert.AreEqual("-i \"input.mp4\" -vn \"output.mp4\"", str); } - [TestMethod] - public void Builder_BuildString_DisableChannel_Both() - { - Assert.ThrowsException(() => FFMpegArguments.FromInputFiles(true, "input.mp4").DisableChannel(Channel.Both)); - } - [TestMethod] public void Builder_BuildString_AudioSamplingRate_Default() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioSamplingRate().OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithAudioSamplingRate()).Arguments; Assert.AreEqual("-i \"input.mp4\" -ar 48000 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_AudioSamplingRate() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithAudioSamplingRate(44000).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithAudioSamplingRate(44000)).Arguments; Assert.AreEqual("-i \"input.mp4\" -ar 44000 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_VariableBitrate() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVariableBitrate(5).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithVariableBitrate(5)).Arguments; Assert.AreEqual("-i \"input.mp4\" -vbr 5 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Faststart() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithFastStart().OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithFastStart()).Arguments; Assert.AreEqual("-i \"input.mp4\" -movflags faststart \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Overwrite() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").OverwriteExisting().OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.OverwriteExisting()).Arguments; Assert.AreEqual("-i \"input.mp4\" -y \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_RemoveMetadata() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithoutMetadata().OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithoutMetadata()).Arguments; Assert.AreEqual("-i \"input.mp4\" -map_metadata -1 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Transpose() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Transpose(Transposition.CounterClockwise90).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Transpose(Transposition.CounterClockwise90)).Arguments; Assert.AreEqual("-i \"input.mp4\" -vf \"transpose=2\" \"output.mp4\"", str); } - [TestMethod] - public void Builder_BuildString_CpuSpeed() - { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithCpuSpeed(10).OutputToFile("output.mp4", false).Arguments; - Assert.AreEqual("-i \"input.mp4\" -quality good -cpu-used 10 -deadline realtime \"output.mp4\"", str); - } - [TestMethod] public void Builder_BuildString_ForceFormat() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").ForceFormat(VideoType.Mp4).OutputToFile("output.mp4", false).Arguments; - Assert.AreEqual("-i \"input.mp4\" -f mp4 \"output.mp4\"", str); + var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.ForceFormat(VideoType.Mp4)).OutputToFile("output.mp4", false, opt => opt.ForceFormat(VideoType.Mp4)).Arguments; + Assert.AreEqual("-f mp4 -i \"input.mp4\" -f mp4 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_FrameOutputCount() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithFrameOutputCount(50).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithFrameOutputCount(50)).Arguments; Assert.AreEqual("-i \"input.mp4\" -vframes 50 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_FrameRate() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithFramerate(50).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithFramerate(50)).Arguments; Assert.AreEqual("-i \"input.mp4\" -r 50 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Loop() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Loop(50).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Loop(50)).Arguments; Assert.AreEqual("-i \"input.mp4\" -loop 50 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Seek() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Seek(TimeSpan.FromSeconds(10)).OutputToFile("output.mp4", false).Arguments; - Assert.AreEqual("-i \"input.mp4\" -ss 00:00:10 \"output.mp4\"", str); + var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.Seek(TimeSpan.FromSeconds(10))).OutputToFile("output.mp4", false, opt => opt.Seek(TimeSpan.FromSeconds(10))).Arguments; + Assert.AreEqual("-ss 00:00:10 -i \"input.mp4\" -ss 00:00:10 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Shortest() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").UsingShortest().OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.UsingShortest()).Arguments; Assert.AreEqual("-i \"input.mp4\" -shortest \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Size() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").Resize(1920, 1080).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.Resize(1920, 1080)).Arguments; Assert.AreEqual("-i \"input.mp4\" -s 1920x1080 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Speed() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithSpeedPreset(Speed.Fast).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithSpeedPreset(Speed.Fast)).Arguments; Assert.AreEqual("-i \"input.mp4\" -preset fast \"output.mp4\"", str); } @@ -226,17 +213,18 @@ public void Builder_BuildString_Speed() public void Builder_BuildString_DrawtextFilter() { var str = FFMpegArguments - .FromInputFiles(true, "input.mp4") - .DrawText(DrawTextOptions - .Create("Stack Overflow", "/path/to/font.ttf") - .WithParameter("fontcolor", "white") - .WithParameter("fontsize", "24") - .WithParameter("box", "1") - .WithParameter("boxcolor", "black@0.5") - .WithParameter("boxborderw", "5") - .WithParameter("x", "(w-text_w)/2") - .WithParameter("y", "(h-text_h)/2")) - .OutputToFile("output.mp4", false).Arguments; + .FromFileInput("input.mp4") + .OutputToFile("output.mp4", false, opt => opt + .DrawText(DrawTextOptions + .Create("Stack Overflow", "/path/to/font.ttf") + .WithParameter("fontcolor", "white") + .WithParameter("fontsize", "24") + .WithParameter("box", "1") + .WithParameter("boxcolor", "black@0.5") + .WithParameter("boxborderw", "5") + .WithParameter("x", "(w-text_w)/2") + .WithParameter("y", "(h-text_h)/2"))) + .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\"", str); } @@ -245,10 +233,11 @@ public void Builder_BuildString_DrawtextFilter() public void Builder_BuildString_DrawtextFilter_Alt() { var str = FFMpegArguments - .FromInputFiles(true, "input.mp4") - .DrawText(DrawTextOptions - .Create("Stack Overflow", "/path/to/font.ttf", ("fontcolor", "white"), ("fontsize", "24"))) - .OutputToFile("output.mp4", false).Arguments; + .FromFileInput("input.mp4") + .OutputToFile("output.mp4", false, opt => opt + .DrawText(DrawTextOptions + .Create("Stack Overflow", "/path/to/font.ttf", ("fontcolor", "white"), ("fontsize", "24")))) + .Arguments; Assert.AreEqual("-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow':fontfile=/path/to/font.ttf:fontcolor=white:fontsize=24\" \"output.mp4\"", str); } @@ -256,35 +245,35 @@ public void Builder_BuildString_DrawtextFilter_Alt() [TestMethod] public void Builder_BuildString_StartNumber() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithStartNumber(50).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithStartNumber(50)).Arguments; Assert.AreEqual("-i \"input.mp4\" -start_number 50 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Threads_1() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").UsingThreads(50).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.UsingThreads(50)).Arguments; Assert.AreEqual("-i \"input.mp4\" -threads 50 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Threads_2() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").UsingMultithreading(true).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.UsingMultithreading(true)).Arguments; Assert.AreEqual($"-i \"input.mp4\" -threads {Environment.ProcessorCount} \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Codec() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithVideoCodec(VideoCodec.LibX264).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithVideoCodec(VideoCodec.LibX264)).Arguments; 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).ForcePixelFormat("yuv420p").OutputToFile("output.mp4").Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", true, opt => opt.WithVideoCodec(VideoCodec.LibX264).ForcePixelFormat("yuv420p")).Arguments; Assert.AreEqual("-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p \"output.mp4\" -y", str); } @@ -292,17 +281,17 @@ public void Builder_BuildString_Codec_Override() [TestMethod] public void Builder_BuildString_Duration() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithDuration(TimeSpan.FromSeconds(20)).OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithDuration(TimeSpan.FromSeconds(20))).Arguments; Assert.AreEqual("-i \"input.mp4\" -t 00:00:20 \"output.mp4\"", str); } [TestMethod] public void Builder_BuildString_Raw() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithCustomArgument(null).OutputToFile("output.mp4", false).Arguments; - Assert.AreEqual("-i \"input.mp4\" \"output.mp4\"", str); + var str = FFMpegArguments.FromFileInput("input.mp4", false, opt => opt.WithCustomArgument(null!)).OutputToFile("output.mp4", false, opt => opt.WithCustomArgument(null!)).Arguments; + Assert.AreEqual(" -i \"input.mp4\" \"output.mp4\"", str); - str = FFMpegArguments.FromInputFiles(true, "input.mp4").WithCustomArgument("-acodec copy").OutputToFile("output.mp4", false).Arguments; + str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.WithCustomArgument("-acodec copy")).Arguments; Assert.AreEqual("-i \"input.mp4\" -acodec copy \"output.mp4\"", str); } @@ -310,7 +299,7 @@ public void Builder_BuildString_Raw() [TestMethod] public void Builder_BuildString_ForcePixelFormat() { - var str = FFMpegArguments.FromInputFiles(true, "input.mp4").ForcePixelFormat("yuv444p").OutputToFile("output.mp4", false).Arguments; + var str = FFMpegArguments.FromFileInput("input.mp4").OutputToFile("output.mp4", false, opt => opt.ForcePixelFormat("yuv444p")).Arguments; Assert.AreEqual("-i \"input.mp4\" -pix_fmt yuv444p \"output.mp4\"", str); } } diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index e51fdea..0b40321 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -63,51 +63,51 @@ public bool Convert(ContainerFormat type, bool multithreaded = false, VideoSize } } - private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] inputArguments) + private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] arguments) { var output = Input.OutputLocation(type); try { var input = FFProbe.Analyse(VideoLibrary.LocalVideoWebm.FullName); - using (var inputStream = File.OpenRead(input.Path)) + using var inputStream = File.OpenRead(input.Path); + var processor = FFMpegArguments + .FromPipeInput(new StreamPipeSource(inputStream)) + .OutputToFile(output, false, opt => + { + foreach (var arg in arguments) + opt.WithArgument(arg); + }); + + var scaling = arguments.OfType().FirstOrDefault(); + + var success = processor.ProcessSynchronously(); + + var outputVideo = FFProbe.Analyse(output); + + Assert.IsTrue(success); + Assert.IsTrue(File.Exists(output)); + Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate); + + if (scaling?.Size == null) { - var pipeSource = new StreamPipeSource(inputStream); - var arguments = FFMpegArguments.FromPipe(pipeSource); - foreach (var arg in inputArguments) - arguments.WithArgument(arg); - var processor = arguments.OutputToFile(output); - - var scaling = arguments.Find(); - - var success = processor.ProcessSynchronously(); - - var outputVideo = FFProbe.Analyse(output); - - Assert.IsTrue(success); - Assert.IsTrue(File.Exists(output)); - Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate); - - if (scaling?.Size == null) + Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width); + Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height); + } + else + { + if (scaling.Size.Value.Width != -1) { - Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width); - Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height); + Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width); } - else + + if (scaling.Size.Value.Height != -1) { - if (scaling.Size.Value.Width != -1) - { - Assert.AreEqual(outputVideo.PrimaryVideoStream.Width, scaling.Size.Value.Width); - } - - if (scaling.Size.Value.Height != -1) - { - Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height); - } - - Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width); - Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height); + Assert.AreEqual(outputVideo.PrimaryVideoStream.Height, scaling.Size.Value.Height); } + + Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Width, input.PrimaryVideoStream.Width); + Assert.AreNotEqual(outputVideo.PrimaryVideoStream.Height, input.PrimaryVideoStream.Height); } } finally @@ -117,17 +117,18 @@ private void ConvertFromStreamPipe(ContainerFormat type, params IArgument[] inpu } } - private void ConvertToStreamPipe(params IArgument[] inputArguments) + private void ConvertToStreamPipe(params IArgument[] arguments) { using var ms = new MemoryStream(); - var arguments = FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo); - foreach (var arg in inputArguments) - arguments.WithArgument(arg); + var processor = FFMpegArguments + .FromFileInput(VideoLibrary.LocalVideo) + .OutputToPipe(new StreamPipeSink(ms), opt => + { + foreach (var arg in arguments) + opt.WithArgument(arg); + }); - var streamPipeDataReader = new StreamPipeSink(ms); - var processor = arguments.OutputToPipe(streamPipeDataReader); - - var scaling = arguments.Find(); + var scaling = arguments.OfType().FirstOrDefault(); processor.ProcessSynchronously(); @@ -159,7 +160,7 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments) } } - public void Convert(ContainerFormat type, Action validationMethod, params IArgument[] inputArguments) + public void Convert(ContainerFormat type, Action validationMethod, params IArgument[] arguments) { var output = Input.OutputLocation(type); @@ -167,13 +168,15 @@ public void Convert(ContainerFormat type, Action validationMetho { var input = FFProbe.Analyse(Input.FullName); - var arguments = FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo.FullName); - foreach (var arg in inputArguments) - arguments.WithArgument(arg); + var processor = FFMpegArguments + .FromFileInput(VideoLibrary.LocalVideo) + .OutputToFile(output, false, opt => + { + foreach (var arg in arguments) + opt.WithArgument(arg); + }); - var processor = arguments.OutputToFile(output); - - var scaling = arguments.Find(); + var scaling = arguments.OfType().FirstOrDefault(); processor.ProcessSynchronously(); var outputVideo = FFProbe.Analyse(output); @@ -214,19 +217,19 @@ public void Convert(ContainerFormat type, params IArgument[] inputArguments) Convert(type, null, inputArguments); } - public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] inputArguments) + public void ConvertFromPipe(ContainerFormat type, System.Drawing.Imaging.PixelFormat fmt, params IArgument[] arguments) { var output = Input.OutputLocation(type); try { var videoFramesSource = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, fmt, 256, 256)); - var arguments = FFMpegArguments.FromPipe(videoFramesSource); - foreach (var arg in inputArguments) - arguments.WithArgument(arg); - var processor = arguments.OutputToFile(output); - - var scaling = arguments.Find(); + var processor = FFMpegArguments.FromPipeInput(videoFramesSource).OutputToFile(output, false, opt => + { + foreach (var arg in arguments) + opt.WithArgument(arg); + }); + var scaling = arguments.OfType().FirstOrDefault(); processor.ProcessSynchronously(); var outputVideo = FFProbe.Analyse(output); @@ -304,9 +307,8 @@ await Assert.ThrowsExceptionAsync(async () => await using var ms = new MemoryStream(); var pipeSource = new StreamPipeSink(ms); await FFMpegArguments - .FromInputFiles(VideoLibrary.LocalVideo) - .ForceFormat("mkv") - .OutputToPipe(pipeSource) + .FromFileInput(VideoLibrary.LocalVideo) + .OutputToPipe(pipeSource, opt => opt.ForceFormat("mkv")) .ProcessAsynchronously(); }); } @@ -324,10 +326,10 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async() using var ms = new MemoryStream(); var pipeSource = new StreamPipeSink(ms); FFMpegArguments - .FromInputFiles(VideoLibrary.LocalVideo) - .WithVideoCodec(VideoCodec.LibX264) - .ForceFormat("matroska") - .OutputToPipe(pipeSource) + .FromFileInput(VideoLibrary.LocalVideo) + .OutputToPipe(pipeSource, opt => opt + .WithVideoCodec(VideoCodec.LibX264) + .ForceFormat("matroska")) .ProcessAsynchronously() .WaitForResult(); } @@ -335,12 +337,12 @@ public void Video_ToMP4_Args_StreamOutputPipe_Async() [TestMethod] public async Task TestDuplicateRun() { - FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo) - .OutputToFile("temporary.mp4", true) + FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo) + .OutputToFile("temporary.mp4") .ProcessSynchronously(); - await FFMpegArguments.FromInputFiles(VideoLibrary.LocalVideo) - .OutputToFile("temporary.mp4", true) + await FFMpegArguments.FromFileInput(VideoLibrary.LocalVideo) + .OutputToFile("temporary.mp4") .ProcessAsynchronously(); File.Delete("temporary.mp4"); @@ -577,9 +579,8 @@ public void Video_Duration() try { FFMpegArguments - .FromInputFiles(VideoLibrary.LocalVideo) - .WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 5)) - .OutputToFile(output) + .FromFileInput(VideoLibrary.LocalVideo) + .OutputToFile(output, false, opt => opt.WithDuration(TimeSpan.FromSeconds(video.Duration.TotalSeconds - 5))) .ProcessSynchronously(); Assert.IsTrue(File.Exists(output)); @@ -613,8 +614,8 @@ public void Video_UpdatesProgress() try { var success = FFMpegArguments - .FromInputFiles(VideoLibrary.LocalVideo) - .WithDuration(TimeSpan.FromSeconds(8)) + .FromFileInput(VideoLibrary.LocalVideo, opt => opt + .WithDuration(TimeSpan.FromSeconds(8))) .OutputToFile(output) .NotifyOnProgress(OnPercentageProgess, analysis.Duration) .NotifyOnProgress(OnTimeProgess) @@ -640,10 +641,10 @@ public void Video_TranscodeInMemory() var writer = new RawVideoPipeSource(BitmapSource.CreateBitmaps(128, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128)); FFMpegArguments - .FromPipe(writer) - .WithVideoCodec("vp9") - .ForceFormat("webm") - .OutputToPipe(reader) + .FromPipeInput(writer) + .OutputToPipe(reader, opt => opt + .WithVideoCodec("vp9") + .ForceFormat("webm")) .ProcessSynchronously(); resStream.Position = 0; @@ -660,10 +661,10 @@ public async Task Video_Cancel_Async() var writer = new RawVideoPipeSource(BitmapSource.CreateBitmaps(512, System.Drawing.Imaging.PixelFormat.Format24bppRgb, 128, 128)); var task = FFMpegArguments - .FromPipe(writer) - .WithVideoCodec("vp9") - .ForceFormat("webm") - .OutputToPipe(reader) + .FromPipeInput(writer) + .OutputToPipe(reader, opt => opt + .WithVideoCodec("vp9") + .ForceFormat("webm")) .CancellableThrough(out var cancel) .ProcessAsynchronously(false); diff --git a/FFMpegCore/FFMpeg/Arguments/CpuSpeedArgument.cs b/FFMpegCore/FFMpeg/Arguments/CpuSpeedArgument.cs deleted file mode 100644 index 228262e..0000000 --- a/FFMpegCore/FFMpeg/Arguments/CpuSpeedArgument.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace FFMpegCore.Arguments -{ - /// - /// Represents cpu speed parameter - /// - public class CpuSpeedArgument : IArgument - { - public readonly int CpuSpeed; - public CpuSpeedArgument(int cpuSpeed) - { - CpuSpeed = cpuSpeed; - } - - public string Text => $"-quality good -cpu-used {CpuSpeed} -deadline realtime"; - } -} diff --git a/FFMpegCore/FFMpeg/Arguments/InputArgument.cs b/FFMpegCore/FFMpeg/Arguments/InputArgument.cs index 0ff4bd8..0ec3cd9 100644 --- a/FFMpegCore/FFMpeg/Arguments/InputArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/InputArgument.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -12,33 +11,25 @@ namespace FFMpegCore.Arguments public class InputArgument : IInputArgument { public readonly bool VerifyExists; - public readonly string[] FilePaths; + public readonly string FilePath; - public InputArgument(bool verifyExists, params string[] filePaths) + public InputArgument(bool verifyExists, string filePaths) { VerifyExists = verifyExists; - FilePaths = filePaths; + FilePath = filePaths; } - public InputArgument(params string[] filePaths) : this(true, filePaths) { } - public InputArgument(params FileInfo[] fileInfos) : this(false, fileInfos) { } - public InputArgument(params Uri[] uris) : this(false, uris) { } - public InputArgument(bool verifyExists, params FileInfo[] fileInfos) : this(verifyExists, fileInfos.Select(v => v.FullName).ToArray()) { } - public InputArgument(bool verifyExists, params Uri[] uris) : this(verifyExists, uris.Select(v => v.AbsoluteUri).ToArray()) { } + public InputArgument(string path, bool verifyExists) : this(verifyExists, path) { } public void Pre() { - if (!VerifyExists) return; - foreach (var filePath in FilePaths) - { - if (!File.Exists(filePath)) - throw new FileNotFoundException("Input file not found", filePath); - } + if (VerifyExists && !File.Exists(FilePath)) + throw new FileNotFoundException("Input file not found", FilePath); } public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask; public void Post() { } - public string Text => string.Join(" ", FilePaths.Select(v => $"-i \"{v}\"")); + public string Text => $"-i \"{FilePath}\""; } } diff --git a/FFMpegCore/FFMpeg/Arguments/OutputArgument.cs b/FFMpegCore/FFMpeg/Arguments/OutputArgument.cs index 64469f8..ec9db7c 100644 --- a/FFMpegCore/FFMpeg/Arguments/OutputArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/OutputArgument.cs @@ -28,8 +28,6 @@ public void Pre() public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask; public void Post() { - if (!File.Exists(Path)) - throw new FFMpegException(FFMpegExceptionType.File, "Output file was not created"); } public OutputArgument(FileInfo value) : this(value.FullName) { } diff --git a/FFMpegCore/FFMpeg/Arguments/SeekedFileInputArgument.cs b/FFMpegCore/FFMpeg/Arguments/SeekedFileInputArgument.cs deleted file mode 100644 index 4a29116..0000000 --- a/FFMpegCore/FFMpeg/Arguments/SeekedFileInputArgument.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace FFMpegCore.Arguments -{ - public class SeekedFileInputArgument : IInputArgument - { - public readonly (string FilePath, TimeSpan StartTime)[] SeekedFiles; - - public SeekedFileInputArgument((string file, TimeSpan startTime)[] seekedFiles) - { - SeekedFiles = seekedFiles; - } - - public void Pre() - { - foreach (var (seekedFile, _) in SeekedFiles) - { - if (!File.Exists(seekedFile)) - throw new FileNotFoundException("Input file not found", seekedFile); - } - } - public Task During(CancellationToken? cancellationToken = null) => Task.CompletedTask; - public void Post() { } - - public string Text => string.Join(" ", SeekedFiles.Select(seekedFile => $"-ss {seekedFile.StartTime} -i \"{seekedFile.FilePath}\"")); - } -} \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 7aad74f..602f03a 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -26,10 +26,10 @@ public static bool Snapshot(IMediaAnalysis source, string output, Size? size = n if (Path.GetExtension(output) != FileExtension.Png) output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; - var arguments = BuildSnapshotArguments(source, size, captureTime); + var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime); return arguments - .OutputToFile(output) + .OutputToFile(output, true, outputOptions) .ProcessSynchronously(); } /// @@ -45,10 +45,10 @@ public static Task SnapshotAsync(IMediaAnalysis source, string output, Siz if (Path.GetExtension(output) != FileExtension.Png) output = Path.GetFileNameWithoutExtension(output) + FileExtension.Png; - var arguments = BuildSnapshotArguments(source, size, captureTime); + var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime); return arguments - .OutputToFile(output) + .OutputToFile(output, true, outputOptions) .ProcessAsynchronously(); } /// @@ -60,12 +60,12 @@ public static Task SnapshotAsync(IMediaAnalysis source, string output, Siz /// Bitmap with the requested snapshot. public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) { - var arguments = BuildSnapshotArguments(source, size, captureTime); + var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime); using var ms = new MemoryStream(); arguments - .ForceFormat("rawvideo") - .OutputToPipe(new StreamPipeSink(ms)) + .OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options + .ForceFormat("rawvideo"))) .ProcessSynchronously(); ms.Position = 0; @@ -80,28 +80,30 @@ public static Bitmap Snapshot(IMediaAnalysis source, Size? size = null, TimeSpan /// Bitmap with the requested snapshot. public static async Task SnapshotAsync(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) { - var arguments = BuildSnapshotArguments(source, size, captureTime); + var (arguments, outputOptions) = BuildSnapshotArguments(source, size, captureTime); using var ms = new MemoryStream(); await arguments - .ForceFormat("rawvideo") - .OutputToPipe(new StreamPipeSink(ms)) + .OutputToPipe(new StreamPipeSink(ms), options => outputOptions(options + .ForceFormat("rawvideo"))) .ProcessAsynchronously(); ms.Position = 0; return new Bitmap(ms); } - private static FFMpegArguments BuildSnapshotArguments(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) + private static (FFMpegArguments, Action outputOptions) BuildSnapshotArguments(IMediaAnalysis source, Size? size = null, TimeSpan? captureTime = null) { captureTime ??= TimeSpan.FromSeconds(source.Duration.TotalSeconds / 3); size = PrepareSnapshotSize(source, size); - return FFMpegArguments - .FromSeekedFiles((source.Path, captureTime ?? TimeSpan.Zero)) - .WithVideoCodec(VideoCodec.Png) - .WithFrameOutputCount(1) - .Resize(size); + return (FFMpegArguments + .FromFileInput(source, options => options + .Seek(captureTime)), + options => options + .WithVideoCodec(VideoCodec.Png) + .WithFrameOutputCount(1) + .Resize(size)); } private static Size? PrepareSnapshotSize(IMediaAnalysis source, Size? wantedSize) @@ -163,44 +165,44 @@ public static bool Convert( return format.Name switch { "mp4" => FFMpegArguments - .FromInputFiles(true, source.Path) - .UsingMultithreading(multithreaded) - .WithVideoCodec(VideoCodec.LibX264) - .WithVideoBitrate(2400) - .Scale(outputSize) - .WithSpeedPreset(speed) - .WithAudioCodec(AudioCodec.Aac) - .WithAudioBitrate(audioQuality) - .OutputToFile(output) + .FromFileInput(source) + .OutputToFile(output, true, options => options + .UsingMultithreading(multithreaded) + .WithVideoCodec(VideoCodec.LibX264) + .WithVideoBitrate(2400) + .Scale(outputSize) + .WithSpeedPreset(speed) + .WithAudioCodec(AudioCodec.Aac) + .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), "ogv" => FFMpegArguments - .FromInputFiles(true, source.Path) - .UsingMultithreading(multithreaded) - .WithVideoCodec(VideoCodec.LibTheora) - .WithVideoBitrate(2400) - .Scale(outputSize) - .WithSpeedPreset(speed) - .WithAudioCodec(AudioCodec.LibVorbis) - .WithAudioBitrate(audioQuality) - .OutputToFile(output) + .FromFileInput(source) + .OutputToFile(output, true, options => options + .UsingMultithreading(multithreaded) + .WithVideoCodec(VideoCodec.LibTheora) + .WithVideoBitrate(2400) + .Scale(outputSize) + .WithSpeedPreset(speed) + .WithAudioCodec(AudioCodec.LibVorbis) + .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), "mpegts" => FFMpegArguments - .FromInputFiles(true, source.Path) - .CopyChannel() - .WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB) - .ForceFormat(VideoType.Ts) - .OutputToFile(output) + .FromFileInput(source) + .OutputToFile(output, true, options => options + .CopyChannel() + .WithBitStreamFilter(Channel.Video, Filter.H264_Mp4ToAnnexB) + .ForceFormat(VideoType.Ts)) .ProcessSynchronously(), "webm" => FFMpegArguments - .FromInputFiles(true, source.Path) - .UsingMultithreading(multithreaded) - .WithVideoCodec(VideoCodec.LibVpx) - .WithVideoBitrate(2400) - .Scale(outputSize) - .WithSpeedPreset(speed) - .WithAudioCodec(AudioCodec.LibVorbis) - .WithAudioBitrate(audioQuality) - .OutputToFile(output) + .FromFileInput(source) + .OutputToFile(output, true, options => options + .UsingMultithreading(multithreaded) + .WithVideoCodec(VideoCodec.LibVpx) + .WithVideoBitrate(2400) + .Scale(outputSize) + .WithSpeedPreset(speed) + .WithAudioCodec(AudioCodec.LibVorbis) + .WithAudioBitrate(audioQuality)) .ProcessSynchronously(), _ => throw new ArgumentOutOfRangeException(nameof(format)) }; @@ -219,13 +221,14 @@ public static bool PosterWithAudio(string image, string audio, string output) FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image)); return FFMpegArguments - .FromInputFiles(true, image, audio) - .Loop(1) - .WithVideoCodec(VideoCodec.LibX264) - .WithConstantRateFactor(21) - .WithAudioBitrate(AudioQuality.Normal) - .UsingShortest() - .OutputToFile(output) + .FromFileInput(image) + .AddFileInput(audio) + .OutputToFile(output, true, options => options + .Loop(1) + .WithVideoCodec(VideoCodec.LibX264) + .WithConstantRateFactor(21) + .WithAudioBitrate(AudioQuality.Normal) + .UsingShortest()) .ProcessSynchronously(); } @@ -249,10 +252,10 @@ public static bool Join(string output, params IMediaAnalysis[] videos) try { return FFMpegArguments - .FromConcatenation(temporaryVideoParts) - .CopyChannel() - .WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc) - .OutputToFile(output) + .FromConcatInput(temporaryVideoParts) + .OutputToFile(output, true, options => options + .CopyChannel() + .WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc)) .ProcessSynchronously(); } finally @@ -294,10 +297,10 @@ public static bool JoinImageSequence(string output, double frameRate = 30, param try { return FFMpegArguments - .FromInputFiles(false, Path.Combine(tempFolderName, "%09d.png")) - .Resize(firstImage.Width, firstImage.Height) - .WithFramerate(frameRate) - .OutputToFile(output) + .FromFileInput(Path.Combine(tempFolderName, "%09d.png"), false) + .OutputToFile(output, true, options => options + .Resize(firstImage.Width, firstImage.Height) + .WithFramerate(frameRate)) .ProcessSynchronously(); } finally @@ -321,7 +324,7 @@ public static bool SaveM3U8Stream(Uri uri, string output) throw new ArgumentException($"Uri: {uri.AbsoluteUri}, does not point to a valid http(s) stream."); return FFMpegArguments - .FromInputFiles(false, uri) + .FromUrlInput(uri) .OutputToFile(output) .ProcessSynchronously(); } @@ -339,10 +342,10 @@ public static bool Mute(string input, string output) FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); return FFMpegArguments - .FromInputFiles(true, source.Path) - .CopyChannel(Channel.Video) - .DisableChannel(Channel.Audio) - .OutputToFile(output) + .FromFileInput(source) + .OutputToFile(output, true, options => options + .CopyChannel(Channel.Video) + .DisableChannel(Channel.Audio)) .ProcessSynchronously(); } @@ -357,9 +360,9 @@ public static bool ExtractAudio(string input, string output) FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.Mp3); return FFMpegArguments - .FromInputFiles(true, input) - .DisableChannel(Channel.Video) - .OutputToFile(output) + .FromFileInput(input) + .OutputToFile(output, true, options => options + .DisableChannel(Channel.Video)) .ProcessSynchronously(); } @@ -378,26 +381,27 @@ public static bool ReplaceAudio(string input, string inputAudio, string output, FFMpegHelper.ExtensionExceptionCheck(output, source.Extension); return FFMpegArguments - .FromInputFiles(true, source.Path, inputAudio) - .CopyChannel() - .WithAudioCodec(AudioCodec.Aac) - .WithAudioBitrate(AudioQuality.Good) - .UsingShortest(stopAtShortest) - .OutputToFile(output) + .FromFileInput(source) + .AddFileInput(inputAudio) + .OutputToFile(output, true, options => options + .CopyChannel() + .WithAudioCodec(AudioCodec.Aac) + .WithAudioBitrate(AudioQuality.Good) + .UsingShortest(stopAtShortest)) .ProcessSynchronously(); } #region PixelFormats - internal static IReadOnlyList GetPixelFormatsInternal() + internal static IReadOnlyList GetPixelFormatsInternal() { - FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); + FFMpegHelper.RootExceptionCheck(); - var list = new List(); + var list = new List(); using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-pix_fmts"); instance.DataReceived += (e, args) => { - if (Enums.PixelFormat.TryParse(args.Data, out var fmt)) - list.Add(fmt); + if (PixelFormat.TryParse(args.Data, out var format)) + list.Add(format); }; var exitCode = instance.BlockUntilFinished(); @@ -406,14 +410,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 Enums.PixelFormat fmt) + public static bool TryGetPixelFormat(string name, out PixelFormat fmt) { if (!FFMpegOptions.Options.UseCache) { @@ -424,7 +428,7 @@ public static bool TryGetPixelFormat(string name, out Enums.PixelFormat fmt) return FFMpegCache.PixelFormats.TryGetValue(name, out fmt); } - public static Enums.PixelFormat GetPixelFormat(string name) + public static PixelFormat GetPixelFormat(string name) { if (TryGetPixelFormat(name, out var fmt)) return fmt; @@ -433,9 +437,10 @@ public static Enums.PixelFormat GetPixelFormat(string name) #endregion #region Codecs - internal static void ParsePartOfCodecs(Dictionary codecs, string arguments, Func parser) + + private static void ParsePartOfCodecs(Dictionary codecs, string arguments, Func parser) { - FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); + FFMpegHelper.RootExceptionCheck(); using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), arguments); instance.DataReceived += (e, args) => @@ -518,7 +523,7 @@ public static Codec GetCodec(string name) #region ContainerFormats internal static IReadOnlyList GetContainersFormatsInternal() { - FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); + FFMpegHelper.RootExceptionCheck(); var list = new List(); using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-formats"); diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs b/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs new file mode 100644 index 0000000..a50d370 --- /dev/null +++ b/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs @@ -0,0 +1,66 @@ +using System; +using System.Drawing; +using FFMpegCore.Arguments; +using FFMpegCore.Enums; + +namespace FFMpegCore +{ + public class FFMpegArgumentOptions : FFMpegOptionsBase + { + internal FFMpegArgumentOptions() { } + + public FFMpegArgumentOptions WithAudioCodec(Codec audioCodec) => WithArgument(new AudioCodecArgument(audioCodec)); + public FFMpegArgumentOptions WithAudioCodec(string audioCodec) => WithArgument(new AudioCodecArgument(audioCodec)); + public FFMpegArgumentOptions WithAudioBitrate(AudioQuality audioQuality) => WithArgument(new AudioBitrateArgument(audioQuality)); + 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)); + + public FFMpegArgumentOptions Scale(VideoSize videoSize) => WithArgument(new ScaleArgument(videoSize)); + public FFMpegArgumentOptions Scale(int width, int height) => WithArgument(new ScaleArgument(width, height)); + public FFMpegArgumentOptions Scale(Size size) => WithArgument(new ScaleArgument(size)); + + public FFMpegArgumentOptions WithBitStreamFilter(Channel channel, Filter filter) => WithArgument(new BitStreamFilterArgument(channel, filter)); + public FFMpegArgumentOptions WithConstantRateFactor(int crf) => WithArgument(new ConstantRateFactorArgument(crf)); + public FFMpegArgumentOptions CopyChannel(Channel channel = Channel.Both) => WithArgument(new CopyArgument(channel)); + public FFMpegArgumentOptions DisableChannel(Channel channel) => WithArgument(new DisableChannelArgument(channel)); + public FFMpegArgumentOptions WithDuration(TimeSpan? duration) => WithArgument(new DurationArgument(duration)); + public FFMpegArgumentOptions WithFastStart() => WithArgument(new FaststartArgument()); + public FFMpegArgumentOptions WithFrameOutputCount(int frames) => WithArgument(new FrameOutputCountArgument(frames)); + + public FFMpegArgumentOptions UsingShortest(bool shortest = true) => WithArgument(new ShortestArgument(shortest)); + public FFMpegArgumentOptions UsingMultithreading(bool multithread) => WithArgument(new ThreadsArgument(multithread)); + public FFMpegArgumentOptions UsingThreads(int threads) => WithArgument(new ThreadsArgument(threads)); + + public FFMpegArgumentOptions WithVideoCodec(Codec videoCodec) => WithArgument(new VideoCodecArgument(videoCodec)); + public FFMpegArgumentOptions WithVideoCodec(string videoCodec) => WithArgument(new VideoCodecArgument(videoCodec)); + public FFMpegArgumentOptions WithVideoBitrate(int bitrate) => WithArgument(new VideoBitrateArgument(bitrate)); + public FFMpegArgumentOptions WithFramerate(double framerate) => WithArgument(new FrameRateArgument(framerate)); + public FFMpegArgumentOptions WithoutMetadata() => WithArgument(new RemoveMetadataArgument()); + public FFMpegArgumentOptions WithSpeedPreset(Speed speed) => WithArgument(new SpeedPresetArgument(speed)); + public FFMpegArgumentOptions WithStartNumber(int startNumber) => WithArgument(new StartNumberArgument(startNumber)); + public FFMpegArgumentOptions WithCustomArgument(string argument) => WithArgument(new CustomArgument(argument)); + + public FFMpegArgumentOptions Seek(TimeSpan? seekTo) => WithArgument(new SeekArgument(seekTo)); + public FFMpegArgumentOptions Transpose(Transposition transposition) => WithArgument(new TransposeArgument(transposition)); + public FFMpegArgumentOptions Loop(int times) => WithArgument(new LoopArgument(times)); + public FFMpegArgumentOptions OverwriteExisting() => WithArgument(new OverwriteArgument()); + + public FFMpegArgumentOptions ForceFormat(ContainerFormat format) => WithArgument(new ForceFormatArgument(format)); + public FFMpegArgumentOptions ForceFormat(string format) => WithArgument(new ForceFormatArgument(format)); + public FFMpegArgumentOptions ForcePixelFormat(string pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat)); + public FFMpegArgumentOptions ForcePixelFormat(PixelFormat pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat)); + + public FFMpegArgumentOptions DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions)); + + public FFMpegArgumentOptions WithArgument(IArgument argument) + { + Arguments.Add(argument); + return this; + } + } +} \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs index e931eaf..ebfac20 100644 --- a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs +++ b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs @@ -125,7 +125,7 @@ await Task.WhenAll(instance.FinishedRunning().ContinueWith(t => private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSource) { - FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); + FFMpegHelper.RootExceptionCheck(); FFMpegHelper.VerifyFFMpegExists(); var instance = new Instance(FFMpegOptions.Options.FFmpegBinary(), _ffMpegArguments.Text); instance.DataReceived += OutputData; @@ -136,6 +136,7 @@ private Instance PrepareInstance(out CancellationTokenSource cancellationTokenSo return instance; } + private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList errorData) { diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs index b3285f6..b2cffd1 100644 --- a/FFMpegCore/FFMpeg/FFMpegArguments.cs +++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs @@ -1,126 +1,81 @@ using System; using System.Collections.Generic; -using System.Drawing; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using FFMpegCore.Arguments; -using FFMpegCore.Enums; using FFMpegCore.Pipes; namespace FFMpegCore { - public class FFMpegArguments + public sealed class FFMpegArguments : FFMpegOptionsBase { - private readonly IInputArgument _inputArgument; - private IOutputArgument _outputArgument = null!; - private readonly List _arguments; + private readonly FFMpegGlobalOptions _globalOptions = new FFMpegGlobalOptions(); - private FFMpegArguments(IInputArgument inputArgument) + private FFMpegArguments() { } + + public string Text => string.Join(" ", _globalOptions.Arguments.Concat(Arguments).Select(arg => arg.Text)); + + public static FFMpegArguments FromConcatInput(IEnumerable filePaths, Action? addArguments = null) => new FFMpegArguments().WithInput(new ConcatArgument(filePaths), addArguments); + public static FFMpegArguments FromDemuxConcatInput(IEnumerable filePaths, Action? addArguments = null) => new FFMpegArguments().WithInput(new DemuxConcatArgument(filePaths), addArguments); + public static FFMpegArguments FromFileInput(string filePath, bool verifyExists = true, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(verifyExists, filePath), addArguments); + public static FFMpegArguments FromFileInput(FileInfo fileInfo, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(fileInfo.FullName, false), addArguments); + public static FFMpegArguments FromFileInput(IMediaAnalysis mediaAnalysis, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments); + public static FFMpegArguments FromUrlInput(Uri uri, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments); + public static FFMpegArguments FromPipeInput(IPipeSource sourcePipe, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputPipeArgument(sourcePipe), addArguments); + + + public FFMpegArguments WithGlobalOptions(Action configureOptions) { - _inputArgument = inputArgument; - _arguments = new List { inputArgument }; - } - - public string Text => string.Join(" ", _arguments.Select(arg => arg.Text)); - - public static FFMpegArguments FromSeekedFiles(params (string file, TimeSpan startTime)[] seekedFiles) => new FFMpegArguments(new SeekedFileInputArgument(seekedFiles)); - public static FFMpegArguments FromInputFiles(params string[] files) => new FFMpegArguments(new InputArgument(true, files)); - public static FFMpegArguments FromInputFiles(bool verifyExists, params string[] files) => new FFMpegArguments(new InputArgument(verifyExists, files)); - public static FFMpegArguments FromInputFiles(params Uri[] uris) => new FFMpegArguments(new InputArgument(false, uris)); - public static FFMpegArguments FromInputFiles(bool verifyExists, params Uri[] uris) => new FFMpegArguments(new InputArgument(verifyExists, uris)); - public static FFMpegArguments FromInputFiles(params FileInfo[] files) => new FFMpegArguments(new InputArgument(false, files)); - public static FFMpegArguments FromInputFiles(bool verifyExists, params FileInfo[] files) => new FFMpegArguments(new InputArgument(verifyExists, files)); - public static FFMpegArguments FromConcatenation(params string[] files) => new FFMpegArguments(new ConcatArgument(files)); - public static FFMpegArguments FromDemuxConcatenation(params string[] files) => new FFMpegArguments(new DemuxConcatArgument(files)); - public static FFMpegArguments FromPipe(IPipeSource writer) => new FFMpegArguments(new InputPipeArgument(writer)); - - - 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)); - public FFMpegArguments WithVariableBitrate(int vbr) => WithArgument(new VariableBitRateArgument(vbr)); - - public FFMpegArguments Resize(VideoSize videoSize) => WithArgument(new SizeArgument(videoSize)); - public FFMpegArguments Resize(int width, int height) => WithArgument(new SizeArgument(width, height)); - public FFMpegArguments Resize(Size? size) => WithArgument(new SizeArgument(size)); - - public FFMpegArguments Scale(VideoSize videoSize) => WithArgument(new ScaleArgument(videoSize)); - public FFMpegArguments Scale(int width, int height) => WithArgument(new ScaleArgument(width, height)); - public FFMpegArguments Scale(Size size) => WithArgument(new ScaleArgument(size)); - - public FFMpegArguments WithBitStreamFilter(Channel channel, Filter filter) => WithArgument(new BitStreamFilterArgument(channel, filter)); - public FFMpegArguments WithConstantRateFactor(int crf) => WithArgument(new ConstantRateFactorArgument(crf)); - public FFMpegArguments CopyChannel(Channel channel = Channel.Both) => WithArgument(new CopyArgument(channel)); - public FFMpegArguments DisableChannel(Channel channel) => WithArgument(new DisableChannelArgument(channel)); - public FFMpegArguments WithDuration(TimeSpan? duration) => WithArgument(new DurationArgument(duration)); - public FFMpegArguments WithFastStart() => WithArgument(new FaststartArgument()); - public FFMpegArguments WithFrameOutputCount(int frames) => WithArgument(new FrameOutputCountArgument(frames)); - - public FFMpegArguments UsingShortest(bool shortest = true) => WithArgument(new ShortestArgument(shortest)); - public FFMpegArguments UsingMultithreading(bool multithread) => WithArgument(new ThreadsArgument(multithread)); - public FFMpegArguments UsingThreads(int threads) => WithArgument(new ThreadsArgument(threads)); - - 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)); - public FFMpegArguments WithoutMetadata() => WithArgument(new RemoveMetadataArgument()); - public FFMpegArguments WithSpeedPreset(Speed speed) => WithArgument(new SpeedPresetArgument(speed)); - public FFMpegArguments WithStartNumber(int startNumber) => WithArgument(new StartNumberArgument(startNumber)); - public FFMpegArguments WithCpuSpeed(int cpuSpeed) => WithArgument(new CpuSpeedArgument(cpuSpeed)); - public FFMpegArguments WithCustomArgument(string argument) => WithArgument(new CustomArgument(argument)); - - public FFMpegArguments Seek(TimeSpan? seekTo) => WithArgument(new SeekArgument(seekTo)); - public FFMpegArguments Transpose(Transposition transposition) => WithArgument(new TransposeArgument(transposition)); - public FFMpegArguments Loop(int times) => WithArgument(new LoopArgument(times)); - public FFMpegArguments OverwriteExisting() => WithArgument(new OverwriteArgument()); - public FFMpegArguments WithVerbosityLevel(VerbosityLevel verbosityLevel = VerbosityLevel.Error) => WithArgument(new VerbosityLevelArgument(verbosityLevel)); - - 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 = true) => ToProcessor(new OutputArgument(file, overwrite)); - public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = true) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite)); - public FFMpegArgumentProcessor OutputToPipe(IPipeSink reader) => ToProcessor(new OutputPipeArgument(reader)); - - public FFMpegArguments WithArgument(IArgument argument) - { - _arguments.Add(argument); + configureOptions(_globalOptions); return this; } - private FFMpegArgumentProcessor ToProcessor(IOutputArgument argument) + + public FFMpegArguments AddConcatInput(IEnumerable filePaths, Action? addArguments = null) => WithInput(new ConcatArgument(filePaths), addArguments); + public FFMpegArguments AddDemuxConcatInput(IEnumerable filePaths, Action? addArguments = null) => WithInput(new DemuxConcatArgument(filePaths), addArguments); + public FFMpegArguments AddFileInput(string filePath, bool verifyExists = true, Action? addArguments = null) => WithInput(new InputArgument(verifyExists, filePath), addArguments); + public FFMpegArguments AddFileInput(FileInfo fileInfo, Action? addArguments = null) => WithInput(new InputArgument(fileInfo.FullName, false), addArguments); + public FFMpegArguments AddFileInput(IMediaAnalysis mediaAnalysis, Action? addArguments = null) => WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments); + public FFMpegArguments AddUrlInput(Uri uri, Action? addArguments = null) => WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments); + public FFMpegArguments AddPipeInput(IPipeSource sourcePipe, Action? addArguments = null) => WithInput(new InputPipeArgument(sourcePipe), addArguments); + + private FFMpegArguments WithInput(IInputArgument inputArgument, Action? addArguments) { - _arguments.Add(argument); - _outputArgument = argument; + var arguments = new FFMpegArgumentOptions(); + addArguments?.Invoke(arguments); + Arguments.AddRange(arguments.Arguments); + Arguments.Add(inputArgument); + return this; + } + + public FFMpegArgumentProcessor OutputToFile(string file, bool overwrite = true, Action? addArguments = null) => ToProcessor(new OutputArgument(file, overwrite), addArguments); + public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = true, Action? addArguments = null) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite), addArguments); + public FFMpegArgumentProcessor OutputToPipe(IPipeSink reader, Action? addArguments = null) => ToProcessor(new OutputPipeArgument(reader), addArguments); + + private FFMpegArgumentProcessor ToProcessor(IOutputArgument argument, Action? addArguments) + { + var args = new FFMpegArgumentOptions(); + addArguments?.Invoke(args); + Arguments.AddRange(args.Arguments); + Arguments.Add(argument); return new FFMpegArgumentProcessor(this); } internal void Pre() { - _inputArgument.Pre(); - _outputArgument.Pre(); + foreach (var argument in Arguments.OfType()) + argument.Pre(); } internal async Task During(CancellationToken? cancellationToken = null) { - await Task.WhenAll(_inputArgument.During(cancellationToken), _outputArgument.During(cancellationToken)).ConfigureAwait(false); + var inputOutputArguments = Arguments.OfType(); + await Task.WhenAll(inputOutputArguments.Select(io => io.During(cancellationToken))).ConfigureAwait(false); } internal void Post() { - _inputArgument.Post(); - _outputArgument.Post(); - } - - public TArgument Find() where TArgument : class, IArgument - { - return _arguments.OfType().FirstOrDefault(); + foreach (var argument in Arguments.OfType()) + argument.Post(); } } } \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/FFMpegGlobalOptions.cs b/FFMpegCore/FFMpeg/FFMpegGlobalOptions.cs new file mode 100644 index 0000000..afac5cf --- /dev/null +++ b/FFMpegCore/FFMpeg/FFMpegGlobalOptions.cs @@ -0,0 +1,18 @@ +using FFMpegCore.Arguments; + +namespace FFMpegCore +{ + public sealed class FFMpegGlobalOptions : FFMpegOptionsBase + { + internal FFMpegGlobalOptions() { } + + public FFMpegGlobalOptions WithVerbosityLevel(VerbosityLevel verbosityLevel = VerbosityLevel.Error) => WithOption(new VerbosityLevelArgument(verbosityLevel)); + + protected FFMpegGlobalOptions WithOption(IArgument argument) + { + Arguments.Add(argument); + return this; + } + + } +} \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/FFMpegOptionsBase.cs b/FFMpegCore/FFMpeg/FFMpegOptionsBase.cs new file mode 100644 index 0000000..015e609 --- /dev/null +++ b/FFMpegCore/FFMpeg/FFMpegOptionsBase.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using FFMpegCore.Arguments; + +namespace FFMpegCore +{ + public abstract class FFMpegOptionsBase + { + internal readonly List Arguments = new List(); + } +} \ No newline at end of file diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index fb81a9c..6eb5001 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -5,15 +5,15 @@ https://github.com/rosenbjerg/FFMpegCore https://github.com/rosenbjerg/FFMpegCore - A great way to use FFMpeg encoding when writing video applications, client-side and server-side. It has wrapper methods that allow conversion to all web formats: MP4, OGV, TS and methods of capturing screens from the videos. + A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your C# applications 1.0.12 1.1.0.0 1.1.0.0 - - Make Tags a dictionary for flexibility -- Handle rotated video frames in snapshot + API changes +- Allow for specifying arguments to input files etc. 8 - 2.2.6 - Vlad Jerca, Malte Rosenbjerg + 3.0.0 + Malte Rosenbjerg, Vlad Jerca ffmpeg ffprobe convert video audio mediafile resize analyze muxing GitHub true diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs index 12dead6..069aacc 100644 --- a/FFMpegCore/FFProbe/FFProbe.cs +++ b/FFMpegCore/FFProbe/FFProbe.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Text.Json; using System.Threading.Tasks; using FFMpegCore.Arguments; @@ -17,6 +18,12 @@ public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.M instance.BlockUntilFinished(); return ParseOutput(filePath, instance); } + public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue) + { + using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity); + instance.BlockUntilFinished(); + return ParseOutput(uri.AbsoluteUri, instance); + } public static IMediaAnalysis Analyse(System.IO.Stream stream, int outputCapacity = int.MaxValue) { var streamPipeSource = new StreamPipeSource(stream); @@ -46,7 +53,13 @@ public static async Task AnalyseAsync(string filePath, int outpu await instance.FinishedRunning(); return ParseOutput(filePath, instance); } - public static async Task AnalyseAsync(System.IO.Stream stream, int outputCapacity = int.MaxValue) + public static async Task AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue) + { + using var instance = PrepareInstance(uri.AbsoluteUri, outputCapacity); + await instance.FinishedRunning(); + return ParseOutput(uri.AbsoluteUri, instance); + } + public static async Task AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue) { var streamPipeSource = new StreamPipeSource(stream); var pipeArgument = new InputPipeArgument(streamPipeSource); @@ -85,11 +98,10 @@ private static IMediaAnalysis ParseOutput(string filePath, Instance instance) private static Instance PrepareInstance(string filePath, int outputCapacity) { - FFProbeHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); - var ffprobe = FFMpegOptions.Options.FFProbeBinary(); + FFProbeHelper.RootExceptionCheck(); FFProbeHelper.VerifyFFProbeExists(); var arguments = $"-print_format json -show_format -sexagesimal -show_streams \"{filePath}\""; - var instance = new Instance(ffprobe, arguments) {DataBufferCapacity = outputCapacity}; + var instance = new Instance(FFMpegOptions.Options.FFProbeBinary(), arguments) {DataBufferCapacity = outputCapacity}; return instance; } }