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 @@ namespace FFMpegCore.Test
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 @@ namespace FFMpegCore.Test
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 @@ namespace FFMpegCore.Test
[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 @@ namespace FFMpegCore.Test
[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 @@ namespace FFMpegCore.Test
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()
{ {
@ -210,7 +221,6 @@ namespace FFMpegCore.Test
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 @@ namespace FFMpegCore.Test
} }
[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 @@ namespace FFMpegCore.FFMPEG
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 @@ namespace FFMpegCore.FFMPEG
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 @@ namespace FFMpegCore.FFMPEG
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 @@ namespace FFMpegCore.FFMPEG
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 @@ namespace FFMpegCore.FFMPEG
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 @@ namespace FFMpegCore.FFMPEG
_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 @@ namespace FFMpegCore.FFMPEG
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 @@ namespace FFMpegCore.FFMPEG
{ {
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: