This commit is contained in:
Malte Rosenbjerg 2020-10-24 22:31:54 +02:00
parent 0136d49edf
commit 010e9947e9
14 changed files with 407 additions and 408 deletions

View file

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

View file

@ -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<ScaleArgument>().FirstOrDefault();
var success = processor.ProcessSynchronously();
var outputVideo = FFProbe.Analyse(output);
Assert.IsTrue(success);
Assert.IsTrue(File.Exists(output));
Assert.IsTrue(Math.Abs((outputVideo.Duration - input.Duration).TotalMilliseconds) < 1000.0 / input.PrimaryVideoStream.FrameRate);
if (scaling?.Size == null)
{
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<ScaleArgument>();
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<ScaleArgument>();
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
processor.ProcessSynchronously();
@ -159,7 +160,7 @@ private void ConvertToStreamPipe(params IArgument[] inputArguments)
}
}
public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] inputArguments)
public void Convert(ContainerFormat type, Action<IMediaAnalysis> validationMethod, params IArgument[] arguments)
{
var output = Input.OutputLocation(type);
@ -167,13 +168,15 @@ public void Convert(ContainerFormat type, Action<IMediaAnalysis> 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<ScaleArgument>();
var scaling = arguments.OfType<ScaleArgument>().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<ScaleArgument>();
var processor = FFMpegArguments.FromPipeInput(videoFramesSource).OutputToFile(output, false, opt =>
{
foreach (var arg in arguments)
opt.WithArgument(arg);
});
var scaling = arguments.OfType<ScaleArgument>().FirstOrDefault();
processor.ProcessSynchronously();
var outputVideo = FFProbe.Analyse(output);
@ -304,9 +307,8 @@ await Assert.ThrowsExceptionAsync<FFMpegException>(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);

View file

@ -1,16 +0,0 @@
namespace FFMpegCore.Arguments
{
/// <summary>
/// Represents cpu speed parameter
/// </summary>
public class CpuSpeedArgument : IArgument
{
public readonly int CpuSpeed;
public CpuSpeedArgument(int cpuSpeed)
{
CpuSpeed = cpuSpeed;
}
public string Text => $"-quality good -cpu-used {CpuSpeed} -deadline realtime";
}
}

View file

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

View file

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

View file

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

View file

@ -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();
}
/// <summary>
@ -45,10 +45,10 @@ public static Task<bool> 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();
}
/// <summary>
@ -60,12 +60,12 @@ public static Task<bool> SnapshotAsync(IMediaAnalysis source, string output, Siz
/// <returns>Bitmap with the requested snapshot.</returns>
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
/// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<Bitmap> 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<FFMpegArgumentOptions> 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<Enums.PixelFormat> GetPixelFormatsInternal()
internal static IReadOnlyList<PixelFormat> GetPixelFormatsInternal()
{
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
FFMpegHelper.RootExceptionCheck();
var list = new List<Enums.PixelFormat>();
var list = new List<PixelFormat>();
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<Enums.PixelFormat> GetPixelFormats()
public static IReadOnlyList<PixelFormat> 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<string, Codec> codecs, string arguments, Func<string, Codec?> parser)
private static void ParsePartOfCodecs(Dictionary<string, Codec> codecs, string arguments, Func<string, Codec?> 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<ContainerFormat> GetContainersFormatsInternal()
{
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
FFMpegHelper.RootExceptionCheck();
var list = new List<ContainerFormat>();
using var instance = new Instances.Instance(FFMpegOptions.Options.FFmpegBinary(), "-formats");

View file

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

View file

@ -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<string> errorData)
{

View file

@ -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<IArgument> _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<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new ConcatArgument(filePaths), addArguments);
public static FFMpegArguments FromDemuxConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new DemuxConcatArgument(filePaths), addArguments);
public static FFMpegArguments FromFileInput(string filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(verifyExists, filePath), addArguments);
public static FFMpegArguments FromFileInput(FileInfo fileInfo, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
public static FFMpegArguments FromFileInput(IMediaAnalysis mediaAnalysis, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments);
public static FFMpegArguments FromUrlInput(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
public static FFMpegArguments FromPipeInput(IPipeSource sourcePipe, Action<FFMpegArgumentOptions>? addArguments = null) => new FFMpegArguments().WithInput(new InputPipeArgument(sourcePipe), addArguments);
public FFMpegArguments WithGlobalOptions(Action<FFMpegGlobalOptions> configureOptions)
{
_inputArgument = inputArgument;
_arguments = new List<IArgument> { 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<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new ConcatArgument(filePaths), addArguments);
public FFMpegArguments AddDemuxConcatInput(IEnumerable<string> filePaths, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new DemuxConcatArgument(filePaths), addArguments);
public FFMpegArguments AddFileInput(string filePath, bool verifyExists = true, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(verifyExists, filePath), addArguments);
public FFMpegArguments AddFileInput(FileInfo fileInfo, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
public FFMpegArguments AddFileInput(IMediaAnalysis mediaAnalysis, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(mediaAnalysis.Path, false), addArguments);
public FFMpegArguments AddUrlInput(Uri uri, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
public FFMpegArguments AddPipeInput(IPipeSource sourcePipe, Action<FFMpegArgumentOptions>? addArguments = null) => WithInput(new InputPipeArgument(sourcePipe), addArguments);
private FFMpegArguments WithInput(IInputArgument inputArgument, Action<FFMpegArgumentOptions>? 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<FFMpegArgumentOptions>? addArguments = null) => ToProcessor(new OutputArgument(file, overwrite), addArguments);
public FFMpegArgumentProcessor OutputToFile(Uri uri, bool overwrite = true, Action<FFMpegArgumentOptions>? addArguments = null) => ToProcessor(new OutputArgument(uri.AbsolutePath, overwrite), addArguments);
public FFMpegArgumentProcessor OutputToPipe(IPipeSink reader, Action<FFMpegArgumentOptions>? addArguments = null) => ToProcessor(new OutputPipeArgument(reader), addArguments);
private FFMpegArgumentProcessor ToProcessor(IOutputArgument argument, Action<FFMpegArgumentOptions>? 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<IInputOutputArgument>())
argument.Pre();
}
internal async Task During(CancellationToken? cancellationToken = null)
{
await Task.WhenAll(_inputArgument.During(cancellationToken), _outputArgument.During(cancellationToken)).ConfigureAwait(false);
var inputOutputArguments = Arguments.OfType<IInputOutputArgument>();
await Task.WhenAll(inputOutputArguments.Select(io => io.During(cancellationToken))).ConfigureAwait(false);
}
internal void Post()
{
_inputArgument.Post();
_outputArgument.Post();
}
public TArgument Find<TArgument>() where TArgument : class, IArgument
{
return _arguments.OfType<TArgument>().FirstOrDefault();
foreach (var argument in Arguments.OfType<IInputOutputArgument>())
argument.Post();
}
}
}

View file

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

View file

@ -0,0 +1,10 @@
using System.Collections.Generic;
using FFMpegCore.Arguments;
namespace FFMpegCore
{
public abstract class FFMpegOptionsBase
{
internal readonly List<IArgument> Arguments = new List<IArgument>();
}
}

View file

@ -5,15 +5,15 @@
<RepositoryUrl>https://github.com/rosenbjerg/FFMpegCore</RepositoryUrl>
<PackageProjectUrl>https://github.com/rosenbjerg/FFMpegCore</PackageProjectUrl>
<Copyright></Copyright>
<Description>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.</Description>
<Description>A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your C# applications</Description>
<Version>1.0.12</Version>
<AssemblyVersion>1.1.0.0</AssemblyVersion>
<FileVersion>1.1.0.0</FileVersion>
<PackageReleaseNotes>- Make Tags a dictionary for flexibility
- Handle rotated video frames in snapshot</PackageReleaseNotes>
<PackageReleaseNotes>API changes
- Allow for specifying arguments to input files etc.</PackageReleaseNotes>
<LangVersion>8</LangVersion>
<PackageVersion>2.2.6</PackageVersion>
<Authors>Vlad Jerca, Malte Rosenbjerg</Authors>
<PackageVersion>3.0.0</PackageVersion>
<Authors>Malte Rosenbjerg, Vlad Jerca</Authors>
<PackageTags>ffmpeg ffprobe convert video audio mediafile resize analyze muxing</PackageTags>
<RepositoryType>GitHub</RepositoryType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>

View file

@ -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<IMediaAnalysis> AnalyseAsync(string filePath, int outpu
await instance.FinishedRunning();
return ParseOutput(filePath, instance);
}
public static async Task<IMediaAnalysis> AnalyseAsync(System.IO.Stream stream, int outputCapacity = int.MaxValue)
public static async Task<IMediaAnalysis> 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<IMediaAnalysis> 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;
}
}