Merge remote-tracking branch 'fork/master' into feature/piping

This commit is contained in:
Максим Багрянцев 2020-05-02 13:02:01 +03:00
commit ee16064f4c
8 changed files with 96 additions and 44 deletions

View file

@ -9,10 +9,10 @@ namespace FFMpegCore.Test
[TestClass] [TestClass]
public class ArgumentBuilderTest : BaseTest public class ArgumentBuilderTest : BaseTest
{ {
List<string> concatFiles = new List<string> private List<string> concatFiles = new List<string>
{ "1.mp4", "2.mp4", "3.mp4", "4.mp4"}; { "1.mp4", "2.mp4", "3.mp4", "4.mp4"};
FFArgumentBuilder builder; private FFArgumentBuilder builder;
public ArgumentBuilderTest() : base() public ArgumentBuilderTest() : base()
{ {
@ -21,7 +21,7 @@ public ArgumentBuilderTest() : base()
private string GetArgumentsString(params Argument[] args) private string GetArgumentsString(params Argument[] args)
{ {
var container = new ArgumentContainer {new InputArgument("input.mp4")}; var container = new ArgumentContainer { new InputArgument("input.mp4") };
foreach (var a in args) foreach (var a in args)
{ {
container.Add(a); container.Add(a);
@ -31,7 +31,6 @@ private string GetArgumentsString(params Argument[] args)
return builder.BuildArguments(container); return builder.BuildArguments(container);
} }
[TestMethod] [TestMethod]
public void Builder_BuildString_IO_1() public void Builder_BuildString_IO_1()
{ {
@ -51,8 +50,22 @@ public void Builder_BuildString_Scale()
[TestMethod] [TestMethod]
public void Builder_BuildString_AudioCodec() public void Builder_BuildString_AudioCodec()
{ {
var str = GetArgumentsString(new AudioCodecArgument(AudioCodec.Aac, AudioQuality.Normal)); var str = GetArgumentsString(new AudioCodecArgument(AudioCodec.Aac));
Assert.AreEqual(str, "-i \"input.mp4\" -c:a aac -b:a 128k \"output.mp4\""); Assert.AreEqual(str, "-i \"input.mp4\" -c:a aac \"output.mp4\"");
}
[TestMethod]
public void Builder_BuildString_AudioBitrate()
{
var str = GetArgumentsString(new AudioBitrateArgument(AudioQuality.Normal));
Assert.AreEqual(str, "-i \"input.mp4\" -b:a 128k \"output.mp4\"");
}
[TestMethod]
public void Builder_BuildString_Quiet()
{
var str = GetArgumentsString(new QuietArgument());
Assert.AreEqual(str, "-i \"input.mp4\" -hide_banner -loglevel warning \"output.mp4\"");
} }
[TestMethod] [TestMethod]
@ -66,8 +79,7 @@ public void Builder_BuildString_BitStream()
[TestMethod] [TestMethod]
public void Builder_BuildString_Concat() public void Builder_BuildString_Concat()
{ {
var container = new ArgumentContainer {new ConcatArgument(concatFiles), new OutputArgument("output.mp4")}; var container = new ArgumentContainer { new ConcatArgument(concatFiles), new OutputArgument("output.mp4") };
var str = builder.BuildArguments(container); var str = builder.BuildArguments(container);
@ -82,7 +94,6 @@ public void Builder_BuildString_Copy_Audio()
Assert.AreEqual(str, "-i \"input.mp4\" -c:a copy \"output.mp4\""); Assert.AreEqual(str, "-i \"input.mp4\" -c:a copy \"output.mp4\"");
} }
[TestMethod] [TestMethod]
public void Builder_BuildString_Copy_Video() public void Builder_BuildString_Copy_Video()
{ {
@ -174,7 +185,7 @@ public void Builder_BuildString_Speed()
[TestMethod] [TestMethod]
public void Builder_BuildString_DrawtextFilter() public void Builder_BuildString_DrawtextFilter()
{ {
var str = GetArgumentsString(new DrawTextArgument("Stack Overflow", "/path/to/font.ttf", var str = GetArgumentsString(new DrawTextArgument("Stack Overflow", "/path/to/font.ttf",
("fontcolor", "white"), ("fontcolor", "white"),
("fontsize", "24"), ("fontsize", "24"),
("box", "1"), ("box", "1"),
@ -198,7 +209,7 @@ public void Builder_BuildString_StartNumber()
public void Builder_BuildString_Threads_1() public void Builder_BuildString_Threads_1()
{ {
var str = GetArgumentsString(new ThreadsArgument(50)); var str = GetArgumentsString(new ThreadsArgument(50));
Assert.AreEqual(str, "-i \"input.mp4\" -threads 50 \"output.mp4\""); Assert.AreEqual(str, "-i \"input.mp4\" -threads 50 \"output.mp4\"");
} }
@ -210,7 +221,6 @@ public void Builder_BuildString_Threads_2()
Assert.AreEqual(str, $"-i \"input.mp4\" -threads {Environment.ProcessorCount} \"output.mp4\""); Assert.AreEqual(str, $"-i \"input.mp4\" -threads {Environment.ProcessorCount} \"output.mp4\"");
} }
[TestMethod] [TestMethod]
public void Builder_BuildString_Codec() public void Builder_BuildString_Codec()
{ {
@ -228,10 +238,21 @@ public void Builder_BuildString_Codec_Override()
} }
[TestMethod] [TestMethod]
public void Builder_BuildString_Duration() { public void Builder_BuildString_Duration()
{
var str = GetArgumentsString(new DurationArgument(TimeSpan.FromSeconds(20))); var str = GetArgumentsString(new DurationArgument(TimeSpan.FromSeconds(20)));
Assert.AreEqual(str, "-i \"input.mp4\" -t 00:00:20 \"output.mp4\""); Assert.AreEqual(str, "-i \"input.mp4\" -t 00:00:20 \"output.mp4\"");
} }
[TestMethod]
public void Builder_BuildString_Raw()
{
var str = GetArgumentsString(new CustomArgument(null));
Assert.AreEqual(str, "-i \"input.mp4\" \"output.mp4\"");
str = GetArgumentsString(new CustomArgument("-acodec copy"));
Assert.AreEqual(str, "-i \"input.mp4\" -acodec copy \"output.mp4\"");
}
} }
} }

View file

@ -0,0 +1,19 @@
using FFMpegCore.FFMPEG.Enums;
namespace FFMpegCore.FFMPEG.Argument
{
/// <summary>
/// Represents parameter of audio codec and it's quality
/// </summary>
public class AudioBitrateArgument : Argument<int>
{
public AudioBitrateArgument(AudioQuality value) : base((int)value) { }
public AudioBitrateArgument(int bitrate) : base(bitrate) { }
/// <inheritdoc/>
public override string GetStringValue()
{
return $"-b:a {Value}k";
}
}
}

View file

@ -7,26 +7,12 @@ namespace FFMpegCore.FFMPEG.Argument
/// </summary> /// </summary>
public class AudioCodecArgument : Argument<AudioCodec> public class AudioCodecArgument : Argument<AudioCodec>
{ {
/// <summary>
/// Bitrate of audio channel
/// </summary>
public int Bitrate { get; } = (int)AudioQuality.Normal;
public AudioCodecArgument() { }
public AudioCodecArgument(AudioCodec value) : base(value) { } public AudioCodecArgument(AudioCodec value) : base(value) { }
public AudioCodecArgument(AudioCodec value, AudioQuality bitrate) : this(value, (int) bitrate) { }
public AudioCodecArgument(AudioCodec value, int bitrate) : base(value)
{
Bitrate = bitrate;
}
/// <inheritdoc/> /// <inheritdoc/>
public override string GetStringValue() public override string GetStringValue()
{ {
return $"-c:a {Value.ToString().ToLower()} -b:a {Bitrate}k"; return $"-c:a {Value.ToString().ToLower()}";
} }
} }
} }

View file

@ -0,0 +1,14 @@
namespace FFMpegCore.FFMPEG.Argument
{
public class CustomArgument : Argument<string>
{
public CustomArgument(string argument) : base(argument)
{
}
public override string GetStringValue()
{
return Value ?? string.Empty;
}
}
}

View file

@ -0,0 +1,10 @@
namespace FFMpegCore.FFMPEG.Argument
{
public class QuietArgument : Argument
{
public override string GetStringValue()
{
return "-hide_banner -loglevel warning";
}
}
}

View file

@ -151,7 +151,8 @@ public VideoInfo Convert(
new ScaleArgument(outputSize), new ScaleArgument(outputSize),
new VideoCodecArgument(VideoCodec.LibX264, 2400), new VideoCodecArgument(VideoCodec.LibX264, 2400),
new SpeedArgument(speed), new SpeedArgument(speed),
new AudioCodecArgument(AudioCodec.Aac, audioQuality), new AudioCodecArgument(AudioCodec.Aac),
new AudioBitrateArgument(audioQuality),
new OutputArgument(output))), new OutputArgument(output))),
VideoType.Ogv => Convert(new ArgumentContainer( VideoType.Ogv => Convert(new ArgumentContainer(
new InputArgument(source), new InputArgument(source),
@ -159,7 +160,8 @@ public VideoInfo Convert(
new ScaleArgument(outputSize), new ScaleArgument(outputSize),
new VideoCodecArgument(VideoCodec.LibTheora, 2400), new VideoCodecArgument(VideoCodec.LibTheora, 2400),
new SpeedArgument(speed), new SpeedArgument(speed),
new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality), new AudioCodecArgument(AudioCodec.LibVorbis),
new AudioBitrateArgument(audioQuality),
new OutputArgument(output))), new OutputArgument(output))),
VideoType.Ts => Convert(new ArgumentContainer( VideoType.Ts => Convert(new ArgumentContainer(
new InputArgument(source), new InputArgument(source),
@ -173,7 +175,8 @@ public VideoInfo Convert(
new ScaleArgument(outputSize), new ScaleArgument(outputSize),
new VideoCodecArgument(VideoCodec.LibVpx, 2400), new VideoCodecArgument(VideoCodec.LibVpx, 2400),
new SpeedArgument(speed), new SpeedArgument(speed),
new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality), new AudioCodecArgument(AudioCodec.LibVorbis),
new AudioBitrateArgument(audioQuality),
new OutputArgument(output))), new OutputArgument(output))),
_ => throw new ArgumentOutOfRangeException(nameof(type)) _ => throw new ArgumentOutOfRangeException(nameof(type))
}; };
@ -196,7 +199,8 @@ public VideoInfo PosterWithAudio(FileInfo image, FileInfo audio, FileInfo output
new InputArgument(image.FullName, audio.FullName), new InputArgument(image.FullName, audio.FullName),
new LoopArgument(1), new LoopArgument(1),
new VideoCodecArgument(VideoCodec.LibX264, 2400), new VideoCodecArgument(VideoCodec.LibX264, 2400),
new AudioCodecArgument(AudioCodec.Aac, AudioQuality.Normal), new AudioCodecArgument(AudioCodec.Aac),
new AudioBitrateArgument(AudioQuality.Normal),
new ShortestArgument(true), new ShortestArgument(true),
new OutputArgument(output) new OutputArgument(output)
); );
@ -377,7 +381,8 @@ public VideoInfo ReplaceAudio(VideoInfo source, FileInfo audio, FileInfo output,
return Convert(new ArgumentContainer( return Convert(new ArgumentContainer(
new InputArgument(source.FullName, audio.FullName), new InputArgument(source.FullName, audio.FullName),
new CopyArgument(), new CopyArgument(),
new AudioCodecArgument(AudioCodec.Aac, AudioQuality.Hd), new AudioCodecArgument(AudioCodec.Aac),
new AudioBitrateArgument(AudioQuality.Hd),
new ShortestArgument(stopAtShortest), new ShortestArgument(stopAtShortest),
new OutputArgument(output) new OutputArgument(output)
)); ));
@ -394,9 +399,7 @@ public VideoInfo Convert(ArgumentContainer arguments, bool skipExistsCheck = fal
_totalTime = TimeSpan.MinValue; _totalTime = TimeSpan.MinValue;
if (output == null) return output != null && output.Exists ? new VideoInfo(output) : null;
return null;
return new VideoInfo(output);
} }
public async Task<VideoInfo> ConvertAsync(ArgumentContainer arguments, bool skipExistsCheck = false) public async Task<VideoInfo> ConvertAsync(ArgumentContainer arguments, bool skipExistsCheck = false)
{ {
@ -408,9 +411,8 @@ public async Task<VideoInfo> ConvertAsync(ArgumentContainer arguments, bool skip
throw new FFMpegException(FFMpegExceptionType.Conversion, "Could not process file without error"); throw new FFMpegException(FFMpegExceptionType.Conversion, "Could not process file without error");
_totalTime = TimeSpan.MinValue; _totalTime = TimeSpan.MinValue;
if (output == null)
return null; return output != null && output.Exists ? new VideoInfo(output) : null;
return new VideoInfo(output);
} }
private static (VideoInfo[] Input, FileInfo Output) GetInputOutput(ArgumentContainer arguments) private static (VideoInfo[] Input, FileInfo Output) GetInputOutput(ArgumentContainer arguments)

View file

@ -147,7 +147,7 @@ private VideoInfo ParseVideoInfoInternal(VideoInfo info, string probeOutput)
{ {
var metadata = JsonConvert.DeserializeObject<FFMpegStreamMetadata>(probeOutput); var metadata = JsonConvert.DeserializeObject<FFMpegStreamMetadata>(probeOutput);
if (metadata.Streams == null || metadata.Streams.Count == 0) if (metadata?.Streams == null || metadata.Streams.Count == 0)
{ {
throw new FFMpegException(FFMpegExceptionType.File, $"No video or audio streams could be detected. Source: ${info.FullName}"); throw new FFMpegException(FFMpegExceptionType.File, $"No video or audio streams could be detected. Source: ${info.FullName}");
} }

View file

@ -358,9 +358,9 @@ public enum VideoCodec
} }
``` ```
### ArgumentBuilder ### ArgumentBuilder
Custom video converting presets could be created with help of `ArgumentsContainer` class: Custom video converting presets could be created with help of `ArgumentContainer` class:
```csharp ```csharp
var container = new ArgumentsContainer(); var container = new ArgumentContainer();
container.Add(new VideoCodecArgument(VideoCodec.LibX264)); container.Add(new VideoCodecArgument(VideoCodec.LibX264));
container.Add(new ScaleArgument(VideoSize.Hd)); container.Add(new ScaleArgument(VideoSize.Hd));
@ -368,7 +368,7 @@ var ffmpeg = new FFMpeg();
var result = ffmpeg.Convert(container, new FileInfo("input.mp4"), new FileInfo("output.mp4")); var result = ffmpeg.Convert(container, new FileInfo("input.mp4"), new FileInfo("output.mp4"));
``` ```
Other availible arguments could be found in `FFMpegCore.FFMPEG.Arguments` namespace. Other availible arguments could be found in `FFMpegCore.FFMPEG.Argument` namespace.
If you need to create your custom argument, you just need to create new class, that is inherited from `Argument`, `Argument<T>` or `Argument<T1, T2>` If you need to create your custom argument, you just need to create new class, that is inherited from `Argument`, `Argument<T>` or `Argument<T1, T2>`
For example: For example: