Skip to content

Lib vlc options builder #387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: 3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 39 additions & 29 deletions src/LibVLCSharp/Shared/LibVLC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,38 @@ internal struct Native
#endif
}

/// <summary>
/// Create and initialize a libvlc instance using a <see cref="LibVLCOptions"/> object.
/// This constructor allows for strongly-typed, flexible configuration of libvlc command-line parameters,
/// including platform-specific defaults, logging verbosity, resamplers, caching, and rendering settings.
/// LibVLC may create threads. Therefore, any thread-unsafe process
/// initialization must be performed before calling libvlc_new(). In particular:
/// <para>- setlocale() and textdomain(),</para>
/// <para>- setenv(), unsetenv(), and putenv(),</para>
/// <para>- On Microsoft Windows, SetErrorMode().</para>
/// On POSIX systems, certain signals must be handled properly (e.g., SIGCHLD must not be ignored).
/// <para/> This will throw a <see cref="VLCException"/> if the native libvlc libraries cannot be found or loaded.
/// <para/> It may also throw a <see cref="VLCException"/> if the LibVLC and LibVLCSharp major versions do not match.
/// See https://code.videolan.org/videolan/LibVLCSharp/-/blob/master/docs/versioning.md for more info about the versioning strategy.
/// <example>
/// <code>
/// var options = new LibVLCOptionsBuilder()
/// .EnableDebugLogs()
/// .SetAudioResampler("soxr")
/// .Build();
///
/// using var libvlc = new LibVLC(options);
/// </code>
/// </example>
/// </summary>
/// <param name="options">An instance of <see cref="LibVLCOptions"/> containing libvlc configuration parameters.</param>

public LibVLC(LibVLCOptions options)
: base(() => MarshalUtils.CreateWithOptions(options.Options, Native.LibVLCNew), Native.LibVLCRelease)
{
_gcHandle = GCHandle.Alloc(this);
}

/// <summary>
/// Create and initialize a libvlc instance.
/// This functions accept a list of &quot;command line&quot; arguments similar to the
Expand Down Expand Up @@ -239,10 +271,8 @@ internal struct Native
/// <param name="options">list of arguments, in the form "--option=value"</param>
/// <returns>the libvlc instance or NULL in case of error</returns>
public LibVLC(params string[] options)
: base(() => MarshalUtils.CreateWithOptions(PatchOptions(options), Native.LibVLCNew), Native.LibVLCRelease)
{
_gcHandle = GCHandle.Alloc(this);
}
: this(new LibVLCOptionsBuilder().AddOptions(options).Build()) { }


/// <summary>
/// Create and initialize a libvlc instance.
Expand Down Expand Up @@ -286,31 +316,11 @@ public LibVLC(params string[] options)
/// <param name="enableDebugLogs">enable verbose debug logs</param>
/// <param name="options">list of arguments (should be NULL)</param>
public LibVLC(bool enableDebugLogs, params string[] options)
: base(() => MarshalUtils.CreateWithOptions(PatchOptions(options, enableDebugLogs), Native.LibVLCNew), Native.LibVLCRelease)
{
_gcHandle = GCHandle.Alloc(this);
}

/// <summary>
/// Make dirty hacks to include necessary defaults on some platforms.
/// </summary>
/// <param name="options">The options given by the user</param>
/// <param name="enableDebugLogs">enable debug logs</param>
/// <returns>The patched options</returns>
static string[] PatchOptions(string[] options, bool enableDebugLogs = false)
{
if(enableDebugLogs)
{
options = options.Concat(new[] { "--verbose=2" }).ToArray();
}
#if UWP
return options.Concat(new[] {"--aout=winstore", "--audio-resampler=speex_resampler"}).ToArray();
#elif ANDROID
return options.Concat(new[] {"--audio-resampler=soxr"}).ToArray();
#else
return options;
#endif
}
: this(new LibVLCOptionsBuilder()
.AddOptions(options)
.EnableDebugLogs()
.Build())
{ }

/// <summary>
/// Dispose of this libvlc instance
Expand Down
219 changes: 219 additions & 0 deletions src/LibVLCSharp/Shared/LibVLCOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace LibVLCSharp.Shared
{
/// <summary>
/// Fluent builder for constructing LibVLC options.
/// Mirrors many common libvlc command line parameters.
/// See https://wiki.videolan.org/VLC_command-line_help for more details about available parameters.
/// OPTIONS ARE PROVIDED AS-IS, WITHOUT ANY GUARANTEE OF FORWARD/BACKWARD COMPATIBILITY.
/// </summary>
public class LibVLCOptionsBuilder
{
private readonly List<string> _options = new();
private bool _includeDebugLogs = false;

#if UWP || ANDROID
private bool _audioResamplerOverridden = false;
#endif

/// <summary>
/// Enables verbose debug logging by adding "--verbose=2".
/// </summary>
public LibVLCOptionsBuilder EnableDebugLogs()
{
_includeDebugLogs = true;
return this;
}

/// <summary>
/// Sets the verbosity level of the log output.
/// </summary>
/// <param name="level">Verbosity level (e.g. 0=errors only, 2=debug).</param>
public LibVLCOptionsBuilder SetVerbosity(int level)
{
_options.Add($"--verbose={level}");
return this;
}

/// <summary>
/// Sets the audio resampler module.
/// </summary>
/// <param name="resampler">The name of the resampler (e.g. speex_resampler, soxr).</param>
public LibVLCOptionsBuilder SetAudioResampler(string resampler)
{
_options.Add($"--audio-resampler={resampler}");
#if UWP || ANDROID
_audioResamplerOverridden = true;
#endif
return this;
}

#if UWP || ANDROID
/// <summary>
/// Disables the default resampler for the current platform.
/// </summary>
public LibVLCOptionsBuilder DisableDefaultResampler()
{
_audioResamplerOverridden = true;
return this;
}
#endif

/// <summary>
/// Sets the audio output module (e.g. "--aout=directsound").
/// </summary>
/// <param name="output">The audio output module name.</param>
public LibVLCOptionsBuilder SetAudioOutput(string output)
{
_options.Add($"--aout={output}");
return this;
}

/// <summary>
/// Disables video output.
/// </summary>
public LibVLCOptionsBuilder DisableVideo()
{
_options.Add("--no-video");
return this;
}

/// <summary>
/// Disables audio output.
/// </summary>
public LibVLCOptionsBuilder DisableAudio()
{
_options.Add("--no-audio");
return this;
}

/// <summary>
/// Sets the network caching value in milliseconds.
/// </summary>
/// <param name="milliseconds">Caching delay in ms (e.g. 300).</param>
public LibVLCOptionsBuilder SetNetworkCaching(int milliseconds)
{
_options.Add($"--network-caching={milliseconds}");
return this;
}

/// <summary>
/// Sets the file caching value in milliseconds.
/// </summary>
/// <param name="milliseconds">Caching delay in ms (e.g. 300).</param>
public LibVLCOptionsBuilder SetFileCaching(int milliseconds)
{
_options.Add($"--file-caching={milliseconds}");
return this;
}

/// <summary>
/// Enables hardware-accelerated decoding using the specified method.
/// </summary>
/// <param name="method">Decoding method (e.g. dxva2, vaapi, d3d11va).</param>
public LibVLCOptionsBuilder EnableHardwareDecoding(string method)
{
_options.Add($"--avcodec-hw={method}");
return this;
}

/// <summary>
/// Enables dropping of late video frames.
/// </summary>
public LibVLCOptionsBuilder DropLateFrames()
{
_options.Add("--drop-late-frames");
return this;
}

/// <summary>
/// Enables frame skipping.
/// </summary>
public LibVLCOptionsBuilder SkipFrames()
{
_options.Add("--skip-frames");
return this;
}

/// <summary>
/// Enables or disables use of Xlib.
/// </summary>
/// <param name="enabled">True to enable Xlib, false to disable.</param>
public LibVLCOptionsBuilder UseXlib(bool enabled)
{
_options.Add(enabled ? "--xlib" : "--no-xlib");
return this;
}

/// <summary>
/// Adds a custom libvlc option.
/// </summary>
/// <param name="option">The command-line option to add.</param>
public LibVLCOptionsBuilder AddOption(string option)
{
_options.Add(option);
return this;
}

/// <summary>
/// Adds multiple custom libvlc options.
/// </summary>
/// <param name="options">The options to add.</param>
public LibVLCOptionsBuilder AddOptions(IEnumerable<string> options)
{
_options.AddRange(options);
return this;
}


/// <summary>
/// Builds the final LibVLCOptions object including any platform-specific defaults.
/// </summary>
public LibVLCOptions Build()
{
var finalOptions = _options;

if (_includeDebugLogs)
finalOptions.Add("--verbose=2");

#if UWP
finalOptions.Add("--aout=winstore");
if (!_audioResamplerOverridden)
finalOptions.Add("--audio-resampler=speex_resampler");
#elif ANDROID
if (!_audioResamplerOverridden)
finalOptions.Add("--audio-resampler=soxr");
#endif

return new LibVLCOptions(finalOptions);
}
}

/// <summary>
/// Represents the finalized LibVLC options as an array of command-line arguments.
/// </summary>
public class LibVLCOptions
{
/// <summary>
/// Gets the collection of command-line arguments.
/// </summary>
public string[] Options { get; }

/// <summary>
/// Initializes a new instance of LibVLCOptions with the specified options.
/// </summary>
/// <param name="options">The options to include.</param>
public LibVLCOptions(IEnumerable<string>? options)
{
Options = options?.ToArray() ?? new string[0];
}

/// <summary>
/// Gets an empty LibVLCOptions instance.
/// </summary>
public static LibVLCOptions Empty => new LibVLCOptions(new string[0]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't called anywhere, can remove.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I understand your point I would recommend to keep this. If someone wants to instantiate LibVLC class without any parameters, he can just call LibVLCOptions.Empty instead of having to construct new empty options.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone wants to instantiate LibVLC class without any parameters

Then they can just write new LibVLC();

}
}