mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
Merge remote-tracking branch 'fork/master' into feature/piping
This commit is contained in:
commit
ee16064f4c
8 changed files with 96 additions and 44 deletions
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
19
FFMpegCore/FFMPEG/Argument/Atoms/AudioBitrateArgument.cs
Normal file
19
FFMpegCore/FFMPEG/Argument/Atoms/AudioBitrateArgument.cs
Normal 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
FFMpegCore/FFMPEG/Argument/Atoms/CustomArgument.cs
Normal file
14
FFMpegCore/FFMPEG/Argument/Atoms/CustomArgument.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
FFMpegCore/FFMPEG/Argument/Atoms/QuietArgument.cs
Normal file
10
FFMpegCore/FFMPEG/Argument/Atoms/QuietArgument.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace FFMpegCore.FFMPEG.Argument
|
||||||
|
{
|
||||||
|
public class QuietArgument : Argument
|
||||||
|
{
|
||||||
|
public override string GetStringValue()
|
||||||
|
{
|
||||||
|
return "-hide_banner -loglevel warning";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue