mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +01:00
Minor refactor and add async Convert
This commit is contained in:
parent
55a7e74817
commit
7406cd0fa2
3 changed files with 110 additions and 121 deletions
|
@ -23,6 +23,19 @@ public ArgumentContainer(params Argument[] arguments)
|
|||
|
||||
public Argument this[Type key] { get => _args[key]; set => _args[key] = value; }
|
||||
|
||||
public bool TryGetArgument<T>(out T output)
|
||||
where T : Argument
|
||||
{
|
||||
if (_args.TryGetValue(typeof(T), out var arg))
|
||||
{
|
||||
output = (T) arg;
|
||||
return true;
|
||||
}
|
||||
|
||||
output = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public ICollection<Type> Keys => _args.Keys;
|
||||
|
||||
public ICollection<Argument> Values => _args.Values;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FFMpegCore.FFMPEG.Argument
|
||||
{
|
||||
|
@ -29,5 +30,9 @@ IEnumerator IEnumerable.GetEnumerator()
|
|||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
public VideoInfo[] GetAsVideoInfo()
|
||||
{
|
||||
return Value.Select(v => new VideoInfo(v)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Instances;
|
||||
|
||||
namespace FFMpegCore.FFMPEG
|
||||
|
@ -21,7 +22,7 @@ namespace FFMpegCore.FFMPEG
|
|||
|
||||
public class FFMpeg
|
||||
{
|
||||
IArgumentBuilder ArgumentBuilder { get; set; }
|
||||
IArgumentBuilder ArgumentBuilder { get; set; } = new FFArgumentBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// Intializes the FFMPEG encoder.
|
||||
|
@ -29,10 +30,7 @@ public class FFMpeg
|
|||
public FFMpeg() : base()
|
||||
{
|
||||
FFMpegHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory);
|
||||
|
||||
_ffmpegPath = FFMpegOptions.Options.FFmpegBinary;
|
||||
|
||||
ArgumentBuilder = new FFArgumentBuilder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -137,66 +135,46 @@ public VideoInfo Convert(
|
|||
FFMpegHelper.ExtensionExceptionCheck(output, FileExtension.ForType(type));
|
||||
FFMpegHelper.ConversionSizeExceptionCheck(source);
|
||||
|
||||
_totalTime = source.Duration;
|
||||
|
||||
var scale = VideoSize.Original == size ? 1 : (double) source.Height / (int) size;
|
||||
|
||||
var outputSize = new Size(
|
||||
(int) (source.Width / scale),
|
||||
(int) (source.Height / scale)
|
||||
);
|
||||
var outputSize = new Size((int) (source.Width / scale), (int) (source.Height / scale));
|
||||
|
||||
if (outputSize.Width % 2 != 0)
|
||||
{
|
||||
outputSize.Width += 1;
|
||||
}
|
||||
|
||||
var container = new ArgumentContainer();
|
||||
|
||||
switch (type)
|
||||
return type switch
|
||||
{
|
||||
case VideoType.Mp4:
|
||||
container.Add(
|
||||
new InputArgument(source),
|
||||
new ThreadsArgument(multithreaded),
|
||||
new ScaleArgument(outputSize),
|
||||
new VideoCodecArgument(VideoCodec.LibX264, 2400),
|
||||
new SpeedArgument(speed),
|
||||
new AudioCodecArgument(AudioCodec.Aac, audioQuality),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
break;
|
||||
case VideoType.Ogv:
|
||||
container.Add(
|
||||
new InputArgument(source),
|
||||
new ThreadsArgument(multithreaded),
|
||||
new ScaleArgument(outputSize),
|
||||
new VideoCodecArgument(VideoCodec.LibTheora, 2400),
|
||||
new SpeedArgument(speed),
|
||||
new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
break;
|
||||
case VideoType.Ts:
|
||||
container.Add(
|
||||
new InputArgument(source),
|
||||
new CopyArgument(),
|
||||
new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB),
|
||||
new ForceFormatArgument(VideoCodec.MpegTs),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!RunProcess(container, output))
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Conversion,
|
||||
$"The video could not be converted to {Enum.GetName(typeof(VideoType), type)}");
|
||||
}
|
||||
|
||||
_totalTime = TimeSpan.MinValue;
|
||||
|
||||
return new VideoInfo(output);
|
||||
VideoType.Mp4 => Convert(new ArgumentContainer(
|
||||
new InputArgument(source),
|
||||
new ThreadsArgument(multithreaded),
|
||||
new ScaleArgument(outputSize),
|
||||
new VideoCodecArgument(VideoCodec.LibX264, 2400),
|
||||
new SpeedArgument(speed),
|
||||
new AudioCodecArgument(AudioCodec.Aac, audioQuality),
|
||||
new OutputArgument(output))),
|
||||
VideoType.Ogv => Convert(new ArgumentContainer(
|
||||
new InputArgument(source),
|
||||
new ThreadsArgument(multithreaded),
|
||||
new ScaleArgument(outputSize),
|
||||
new VideoCodecArgument(VideoCodec.LibTheora, 2400),
|
||||
new SpeedArgument(speed),
|
||||
new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality),
|
||||
new OutputArgument(output))),
|
||||
VideoType.Ts => Convert(new ArgumentContainer(
|
||||
new InputArgument(source),
|
||||
new CopyArgument(),
|
||||
new BitStreamFilterArgument(Channel.Video, Filter.H264_Mp4ToAnnexB),
|
||||
new ForceFormatArgument(VideoCodec.MpegTs),
|
||||
new OutputArgument(output))),
|
||||
VideoType.WebM => Convert(new ArgumentContainer(
|
||||
new InputArgument(source),
|
||||
new ThreadsArgument(multithreaded),
|
||||
new ScaleArgument(outputSize),
|
||||
new VideoCodecArgument(VideoCodec.LibVpx, 2400),
|
||||
new SpeedArgument(speed),
|
||||
new AudioCodecArgument(AudioCodec.LibVorbis, audioQuality),
|
||||
new OutputArgument(output))),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type))
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -213,14 +191,13 @@ public VideoInfo PosterWithAudio(FileInfo image, FileInfo audio, FileInfo output
|
|||
FFMpegHelper.ConversionSizeExceptionCheck(Image.FromFile(image.FullName));
|
||||
|
||||
var container = new ArgumentContainer(
|
||||
new LoopArgument(1),
|
||||
new InputArgument(image.FullName, audio.FullName),
|
||||
new LoopArgument(1),
|
||||
new VideoCodecArgument(VideoCodec.LibX264, 2400),
|
||||
new AudioCodecArgument(AudioCodec.Aac, AudioQuality.Normal),
|
||||
new ShortestArgument(true),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
|
||||
if (!RunProcess(container, output))
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation,
|
||||
|
@ -245,30 +222,18 @@ public VideoInfo Join(FileInfo output, params VideoInfo[] videos)
|
|||
{
|
||||
FFMpegHelper.ConversionSizeExceptionCheck(video);
|
||||
var destinationPath = video.FullName.Replace(video.Extension, FileExtension.Ts);
|
||||
Convert(
|
||||
video,
|
||||
new FileInfo(destinationPath),
|
||||
VideoType.Ts
|
||||
);
|
||||
Convert(video, new FileInfo(destinationPath), VideoType.Ts);
|
||||
return destinationPath;
|
||||
}).ToList();
|
||||
|
||||
var container = new ArgumentContainer(
|
||||
new ConcatArgument(temporaryVideoParts),
|
||||
new CopyArgument(),
|
||||
new BitStreamFilterArgument(Channel.Audio, Filter.Aac_AdtstoAsc),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
if (!RunProcess(container, output))
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation,
|
||||
"Could not join the provided video files.");
|
||||
}
|
||||
|
||||
return new VideoInfo(output);
|
||||
return Convert(new ArgumentContainer(
|
||||
new ConcatArgument(temporaryVideoParts),
|
||||
new CopyArgument(),
|
||||
new BitStreamFilterArgument(Channel.Audio, Filter.Aac_AdtstoAsc),
|
||||
new OutputArgument(output)
|
||||
));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -314,7 +279,7 @@ public VideoInfo JoinImageSequence(FileInfo output, double frameRate = 30, param
|
|||
throw new FFMpegException(FFMpegExceptionType.Operation,
|
||||
"Could not join the provided image sequence.");
|
||||
}
|
||||
|
||||
|
||||
return new VideoInfo(output);
|
||||
}
|
||||
finally
|
||||
|
@ -335,18 +300,10 @@ public VideoInfo SaveM3U8Stream(Uri uri, FileInfo output)
|
|||
|
||||
if (uri.Scheme == "http" || uri.Scheme == "https")
|
||||
{
|
||||
var container = new ArgumentContainer(
|
||||
return Convert(new ArgumentContainer(
|
||||
new InputArgument(uri),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
|
||||
if (!RunProcess(container, output))
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation,
|
||||
$"Saving the ${uri.AbsoluteUri} stream failed.");
|
||||
}
|
||||
|
||||
return new VideoInfo(output);
|
||||
));
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Uri: {uri.AbsoluteUri}, does not point to a valid http(s) stream.");
|
||||
|
@ -364,19 +321,12 @@ public VideoInfo Mute(VideoInfo source, FileInfo output)
|
|||
FFMpegHelper.ConversionSizeExceptionCheck(source);
|
||||
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
|
||||
|
||||
var container = new ArgumentContainer(
|
||||
return Convert(new ArgumentContainer(
|
||||
new InputArgument(source),
|
||||
new CopyArgument(),
|
||||
new CopyArgument(Channel.Video),
|
||||
new DisableChannelArgument(Channel.Audio),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
|
||||
if (!RunProcess(container, output))
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, "Could not mute the requested video.");
|
||||
}
|
||||
|
||||
return new VideoInfo(output);
|
||||
));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -422,41 +372,50 @@ public VideoInfo ReplaceAudio(VideoInfo source, FileInfo audio, FileInfo output,
|
|||
FFMpegHelper.ConversionSizeExceptionCheck(source);
|
||||
FFMpegHelper.ExtensionExceptionCheck(output, source.Extension);
|
||||
|
||||
var container = new ArgumentContainer(
|
||||
return Convert(new ArgumentContainer(
|
||||
new InputArgument(source.FullName, audio.FullName),
|
||||
new CopyArgument(),
|
||||
new AudioCodecArgument(AudioCodec.Aac, AudioQuality.Hd),
|
||||
new ShortestArgument(stopAtShortest),
|
||||
new OutputArgument(output)
|
||||
);
|
||||
|
||||
if (!RunProcess(container, output))
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, "Could not replace the video audio.");
|
||||
}
|
||||
|
||||
return new VideoInfo(output);
|
||||
));
|
||||
}
|
||||
|
||||
public VideoInfo Convert(ArgumentContainer arguments)
|
||||
{
|
||||
var output = ((OutputArgument) arguments[typeof(OutputArgument)]).GetAsFileInfo();
|
||||
var sources = ((InputArgument) arguments[typeof(InputArgument)]).GetAsVideoInfo();
|
||||
|
||||
// Sum duration of all sources
|
||||
_totalTime = TimeSpan.Zero;
|
||||
foreach (var source in sources)
|
||||
_totalTime += source.Duration;
|
||||
var (sources, output) = GetInputOutput(arguments);
|
||||
_totalTime = TimeSpan.FromSeconds(sources.Sum(source => source.Duration.TotalSeconds));
|
||||
|
||||
if (!RunProcess(arguments, output))
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, "Could not replace the video audio.");
|
||||
}
|
||||
|
||||
_totalTime = TimeSpan.MinValue;
|
||||
|
||||
return new VideoInfo(output);
|
||||
}
|
||||
public async Task<VideoInfo> ConvertAsync(ArgumentContainer arguments)
|
||||
{
|
||||
var (sources, output) = GetInputOutput(arguments);
|
||||
_totalTime = TimeSpan.FromSeconds(sources.Sum(source => source.Duration.TotalSeconds));
|
||||
|
||||
if (!await RunProcessAsync(arguments, output))
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, "Could not replace the video audio.");
|
||||
|
||||
_totalTime = TimeSpan.MinValue;
|
||||
return new VideoInfo(output);
|
||||
}
|
||||
|
||||
private static (VideoInfo[] Input, FileInfo Output) GetInputOutput(ArgumentContainer arguments)
|
||||
{
|
||||
var output = ((OutputArgument) arguments[typeof(OutputArgument)]).GetAsFileInfo();
|
||||
VideoInfo[] sources;
|
||||
if (arguments.TryGetArgument<InputArgument>(out var input))
|
||||
sources = input.GetAsVideoInfo();
|
||||
else if (arguments.TryGetArgument<ConcatArgument>(out var concat))
|
||||
sources = concat.GetAsVideoInfo();
|
||||
else
|
||||
throw new FFMpegException(FFMpegExceptionType.Operation, "No input or concat argument found");
|
||||
return (sources, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the associated process is still alive/running.
|
||||
|
@ -489,9 +448,21 @@ private bool RunProcess(ArgumentContainer container, FileInfo output)
|
|||
var exitCode = _instance.BlockUntilFinished();
|
||||
|
||||
if (!File.Exists(output.FullName) || new FileInfo(output.FullName).Length == 0)
|
||||
{
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\n", _instance.ErrorData));
|
||||
}
|
||||
|
||||
return exitCode == 0;
|
||||
}
|
||||
private async Task<bool> RunProcessAsync(ArgumentContainer container, FileInfo output)
|
||||
{
|
||||
_instance?.Dispose();
|
||||
var arguments = ArgumentBuilder.BuildArguments(container);
|
||||
|
||||
_instance = new Instance(_ffmpegPath, arguments);
|
||||
_instance.DataReceived += OutputData;
|
||||
var exitCode = await _instance.FinishedRunning();
|
||||
|
||||
if (!File.Exists(output.FullName) || new FileInfo(output.FullName).Length == 0)
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\n", _instance.ErrorData));
|
||||
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue