Add various arguments

This commit is contained in:
PCF 2025-11-02 20:06:23 +01:00
parent 9f779a56e7
commit 994121ce70
No known key found for this signature in database
GPG key ID: 66C3BD48B2EB2CBA
42 changed files with 896 additions and 0 deletions

View file

@ -1,5 +1,6 @@
using System.Drawing; using System.Drawing;
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Arguments.MainOptions;
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
@ -46,6 +47,38 @@ public class ArgumentBuilderTest
Assert.AreEqual("-i \"input.mp4\" -b:a 128k \"output.mp4\" -y", str); Assert.AreEqual("-i \"input.mp4\" -b:a 128k \"output.mp4\" -y", str);
} }
[TestMethod]
public void Builder_BuildString_HideBanner()
{
var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithHideBanner())
.OutputToFile("output.mp4", false).Arguments;
Assert.AreEqual("-hide_banner -i \"input.mp4\" \"output.mp4\"", str);
}
[TestMethod]
public void Builder_BuildString_NoStats()
{
var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithNoStats())
.OutputToFile("output.mp4", false).Arguments;
Assert.AreEqual("-nostats -i \"input.mp4\" \"output.mp4\"", str);
}
[TestMethod]
public void Builder_BuildString_With_Explicit_Stats()
{
var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithArgument(new Stats(true)))
.OutputToFile("output.mp4", false).Arguments;
Assert.AreEqual("-stats -i \"input.mp4\" \"output.mp4\"", str);
}
[TestMethod]
public void Builder_BuildString_HardwareAccelerationOutputFormat()
{
var str = FFMpegArguments.FromFileInput("input.mp4").WithGlobalOptions(opt => opt.WithHardwareAccelerationOutputFormat(HardwareAccelerationDevice.Auto))
.OutputToFile("output.mp4", false).Arguments;
Assert.AreEqual("-hwaccel_output_format auto -i \"input.mp4\" \"output.mp4\"", str);
}
[TestMethod] [TestMethod]
public void Builder_BuildString_Quiet() public void Builder_BuildString_Quiet()
{ {
@ -552,6 +585,143 @@ public class ArgumentBuilderTest
str); str);
} }
[TestMethod]
public void Builder_BuildString_VideoFilter_Vaapi_Scale()
{
var str = FFMpegArguments
.FromFileInput("input.mp4")
.OutputToFile("output.mp4", false, opt => opt
.WithVideoFilters(filterOptions => filterOptions
.Format("nv12", "vaapi")
.HardwareUpload()
.WithVaapiVideoFilter(options => options
.Scale(VideoSize.FullHd)
)
)
.WithVaapiRcMode(VaapiRcMode.CQP)
.WithH264VaapiOptions(options => options
.WithQuantizer(28)
)
)
.Arguments;
Assert.AreEqual(
"-i \"input.mp4\" -vf \"format=pix_fmts=nv12|vaapi, hwupload, scale_vaapi=-1:1080\" -rc_mode CQP -qp 28 \"output.mp4\"",
str);
}
[TestMethod]
public void Builder_BuildString_VideoFilter_Vaapi_Scale2()
{
var str = FFMpegArguments
.FromFileInput("input.mp4")
.OutputToFile("output.mp4", false, opt => opt
.WithVideoFilters(filterOptions => filterOptions
.Format("nv12", "vaapi")
.HardwareUpload()
.WithVaapiVideoFilter(options => options
.Scale(2560, 1440)
)
)
.WithVaapiRcMode(VaapiRcMode.CQP)
.WithH264VaapiOptions(options => options
.WithQuantizer(28)
)
)
.Arguments;
Assert.AreEqual(
"-i \"input.mp4\" -vf \"format=pix_fmts=nv12|vaapi, hwupload, scale_vaapi=2560:1440\" -rc_mode CQP -qp 28 \"output.mp4\"",
str);
}
[TestMethod]
public void Builder_BuildString_VideoFilter_Vaapi_Scale3()
{
var str = FFMpegArguments
.FromFileInput("input.mp4")
.OutputToFile("output.mp4", false, opt => opt
.WithVideoFilters(filterOptions => filterOptions
.Format("nv12", "vaapi")
.HardwareUpload()
.WithVaapiVideoFilter(options => options
.Scale(new Size(1280, 720))
)
)
.WithVaapiRcMode(VaapiRcMode.CQP)
.WithH264VaapiOptions(options => options
.WithQuantizer(28)
)
)
.Arguments;
Assert.AreEqual(
"-i \"input.mp4\" -vf \"format=pix_fmts=nv12|vaapi, hwupload, scale_vaapi=1280:720\" -rc_mode CQP -qp 28 \"output.mp4\"",
str);
}
[TestMethod]
public void Builder_BuildString_Single_Image()
{
var str = FFMpegArguments
.FromFileInput("input.jpg")
.OutputToFile("output.jpg", false, opt => opt
.WithVideoFilters(filterOptions => filterOptions
.Scale(-1, 120)
)
.WithImage2Options(options => options
.WithUpdate()
)
)
.Arguments;
Assert.AreEqual(
"-i \"input.jpg\" -vf \"scale=-1:120\" -update 1 \"output.jpg\"",
str);
}
[TestMethod]
public void Builder_BuildString_Segments()
{
var str = FFMpegArguments
.FromFileInput("input.mp4")
.OutputToFile("output-%Y%m%d-%s.mp4", false, opt => opt
.WithUseWallclockAsTimestamps()
.ForceFormat("segment")
.WithSegmentOptions(options => options
.WithSegmentAtClocktime()
.WithSegmentTime(TimeSpan.FromMinutes(10))
.WithMinimumSegmentDuration(TimeSpan.FromMinutes(5))
.WithResetTimestamps()
.WithStrftime()
)
)
.Arguments;
Assert.AreEqual(
"-i \"input.mp4\" -use_wallclock_as_timestamps 1 -f segment -segment_atclocktime 1 -segment_time 600 -min_seg_duration 300 -reset_timestamps 1 -strftime 1 \"output-%Y%m%d-%s.mp4\"",
str);
}
[TestMethod]
public void Builder_BuildString_Rtsp_Stream()
{
var str = FFMpegArguments
.FromUrlInput(new Uri("rtsp://server/stream?query"), options => options
.WithAnalyzeDuration(TimeSpan.FromSeconds(1))
.WithProbeSize(1_000_000)
.WithRtspProtocolOptions(argumentOptions => argumentOptions
.WithRtspTransport(RtspTransportProtocol.tcp)
)
)
.OutputToFile("output.mp4", false)
.Arguments;
Assert.AreEqual(
"-analyzeduration 1000000 -probesize 1000000 -rtsp_transport tcp -i \"rtsp://server/stream?query\" \"output.mp4\"",
str);
}
[TestMethod] [TestMethod]
public void Builder_BuildString_GifPalette() public void Builder_BuildString_GifPalette()
{ {

View file

@ -0,0 +1,11 @@
namespace FFMpegCore.Arguments;
/// <summary>
/// Base class for boolean arguments with value <c>0</c> or <c>1</c>.
/// </summary>
/// <param name="value"></param>
public abstract class BaseBoolArgument(bool value) : IArgument
{
protected abstract string ArgumentName { get; }
public string Text => $"-{ArgumentName} {(value ? '1' : '0')}";
}

View file

@ -0,0 +1,14 @@
namespace FFMpegCore.Arguments;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg.html#Options" />
/// Base class for option arguments.
/// Options which do not take arguments are boolean options, and set the corresponding value to true.
/// They can be set to false by prefixing the option name with "no". For example using "-nofoo" will set the boolean option with name "foo" to false.
/// </summary>
/// <param name="value"></param>
public abstract class BaseOptionArgument(bool value) : IArgument
{
protected abstract string ArgumentName { get; }
public string Text => $"-{(value ? "" : "no")}{ArgumentName}";
}

View file

@ -0,0 +1,12 @@
using FFMpegCore.Enums;
namespace FFMpegCore.Arguments.Codecs.Vaapi;
/// <summary>
/// <see href="https://www.ffmpeg.org/ffmpeg-codecs.html#VAAPI-encoders" />
/// Set the rate control mode to use. A given driver may only support a subset of modes.
/// </summary>
public sealed class VaapiRcModeArgument(VaapiRcMode rcMode) : IArgument
{
public string Text => $"-rc_mode {rcMode}";
}

View file

@ -0,0 +1,30 @@
namespace FFMpegCore.Arguments.Codecs.Vaapi.h264Vaapi;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#image2_002c-image2pipe" />
/// </summary>
public sealed class H264VaapiArgumentOptions
{
private readonly FFMpegArgumentOptions _options;
internal H264VaapiArgumentOptions(FFMpegArgumentOptions options)
{
_options = options;
}
/// <summary>
/// <inheritdoc cref="VaapiQpArgument"/>
/// </summary>
/// <param name="quantizer"><c>0</c> - <c>52</c></param>
/// <returns></returns>
public H264VaapiArgumentOptions WithQuantizer(sbyte quantizer)
{
return WithArgument(new VaapiQpArgument(quantizer));
}
public H264VaapiArgumentOptions WithArgument(IH264VaapiArgument argument)
{
_options.WithArgument(argument);
return this;
}
}

View file

@ -0,0 +1,3 @@
namespace FFMpegCore.Arguments.Codecs.Vaapi.h264Vaapi;
public interface IH264VaapiArgument : IArgument;

View file

@ -0,0 +1,11 @@
namespace FFMpegCore.Arguments.Codecs.Vaapi.h264Vaapi;
/// <summary>
/// undocumented(?) <see href="https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/vaapi_encode_h264.c#L1073"/>
/// Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)
/// </summary>
/// <param name="quantizer"><c>0</c> - <c>52</c></param>
public sealed class VaapiQpArgument(sbyte quantizer) : IH264VaapiArgument
{
public string Text => $"-qp {quantizer}";
}

View file

@ -0,0 +1,19 @@
namespace FFMpegCore.Arguments.Formats;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#Format-Options"/>
/// Specify how many microseconds are analyzed to probe the input.
/// A higher value will enable detecting more accurate information, but will increase latency.
/// It defaults to 5,000,000 microseconds = 5 seconds.
/// </summary>
public sealed class AnalyzeDurationArgument(TimeSpan duration) : IArgument
{
#if NET8_OR_GREATER
private readonly long _duration = Convert.ToInt64(duration.TotalMicroseconds);
#else
// https://github.com/dotnet/runtime/blob/e8812e7419db9137f20b990786a53ed71e27e11e/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs#L371
private readonly long _duration = Convert.ToInt64((double)duration.Ticks / 10);
#endif
public string Text => $"-analyzeduration {_duration}";
}

View file

@ -0,0 +1,3 @@
namespace FFMpegCore.Arguments.Formats;
public interface IDemuxerArgument : IArgument;

View file

@ -0,0 +1,3 @@
namespace FFMpegCore.Arguments.Formats;
public interface IMuxerArgument : IArgument;

View file

@ -0,0 +1,12 @@
namespace FFMpegCore.Arguments.Formats;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#Format-Options"/>
/// Set probing size in bytes, i.e. the size of the data to analyze to get stream information.
/// A higher value will enable detecting more information in case it is dispersed into the stream, but will increase latency.
/// Must be an integer not lesser than 32. It is 5000000 by default.
/// </summary>
public sealed class ProbeSizeArgument(long probesize) : IArgument
{
public string Text => $"-probesize {probesize}";
}

View file

@ -0,0 +1,10 @@
namespace FFMpegCore.Arguments.Formats;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#Format-Options" />
/// Use wallclock as timestamps if set to 1. Default is 0.
/// </summary>
public sealed class UseWallclockAsTimestampsArgument(bool value) : BaseBoolArgument(value)
{
protected override string ArgumentName => "use_wallclock_as_timestamps";
}

View file

@ -0,0 +1,3 @@
namespace FFMpegCore.Arguments.Formats.image2;
public interface IImage2Argument : IArgument;

View file

@ -0,0 +1,5 @@
namespace FFMpegCore.Arguments.Formats.image2;
public interface IImage2MuxerArgument :
IImage2Argument,
IDemuxerArgument;

View file

@ -0,0 +1,29 @@
namespace FFMpegCore.Arguments.Formats.image2;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#image2_002c-image2pipe" />
/// </summary>
public sealed class Image2ArgumentOptions
{
private readonly FFMpegArgumentOptions _options;
internal Image2ArgumentOptions(FFMpegArgumentOptions options)
{
_options = options;
}
/// <summary>
/// <inheritdoc cref="UpdateArgument"/>
/// </summary>
/// <param name="value"></param>
public Image2ArgumentOptions WithUpdate(bool value = true)
{
return WithArgument(new UpdateArgument(value));
}
public Image2ArgumentOptions WithArgument(IImage2Argument argument)
{
_options.WithArgument(argument);
return this;
}
}

View file

@ -0,0 +1,10 @@
namespace FFMpegCore.Arguments.Formats.image2;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#Options-27" />
/// If set to 1, the filename will always be interpreted as just a filename, not a pattern, and the corresponding file will be continuously overwritten with new images. Default value is 0.
/// </summary>
public sealed class UpdateArgument(bool value = false) : BaseBoolArgument(value), IImage2MuxerArgument
{
protected override string ArgumentName => "update";
}

View file

@ -0,0 +1,3 @@
namespace FFMpegCore.Arguments.Formats.segment;
public interface ISegmentArgument : IArgument;

View file

@ -0,0 +1,5 @@
namespace FFMpegCore.Arguments.Formats.segment;
public interface ISegmentMuxerArgument :
ISegmentArgument,
IDemuxerArgument;

View file

@ -0,0 +1,14 @@
namespace FFMpegCore.Arguments.Formats.segment;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment" />
/// Set minimum segment duration to time, the value must be a duration specification.
/// This prevents the muxer ending segments at a duration below this value.
/// Only effective with segment_time. Default value is "0".
/// </summary>
public sealed class MinSegmentDurationArgument(TimeSpan duration) : ISegmentMuxerArgument
{
private readonly long _duration = Convert.ToInt64(duration.TotalSeconds);
public string Text => $"-min_seg_duration {_duration}";
}

View file

@ -0,0 +1,13 @@
namespace FFMpegCore.Arguments.Formats.segment;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment" />
/// Reset timestamps at the beginning of each segment, so that each segment will start with near-zero timestamps.
/// It is meant to ease the playback of the generated segments.
/// May not work with some combinations of muxers/codecs.
/// It is set to <c>0</c> by default.
/// </summary>
public sealed class ResetTimestampsArgument(bool value) : BaseBoolArgument(value), ISegmentMuxerArgument
{
protected override string ArgumentName => "reset_timestamps";
}

View file

@ -0,0 +1,70 @@
namespace FFMpegCore.Arguments.Formats.segment;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment" />
/// </summary>
public sealed class SegmentArgumentOptions
{
private readonly FFMpegArgumentOptions _options;
internal SegmentArgumentOptions(FFMpegArgumentOptions options)
{
_options = options;
}
/// <summary>
/// <inheritdoc cref="MinSegmentDurationArgument"/>
/// </summary>
/// <param name="duration"></param>
/// <returns></returns>
public SegmentArgumentOptions WithMinimumSegmentDuration(TimeSpan duration)
{
return WithArgument(new MinSegmentDurationArgument(duration));
}
/// <summary>
/// <inheritdoc cref="SegmentTimeArgument"/>
/// </summary>
/// <param name="duration"></param>
/// <returns></returns>
public SegmentArgumentOptions WithSegmentTime(TimeSpan duration)
{
return WithArgument(new SegmentTimeArgument(duration));
}
/// <summary>
/// <inheritdoc cref="SegmentAtClocktimeArgument"/>
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public SegmentArgumentOptions WithSegmentAtClocktime(bool value = true)
{
return WithArgument(new SegmentAtClocktimeArgument(value));
}
/// <summary>
/// <inheritdoc cref="StrftimeArgument"/>
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public SegmentArgumentOptions WithStrftime(bool value = true)
{
return WithArgument(new StrftimeArgument(value));
}
/// <summary>
/// <inheritdoc cref="ResetTimestampsArgument"/>
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public SegmentArgumentOptions WithResetTimestamps(bool value = true)
{
return WithArgument(new ResetTimestampsArgument(value));
}
public SegmentArgumentOptions WithArgument(ISegmentArgument argument)
{
_options.WithArgument(argument);
return this;
}
}

View file

@ -0,0 +1,13 @@
namespace FFMpegCore.Arguments.Formats.segment;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment" />
/// If set to "1" split at regular clock time intervals starting from 00:00 oclock.
/// The time value specified in segment_time is used for setting the length of the splitting interval.
/// For example with segment_time set to "900" this makes it possible to create files at 12:00 oclock, 12:15, 12:30, etc.
/// Default value is "0".
/// </summary>
public sealed class SegmentAtClocktimeArgument(bool value) : BaseBoolArgument(value), ISegmentMuxerArgument
{
protected override string ArgumentName => "segment_atclocktime";
}

View file

@ -0,0 +1,13 @@
namespace FFMpegCore.Arguments.Formats.segment;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment" />
/// Set segment duration to time, the value must be a duration specification. Default value is "2". See also the segment_times option.
/// Note that splitting may not be accurate, unless you force the reference stream key-frames at the given time. See the introductory notice and the examples below.
/// </summary>
public sealed class SegmentTimeArgument(TimeSpan duration) : ISegmentMuxerArgument
{
private readonly long _duration = Convert.ToInt64(duration.TotalSeconds);
public string Text => $"-segment_time {_duration}";
}

View file

@ -0,0 +1,12 @@
namespace FFMpegCore.Arguments.Formats.segment;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment" />
/// Use the strftime function to define the name of the new segments to write.
/// If this is selected, the output segment name must contain a strftime function template.
/// Default value is 0.
/// </summary>
public sealed class StrftimeArgument(bool value) : BaseBoolArgument(value), ISegmentMuxerArgument
{
protected override string ArgumentName => "strftime";
}

View file

@ -0,0 +1,12 @@
namespace FFMpegCore.Arguments.GenericOptions;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg.html#Generic-options" />
/// Suppress printing banner.
/// All FFmpeg tools will normally show a copyright notice, build options and library versions.
/// This option can be used to suppress printing this information.
/// </summary>
public sealed class HideBanner : IArgument
{
public string Text => "-hide_banner";
}

View file

@ -0,0 +1,10 @@
namespace FFMpegCore.Arguments.MainOptions;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg.html#Main-options" />
/// Explicitly disable logging of encoding progress/statistics.
/// </summary>
public sealed class Stats(bool value) : BaseOptionArgument(value)
{
protected override string ArgumentName => "stats";
}

View file

@ -0,0 +1,3 @@
namespace FFMpegCore.Arguments.Protocols.Rtsp;
public interface IRtspArgument : IArgument;

View file

@ -0,0 +1,7 @@
using FFMpegCore.Arguments.Formats;
namespace FFMpegCore.Arguments.Protocols.Rtsp;
public interface IRtspDemuxerArgument :
IRtspArgument,
IDemuxerArgument;

View file

@ -0,0 +1,7 @@
using FFMpegCore.Arguments.Formats;
namespace FFMpegCore.Arguments.Protocols.Rtsp;
public interface IRtspMuxerArgument :
IRtspArgument,
IDemuxerArgument;

View file

@ -0,0 +1,32 @@
using FFMpegCore.Enums;
namespace FFMpegCore.Arguments.Protocols.Rtsp;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-protocols.html#rtsp" />
/// </summary>
public sealed class RtspArgumentOptions
{
private readonly FFMpegArgumentOptions _options;
internal RtspArgumentOptions(FFMpegArgumentOptions options)
{
_options = options;
}
/// <summary>
/// <inheritdoc cref="RtspTransportArgument"/>
/// </summary>
/// <param name="rtspTransportProtocol"></param>
/// <returns></returns>
public RtspArgumentOptions WithRtspTransport(RtspTransportProtocol rtspTransportProtocol)
{
return WithArgument(new RtspTransportArgument(rtspTransportProtocol));
}
public RtspArgumentOptions WithArgument(IRtspArgument argument)
{
_options.WithArgument(argument);
return this;
}
}

View file

@ -0,0 +1,12 @@
using FFMpegCore.Enums;
namespace FFMpegCore.Arguments.Protocols.Rtsp;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-protocols.html#rtsp" />
/// Set RTSP transport protocols.
/// </summary>
public sealed class RtspTransportArgument(RtspTransportProtocol protocol) : IRtspMuxerArgument, IRtspDemuxerArgument
{
public string Text => $"-rtsp_transport {protocol}";
}

View file

@ -0,0 +1,3 @@
namespace FFMpegCore.Arguments.VideoFilters.Vaapi;
public interface IVaapiVideoFilterArgument : IVideoFilterArgument;

View file

@ -0,0 +1,54 @@
using System.Drawing;
using FFMpegCore.Enums;
namespace FFMpegCore.Arguments.VideoFilters.Vaapi;
/// <summary>
/// <see href="https://www.ffmpeg.org/ffmpeg-filters.html#VAAPI-Video-Filters" />
/// </summary>
public sealed class VaapiVideoFilterOptions
{
private readonly VideoFilterOptions _options;
internal VaapiVideoFilterOptions(VideoFilterOptions options)
{
_options = options;
}
/// <summary>
/// <inheritdoc cref="VideoFilterScaleVaapiArgument"/>
/// </summary>
/// <param name="videosize"></param>
/// <returns></returns>
public VaapiVideoFilterOptions Scale(VideoSize videosize)
{
return WithArgument(new VideoFilterScaleVaapiArgument(videosize));
}
/// <summary>
/// <inheritdoc cref="VideoFilterScaleVaapiArgument"/>
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public VaapiVideoFilterOptions Scale(int width, int height)
{
return WithArgument(new VideoFilterScaleVaapiArgument(width, height));
}
/// <summary>
/// <inheritdoc cref="VideoFilterScaleVaapiArgument"/>
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
public VaapiVideoFilterOptions Scale(Size size)
{
return WithArgument(new VideoFilterScaleVaapiArgument(size));
}
public VaapiVideoFilterOptions WithArgument(IVaapiVideoFilterArgument argument)
{
_options.Arguments.Add(argument);
return this;
}
}

View file

@ -0,0 +1,29 @@
using System.Drawing;
using FFMpegCore.Enums;
namespace FFMpegCore.Arguments.VideoFilters.Vaapi;
/// <summary>
/// <see href="https://trac.ffmpeg.org/wiki/Hardware/VAAPI#SurfaceFormats" />
/// undocumented in: <see href="https://www.ffmpeg.org/ffmpeg-filters.html#VAAPI-Video-Filters" />
/// may refer to: <see href="https://www.ffmpeg.org/ffmpeg-filters.html#scale-1" />
/// </summary>
public sealed class VideoFilterScaleVaapiArgument : IVaapiVideoFilterArgument
{
private readonly Size? _size;
public VideoFilterScaleVaapiArgument(Size? size)
{
_size = size;
}
public VideoFilterScaleVaapiArgument(int width, int height) : this(new Size(width, height)) { }
public VideoFilterScaleVaapiArgument(VideoSize videosize)
{
_size = videosize == VideoSize.Original ? null : new Size(-1, (int)videosize);
}
public string Key => "scale_vaapi";
public string Value => _size == null ? string.Empty : $"{_size.Value.Width}:{_size.Value.Height}";
}

View file

@ -0,0 +1,26 @@
namespace FFMpegCore.Arguments.VideoFilters;
/// <summary>
/// <see href="https://www.ffmpeg.org/ffmpeg-filters.html#format-1" />
/// Convert the input video to one of the specified pixel formats.
/// Libavfilter will try to pick one that is suitable as input to the next filter.
/// </summary>
public sealed class VideoFilterFormatArgument : IVideoFilterArgument
{
public string Key => "format";
public string Value { get; }
private VideoFilterFormatArgument(string pixelFormat)
{
Value = $"pix_fmts={pixelFormat}";
}
public VideoFilterFormatArgument(params ReadOnlySpan<string?> pixelFormats)
#if NETSTANDARD2_1_OR_GREATER || NET8_OR_GREATER
: this(string.Join('|', pixelFormats))
#else
: this(string.Join("|", pixelFormats.ToArray()))
#endif
{
}
}

View file

@ -0,0 +1,11 @@
namespace FFMpegCore.Arguments.VideoFilters;
/// <summary>
/// <see href="https://www.ffmpeg.org/ffmpeg-filters.html#hwupload" />
/// Upload system memory frames to hardware surfaces.
/// </summary>
public sealed class VideoFilterHardwareUploadArgument : IVideoFilterArgument
{
public string Key => string.Empty;
public string Value => "hwupload";
}

View file

@ -1,4 +1,6 @@
using System.Drawing; using System.Drawing;
using FFMpegCore.Arguments.VideoFilters;
using FFMpegCore.Arguments.VideoFilters.Vaapi;
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Exceptions; using FFMpegCore.Exceptions;
@ -94,6 +96,31 @@ public class VideoFilterOptions
return WithArgument(new PadArgument(padOptions)); return WithArgument(new PadArgument(padOptions));
} }
/// <summary>
/// <inheritdoc cref="VideoFilterFormatArgument"/>
/// </summary>
/// <param name="pixelFormats"></param>
/// <returns></returns>
public VideoFilterOptions Format(params ReadOnlySpan<string?> pixelFormats)
{
return WithArgument(new VideoFilterFormatArgument(pixelFormats));
}
/// <summary>
/// <inheritdoc cref="VideoFilterHardwareUploadArgument"/>
/// </summary>
/// <returns></returns>
public VideoFilterOptions HardwareUpload()
{
return WithArgument(new VideoFilterHardwareUploadArgument());
}
public VideoFilterOptions WithVaapiVideoFilter(Action<VaapiVideoFilterOptions> setupAction)
{
setupAction(new VaapiVideoFilterOptions(this));
return this;
}
public VideoFilterOptions WithArgument(IVideoFilterArgument argument) public VideoFilterOptions WithArgument(IVideoFilterArgument argument)
{ {
Arguments.Add(argument); Arguments.Add(argument);

View file

@ -0,0 +1,12 @@
using FFMpegCore.Enums;
namespace FFMpegCore.Arguments.VideoOptions;
/// <summary>
/// <see href="https://trac.ffmpeg.org/wiki/Hardware/VAAPI#SurfaceFormats" />
/// undocumented in <see href="https://www.ffmpeg.org/ffmpeg.html#Advanced-Video-options" />
/// </summary>
public sealed class HardwareAccelerationOutputFormatArgument(HardwareAccelerationDevice hardwareAccelerationDevice) : IArgument
{
public string Text => $"-hwaccel_output_format {hardwareAccelerationDevice.ToString().ToLowerInvariant()}";
}

View file

@ -0,0 +1,14 @@
namespace FFMpegCore.Enums;
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg-protocols.html#rtsp" />
/// RTSP transport protocols
/// </summary>
public enum RtspTransportProtocol
{
udp,
tcp,
udp_multicast,
http,
https,
}

View file

@ -0,0 +1,42 @@
namespace FFMpegCore.Enums;
/// <summary>
/// <see href="https://www.ffmpeg.org/ffmpeg-codecs.html#VAAPI-encoders" />
/// </summary>
public enum VaapiRcMode
{
/// <summary>
/// Choose the mode automatically based on driver support and the other options. This is the default.
/// </summary>
auto,
/// <summary>
/// Constant-quality.
/// </summary>
CQP,
/// <summary>
/// Constant-bitrate.
/// </summary>
CBR,
/// <summary>
/// Variable-bitrate.
/// </summary>
VBR,
/// <summary>
/// Intelligent constant-quality.
/// </summary>
ICQ,
/// <summary>
/// Quality-defined variable-bitrate.
/// </summary>
QVBR,
/// <summary>
/// Average variable bitrate.
/// </summary>
AVBR,
}

View file

@ -1,5 +1,11 @@
using System.Drawing; using System.Drawing;
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Arguments.Codecs.Vaapi;
using FFMpegCore.Arguments.Codecs.Vaapi.h264Vaapi;
using FFMpegCore.Arguments.Formats;
using FFMpegCore.Arguments.Formats.image2;
using FFMpegCore.Arguments.Formats.segment;
using FFMpegCore.Arguments.Protocols.Rtsp;
using FFMpegCore.Enums; using FFMpegCore.Enums;
namespace FFMpegCore; namespace FFMpegCore;
@ -268,6 +274,70 @@ public class FFMpegArgumentOptions : FFMpegArgumentsBase
return WithArgument(new CopyCodecArgument()); return WithArgument(new CopyCodecArgument());
} }
/// <summary>
/// <inheritdoc cref="WithUseWallclockAsTimestamps"/>
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public FFMpegArgumentOptions WithUseWallclockAsTimestamps(bool value = true)
{
return WithArgument(new UseWallclockAsTimestampsArgument(value));
}
/// <summary>
/// <inheritdoc cref="AnalyzeDurationArgument"/>
/// </summary>
/// <param name="duration"></param>
/// <returns></returns>
public FFMpegArgumentOptions WithAnalyzeDuration(TimeSpan duration)
{
return WithArgument(new AnalyzeDurationArgument(duration));
}
/// <summary>
/// <inheritdoc cref="ProbeSizeArgument"/>
/// </summary>
/// <param name="probesizeInBytes"></param>
/// <returns></returns>
public FFMpegArgumentOptions WithProbeSize(long probesizeInBytes = 5000000)
{
return WithArgument(new ProbeSizeArgument(probesizeInBytes));
}
/// <summary>
/// <inheritdoc cref="VaapiRcModeArgument"/>
/// </summary>
/// <param name="rcMode"></param>
/// <returns></returns>
public FFMpegArgumentOptions WithVaapiRcMode(VaapiRcMode rcMode)
{
return WithArgument(new VaapiRcModeArgument(rcMode));
}
public FFMpegArgumentOptions WithImage2Options(Action<Image2ArgumentOptions> setupAction)
{
setupAction(new Image2ArgumentOptions(this));
return this;
}
public FFMpegArgumentOptions WithSegmentOptions(Action<SegmentArgumentOptions> setupAction)
{
setupAction(new SegmentArgumentOptions(this));
return this;
}
public FFMpegArgumentOptions WithH264VaapiOptions(Action<H264VaapiArgumentOptions> setupAction)
{
setupAction(new H264VaapiArgumentOptions(this));
return this;
}
public FFMpegArgumentOptions WithRtspProtocolOptions(Action<RtspArgumentOptions> setupAction)
{
setupAction(new RtspArgumentOptions(this));
return this;
}
public FFMpegArgumentOptions WithArgument(IArgument argument) public FFMpegArgumentOptions WithArgument(IArgument argument)
{ {
Arguments.Add(argument); Arguments.Add(argument);

View file

@ -1,4 +1,8 @@
using FFMpegCore.Arguments; using FFMpegCore.Arguments;
using FFMpegCore.Arguments.GenericOptions;
using FFMpegCore.Arguments.MainOptions;
using FFMpegCore.Arguments.VideoOptions;
using FFMpegCore.Enums;
namespace FFMpegCore; namespace FFMpegCore;
@ -11,6 +15,39 @@ public sealed class FFMpegGlobalArguments : FFMpegArgumentsBase
return WithArgument(new VerbosityLevelArgument(verbosityLevel)); return WithArgument(new VerbosityLevelArgument(verbosityLevel));
} }
/// <summary>
/// <see href="https://trac.ffmpeg.org/wiki/Hardware/VAAPI#SurfaceFormats" />
/// undocumented in <see href="https://www.ffmpeg.org/ffmpeg.html#Advanced-Video-options" />
/// </summary>
/// <param name="device"></param>
/// <returns></returns>
public FFMpegGlobalArguments WithHardwareAccelerationOutputFormat(HardwareAccelerationDevice device)
{
return WithArgument(new HardwareAccelerationOutputFormatArgument(device));
}
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg.html#Generic-options" />
/// Suppress printing banner.
/// All FFmpeg tools will normally show a copyright notice, build options and library versions.
/// This option can be used to suppress printing this information.
/// </summary>
/// <returns></returns>
public FFMpegGlobalArguments WithHideBanner()
{
return WithArgument(new HideBanner());
}
/// <summary>
/// <see href="https://ffmpeg.org/ffmpeg.html#Main-options" />
/// Explicitly disable logging of encoding progress/statistics.
/// </summary>
/// <returns></returns>
public FFMpegGlobalArguments WithNoStats()
{
return WithArgument(new Stats(false));
}
public FFMpegGlobalArguments WithArgument(IArgument argument) public FFMpegGlobalArguments WithArgument(IArgument argument)
{ {
Arguments.Add(argument); Arguments.Add(argument);