static void OnLogInternal(IntPtr data, LogLevel level, IntPtr ctx, IntPtr format, IntPtr args) { if (data == IntPtr.Zero) { return; } var gch = GCHandle.FromIntPtr(data); if (!gch.IsAllocated || !(gch.Target is LibVLC libvlc) || libvlc.IsDisposed) { return; } try { var message = MarshalUtils.GetLogMessage(format, args); GetLogContext(ctx, out var module, out var file, out var line); #if NET40 Task.Factory.StartNew(() => libvlc._log?.Invoke(null, new LogEventArgs(level, message, module, file, line))); #else Task.Run(() => libvlc._log?.Invoke(null, new LogEventArgs(level, message, module, file, line))); #endif } // Silently catching OOM exceptions and others as this is not critical if it fails catch { } }
public AudioOutputDevice[] AudioOutputDevices(string audioOutputName) { return(MarshalUtils.Retrieve(() => Native.LibVLCAudioOutputDeviceListGet(NativeReference, audioOutputName), Marshal.PtrToStructure <AudioOutputDevice.Internal>, s => AudioOutputDevice.__CreateInstance(s), device => device.Next, Native.LibVLCAudioOutputDeviceListRelease)); }
/// <summary> /// Post a login answer. /// After this call, the instance won't be valid anymore /// </summary> /// <param name="username">valid non-empty string</param> /// <param name="password">valid string</param> /// <param name="store">if true stores the credentials</param> /// <returns></returns> public bool PostLogin(string username, string password, bool store) { if (_id == IntPtr.Zero) { throw new VLCException("Calling method on dismissed Dialog instance"); } if (username == null) { username = string.Empty; } if (password == null) { password = string.Empty; } var usernamePtr = username.ToUtf8(); var passwordPtr = password.ToUtf8(); var result = MarshalUtils.PerformInteropAndFree( () => Native.LibVLCDialogPostLogin(_id, usernamePtr, passwordPtr, store), usernamePtr, passwordPtr) == 0; _id = IntPtr.Zero; return(result); }
/// <summary> /// Code taken from Vlc.DotNet /// </summary> /// <param name="data"></param> /// <param name="level"></param> /// <param name="ctx"></param> /// <param name="format"></param> /// <param name="args"></param> void OnLogInternal(IntPtr data, LogLevel level, IntPtr ctx, string format, IntPtr args) { if (_log == null) { return; } // Original source for va_list handling: https://stackoverflow.com/a/37629480/2663813 var byteLength = MarshalUtils.Vscprintf(format, args) + 1; var utf8Buffer = Marshal.AllocHGlobal(byteLength); string formattedDecodedMessage; try { MarshalUtils.Vsprintf(utf8Buffer, format, args); formattedDecodedMessage = (string)Utf8StringMarshaler.GetInstance().MarshalNativeToManaged(utf8Buffer); } finally { Marshal.FreeHGlobal(utf8Buffer); } GetLogContext(ctx, out var module, out var file, out var line); // Do the notification on another thread, so that VLC is not interrupted by the logging #if NET40 Task.Factory.StartNew(() => _log?.Invoke(NativeReference, new LogEventArgs(level, formattedDecodedMessage, module, file, line))); #else Task.Run(() => _log?.Invoke(NativeReference, new LogEventArgs(level, formattedDecodedMessage, module, file, line))); #endif }
/// <summary> /// <para>Sets the application name. LibVLC passes this as the user agent string</para> /// <para>when a protocol requires it.</para> /// </summary> /// <param name="name">human-readable application name, e.g. "FooBar player 1.2.3"</param> /// <param name="http">HTTP User Agent, e.g. "FooBar/1.2.3 Python/2.6.0"</param> /// <remarks>LibVLC 1.1.1 or later</remarks> public void SetUserAgent(string name, string http) { var nameUtf8 = name.ToUtf8(); var httpUtf8 = http.ToUtf8(); MarshalUtils.PerformInteropAndFree(() => Native.LibVLCSetUserAgent(NativeReference, nameUtf8, httpUtf8), nameUtf8, httpUtf8); }
static void OnLogInternal(IntPtr data, LogLevel level, IntPtr ctx, IntPtr format, IntPtr args) { if (_log == null) { return; } IntPtr buffer = IntPtr.Zero; try { buffer = Marshal.AllocHGlobal(2048 + 1); var count = MarshalUtils.vsprintf(buffer, format, args); if (count > -1) { var message = buffer.FromUtf8(); GetLogContext(ctx, out var module, out var file, out var line); #if NET40 Task.Factory.StartNew(() => _log?.Invoke(null, new LogEventArgs(level, message, module, file, line))); #else Task.Run(() => _log?.Invoke(null, new LogEventArgs(level, message, module, file, line))); #endif } } finally { if (buffer != IntPtr.Zero) { Marshal.FreeHGlobal(buffer); } } }
/// <summary> /// Media discoverer constructor /// </summary> /// <param name="libVLC">libvlc instance this will be attached to</param> /// <param name="name">name from one of LibVLC.MediaDiscoverers</param> public MediaDiscoverer(LibVLC libVLC, string name) : base(() => { var nameUtf8 = name.ToUtf8(); return(MarshalUtils.PerformInteropAndFree(() => Native.LibVLCMediaDiscovererNew(libVLC.NativeReference, nameUtf8), nameUtf8)); }, Native.LibVLCMediaDiscovererRelease) { }
/// <summary> /// Close log file handle /// </summary> /// <returns>true if no file to close or close operation successful, false otherwise</returns> public bool CloseLogFile() { if (_logFileHandle == IntPtr.Zero) { return(true); } return(MarshalUtils.Close(_logFileHandle)); }
/// <summary> /// <para>Sets some meta-information about the application.</para> /// <para>See also libvlc_set_user_agent().</para> /// </summary> /// <param name="id">Java-style application identifier, e.g. "com.acme.foobar"</param> /// <param name="version">application version numbers, e.g. "1.2.3"</param> /// <param name="icon">application icon name, e.g. "foobar"</param> /// <remarks>LibVLC 2.1.0 or later.</remarks> public void SetAppId(string?id, string?version, string?icon) { var idUtf8 = id.ToUtf8(); var versionUtf8 = version.ToUtf8(); var iconUtf8 = icon.ToUtf8(); MarshalUtils.PerformInteropAndFree(() => Native.LibVLCSetAppId(NativeReference, idUtf8, versionUtf8, iconUtf8), idUtf8, versionUtf8, iconUtf8); }
IntPtr NativeFilePtr(string filename) { var result = MarshalUtils.Open(filename, out var filePtr); if (!result) { throw new VLCException("Could not get FILE * for log_set_file"); } return(filePtr); }
static int ReadMediaCallback(IntPtr opaque, IntPtr buf, uint len) { var input = MarshalUtils.GetInstance <MediaInput>(opaque); if (input == null) { return(-1); } return(input.Read(buf, len)); }
/// <summary> /// Create a media from a MediaInput /// requires libvlc 3.0 or higher /// </summary> /// <param name="libVLC">the libvlc instance</param> /// <param name="input">the media to be used by libvlc. LibVLCSharp will NOT dispose or close it. /// Use <see cref="StreamMediaInput"/> or implement your own.</param> /// <param name="options">the libvlc options</param> public Media(LibVLC libVLC, MediaInput input, params string[] options) : base(() => CtorFromInput(libVLC, input), Native.LibVLCMediaRelease) { foreach (var option in options) { var optionUtf8 = option.ToUtf8(); MarshalUtils.PerformInteropAndFree(() => Native.LibVLCMediaAddOption(NativeReference, optionUtf8), optionUtf8); } }
static int SeekMediaCallback(IntPtr opaque, ulong offset) { var input = MarshalUtils.GetInstance <MediaInput>(opaque); if (input == null) { return(-1); } return(input.Seek(offset) ? 0 : -1); }
/// <summary>Add an option to the media with configurable flags.</summary> /// <param name="option">the media option</param> /// <param name="flags">the flags for this option</param> /// <remarks> /// <para>This option will be used to determine how the media_player will</para> /// <para>read the media. This allows to use VLC's advanced</para> /// <para>reading/streaming options on a per-media basis.</para> /// <para>The options are detailed in vlc --long-help, for instance</para> /// <para>"--sout-all". Note that all options are not usable on medias:</para> /// <para>specifically, due to architectural issues, video-related options</para> /// <para>such as text renderer options cannot be set on a single media. They</para> /// <para>must be set on the whole libvlc instance instead.</para> /// </remarks> public void AddOptionFlag(string option, uint flags) { if (string.IsNullOrEmpty(option)) { throw new ArgumentNullException(nameof(option)); } var optionUtf8 = option.ToUtf8(); MarshalUtils.PerformInteropAndFree(() => Native.LibVLCMediaAddOptionFlag(NativeReference, optionUtf8, flags), optionUtf8); }
/// <summary> /// <para>Set the meta of the media (this function will not save the meta, call</para> /// <para>libvlc_media_save_meta in order to save the meta)</para> /// </summary> /// <param name="metadataType">the <see cref="MetadataType"/> to write</param> /// <param name="metaValue">the media's meta</param> public void SetMeta(MetadataType metadataType, string metaValue) { if (string.IsNullOrEmpty(metaValue)) { throw new ArgumentNullException(metaValue); } var metaUtf8 = metaValue.ToUtf8(); MarshalUtils.PerformInteropAndFree(() => Native.LibVLCMediaSetMeta(NativeReference, metadataType, metaUtf8), metaUtf8); }
/// <summary>Gets a list of audio output devices for a given audio output module,</summary> /// <param name="audioOutputName"> /// <para>audio output name</para> /// <para>(as returned by libvlc_audio_output_list_get())</para> /// </param> /// <returns> /// <para>A NULL-terminated linked list of potential audio output devices.</para> /// <para>It must be freed with libvlc_audio_output_device_list_release()</para> /// </returns> /// <remarks> /// <para>libvlc_audio_output_device_set().</para> /// <para>Not all audio outputs support this. In particular, an empty (NULL)</para> /// <para>list of devices doesnotimply that the specified audio output does</para> /// <para>not work.</para> /// <para>The list might not be exhaustive.</para> /// <para>Some audio output devices in the list might not actually work in</para> /// <para>some circumstances. By default, it is recommended to not specify any</para> /// <para>explicit audio device.</para> /// <para>LibVLC 2.1.0 or later.</para> /// </remarks> public AudioOutputDevice[] AudioOutputDevices(string audioOutputName) => MarshalUtils.Retrieve(() => { var audioOutputNameUtf8 = audioOutputName.ToUtf8(); return(MarshalUtils.PerformInteropAndFree(() => Native.LibVLCAudioOutputDeviceListGet(NativeReference, audioOutputNameUtf8), audioOutputNameUtf8)); }, MarshalUtils.PtrToStructure <AudioOutputDeviceStructure>, s => s.Build(), device => device.Next, Native.LibVLCAudioOutputDeviceListRelease);
static int OpenMediaCallback(IntPtr opaque, ref IntPtr data, out ulong size) { data = opaque; var input = MarshalUtils.GetInstance <MediaInput>(opaque); if (input == null) { size = 0UL; return(-1); } return(input.Open(out size) ? 0 : -1); }
Media(Func <IntPtr> create, Action <IntPtr> release, params string[] options) : base(create, release) { if (options == null) { return; } foreach (var optionUtf8 in options.ToUtf8()) { if (optionUtf8 != IntPtr.Zero) { MarshalUtils.PerformInteropAndFree(() => Native.LibVLCMediaAddOption(NativeReference, optionUtf8), optionUtf8); } } }
/// <summary> /// Create a new renderer discoverer with a LibVLC and protocol name depending on host platform /// </summary> /// <param name="libVLC">libvlc instance this will be connected to</param> /// <param name="name"> /// The service discovery protocol name depending on platform. Use <see cref="LibVLC.RendererList"/> to find the one for your platform, /// or let libvlcsharp find it for you /// </param> public RendererDiscoverer(LibVLC libVLC, string?name = null) : base(() => { if (string.IsNullOrEmpty(name)) { #if APPLE name = Bonjour; #else name = Mdns; #endif } var nameUtf8 = name.ToUtf8(); return(MarshalUtils.PerformInteropAndFree(() => Native.LibVLCRendererDiscovererNew(libVLC.NativeReference, nameUtf8), nameUtf8)); }, Native.LibVLCRendererDiscovererRelease) { }
/// <summary> /// <para>Create and initialize a libvlc instance.</para> /// <para>This functions accept a list of "command line" arguments similar to the</para> /// <para>main(). These arguments affect the LibVLC instance default configuration.</para> /// </summary> /// <param name="argc">the number of arguments (should be 0)</param> /// <param name="args">list of arguments (should be NULL)</param> /// <returns>the libvlc instance or NULL in case of error</returns> /// <remarks> /// <para>LibVLC may create threads. Therefore, any thread-unsafe process</para> /// <para>initialization must be performed before calling libvlc_new(). In particular</para> /// <para>and where applicable:</para> /// <para>- setlocale() and textdomain(),</para> /// <para>- setenv(), unsetenv() and putenv(),</para> /// <para>- with the X11 display system, XInitThreads()</para> /// <para>(see also libvlc_media_player_set_xwindow()) and</para> /// <para>- on Microsoft Windows, SetErrorMode().</para> /// <para>- sigprocmask() shall never be invoked; pthread_sigmask() can be used.</para> /// <para>On POSIX systems, the SIGCHLD signalmust notbe ignored, i.e. the</para> /// <para>signal handler must set to SIG_DFL or a function pointer, not SIG_IGN.</para> /// <para>Also while LibVLC is active, the wait() function shall not be called, and</para> /// <para>any call to waitpid() shall use a strictly positive value for the first</para> /// <para>parameter (i.e. the PID). Failure to follow those rules may lead to a</para> /// <para>deadlock or a busy loop.</para> /// <para>Also on POSIX systems, it is recommended that the SIGPIPE signal be blocked,</para> /// <para>even if it is not, in principles, necessary, e.g.:</para> /// <para>On Microsoft Windows Vista/2008, the process error mode</para> /// <para>SEM_FAILCRITICALERRORS flagmustbe set before using LibVLC.</para> /// <para>On later versions, that is optional and unnecessary.</para> /// <para>Also on Microsoft Windows (Vista and any later version), setting the default</para> /// <para>DLL directories to SYSTEM32 exclusively is strongly recommended for</para> /// <para>security reasons:</para> /// <para>Arguments are meant to be passed from the command line to LibVLC, just like</para> /// <para>VLC media player does. The list of valid arguments depends on the LibVLC</para> /// <para>version, the operating system and platform, and set of available LibVLC</para> /// <para>plugins. Invalid or unsupported arguments will cause the function to fail</para> /// <para>(i.e. return NULL). Also, some arguments may alter the behaviour or</para> /// <para>otherwise interfere with other LibVLC functions.</para> /// <para>There is absolutely no warranty or promise of forward, backward and</para> /// <para>cross-platform compatibility with regards to libvlc_new() arguments.</para> /// <para>We recommend that you do not use them, other than when debugging.</para> /// </remarks> public LibVLC(string[] args = null) : base(() => { var utf8Args = default(IntPtr[]); try { utf8Args = MarshalUtils.ToUtf8(args); return(Native.LibVLCNew(utf8Args.Length, utf8Args)); } finally { foreach (var arg in utf8Args) { if (arg != IntPtr.Zero) { Marshal.FreeHGlobal(arg); } } } }, Native.LibVLCRelease) { __ownsNativeInstance = true; NativeToManagedMap[NativeReference] = this; }
/// <summary> /// Try to start a user interface for the libvlc instance. /// </summary> /// <param name="name">interface name, or null for default</param> /// <returns>True if successful, false otherwise</returns> public bool AddInterface(string?name) { var namePtr = name.ToUtf8(); return(MarshalUtils.PerformInteropAndFree(() => Native.LibVLCAddInterface(NativeReference, namePtr) == 0, namePtr)); }
internal protected LibVLCEvent RetrieveEvent(IntPtr eventPtr) => MarshalUtils.PtrToStructure <LibVLCEvent>(eventPtr);
/// <summary>Add a slave to the current media.</summary> /// <param name="type">subtitle or audio</param> /// <param name="priority">from 0 (low priority) to 4 (high priority)</param> /// <param name="uri">Uri of the slave (should contain a valid scheme).</param> /// <returns>0 on success, -1 on error.</returns> /// <remarks> /// <para>A slave is an external input source that may contains an additional subtitle</para> /// <para>track (like a .srt) or an additional audio track (like a .ac3).</para> /// <para>This function must be called before the media is parsed (via</para> /// <para>libvlc_media_parse_with_options()) or before the media is played (via</para> /// <para>libvlc_media_player_play())</para> /// <para>LibVLC 3.0.0 and later.</para> /// </remarks> public bool AddSlave(MediaSlaveType type, uint priority, string uri) { var uriUtf8 = uri.ToUtf8(); return(MarshalUtils.PerformInteropAndFree(() => Native.LibVLCMediaAddSlaves(NativeReference, type, priority, uriUtf8) != 0, uriUtf8)); }
/// <summary>Gets a list of audio output devices for a given audio output module,</summary> /// <param name="audioOutputName"> /// <para>audio output name</para> /// <para>(as returned by libvlc_audio_output_list_get())</para> /// </param> /// <returns> /// <para>A NULL-terminated linked list of potential audio output devices.</para> /// <para>It must be freed with libvlc_audio_output_device_list_release()</para> /// </returns> /// <remarks> /// <para>libvlc_audio_output_device_set().</para> /// <para>Not all audio outputs support this. In particular, an empty (NULL)</para> /// <para>list of devices doesnotimply that the specified audio output does</para> /// <para>not work.</para> /// <para>The list might not be exhaustive.</para> /// <para>Some audio output devices in the list might not actually work in</para> /// <para>some circumstances. By default, it is recommended to not specify any</para> /// <para>explicit audio device.</para> /// <para>LibVLC 2.1.0 or later.</para> /// </remarks> public AudioOutputDevice[] AudioOutputDevices(string audioOutputName) => MarshalUtils.Retrieve(() => Native.LibVLCAudioOutputDeviceListGet(NativeReference, Utf8StringMarshaler.GetInstance().MarshalManagedToNative(audioOutputName)), MarshalUtils.PtrToStructure <AudioOutputDeviceStructure>, s => s.Build(), device => device.Next, Native.LibVLCAudioOutputDeviceListRelease);
static void CloseMediaCallback(IntPtr opaque) { var input = MarshalUtils.GetInstance <MediaInput>(opaque); input?.Close(); }
/// <summary> /// Create and initialize a libvlc instance. /// This functions accept a list of "command line" arguments similar to the /// main(). These arguments affect the LibVLC instance default configuration. /// LibVLC may create threads. Therefore, any thread-unsafe process /// initialization must be performed before calling libvlc_new(). In particular /// and where applicable: /// <para>- setlocale() and textdomain(),</para> /// <para>- setenv(), unsetenv() and putenv(),</para> /// <para>- with the X11 display system, XInitThreads()</para> /// (see also libvlc_media_player_set_xwindow()) and /// <para>- on Microsoft Windows, SetErrorMode().</para> /// <para>- sigprocmask() shall never be invoked; pthread_sigmask() can be used.</para> /// On POSIX systems, the SIGCHLD signalmust notbe ignored, i.e. the /// signal handler must set to SIG_DFL or a function pointer, not SIG_IGN. /// Also while LibVLC is active, the wait() function shall not be called, and /// any call to waitpid() shall use a strictly positive value for the first /// parameter (i.e. the PID). Failure to follow those rules may lead to a /// deadlock or a busy loop. /// Also on POSIX systems, it is recommended that the SIGPIPE signal be blocked, /// even if it is not, in principles, necessary, e.g.: /// On Microsoft Windows Vista/2008, the process error mode /// SEM_FAILCRITICALERRORS flagmustbe set before using LibVLC. /// On later versions, that is optional and unnecessary. /// Also on Microsoft Windows (Vista and any later version), setting the default /// DLL directories to SYSTEM32 exclusively is strongly recommended for /// security reasons: /// Arguments are meant to be passed from the command line to LibVLC, just like /// VLC media player does. The list of valid arguments depends on the LibVLC /// version, the operating system and platform, and set of available LibVLC /// plugins. Invalid or unsupported arguments will cause the function to fail /// (i.e. return NULL). Also, some arguments may alter the behaviour or /// otherwise interfere with other LibVLC functions. /// There is absolutely no warranty or promise of forward, backward and /// cross-platform compatibility with regards to libvlc_new() arguments. /// We recommend that you do not use them, other than when debugging. /// </summary> /// <param name="options">list of arguments (should be NULL)</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) { }
private static void EventCallback(IntPtr evt, IntPtr userData) { var eventManager = MarshalUtils.GetInstance <EventTypeManager>(userData); eventManager?.EventHandler(evt); }
/// <summary> /// Create and initialize a libvlc instance. /// This functions accept a list of "command line" arguments similar to the /// main(). These arguments affect the LibVLC instance default configuration. /// LibVLC may create threads. Therefore, any thread-unsafe process /// initialization must be performed before calling libvlc_new(). In particular /// and where applicable: /// <para>- setlocale() and textdomain(),</para> /// <para>- setenv(), unsetenv() and putenv(),</para> /// <para>- with the X11 display system, XInitThreads()</para> /// (see also libvlc_media_player_set_xwindow()) and /// <para>- on Microsoft Windows, SetErrorMode().</para> /// <para>- sigprocmask() shall never be invoked; pthread_sigmask() can be used.</para> /// On POSIX systems, the SIGCHLD signalmust notbe ignored, i.e. the /// signal handler must set to SIG_DFL or a function pointer, not SIG_IGN. /// Also while LibVLC is active, the wait() function shall not be called, and /// any call to waitpid() shall use a strictly positive value for the first /// parameter (i.e. the PID). Failure to follow those rules may lead to a /// deadlock or a busy loop. /// Also on POSIX systems, it is recommended that the SIGPIPE signal be blocked, /// even if it is not, in principles, necessary, e.g.: /// On Microsoft Windows Vista/2008, the process error mode /// SEM_FAILCRITICALERRORS flagmustbe set before using LibVLC. /// On later versions, that is optional and unnecessary. /// Also on Microsoft Windows (Vista and any later version), setting the default /// DLL directories to SYSTEM32 exclusively is strongly recommended for /// security reasons: /// Arguments are meant to be passed from the command line to LibVLC, just like /// VLC media player does. The list of valid arguments depends on the LibVLC /// version, the operating system and platform, and set of available LibVLC /// plugins. Invalid or unsupported arguments will cause the function to fail /// (i.e. return NULL). Also, some arguments may alter the behaviour or /// otherwise interfere with other LibVLC functions. /// There is absolutely no warranty or promise of forward, backward and /// cross-platform compatibility with regards to libvlc_new() arguments. /// We recommend that you do not use them, other than when debugging. /// </summary> /// <param name="options">list of arguments (should be NULL)</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); }
/// <summary>Get media discoverer services by category</summary> /// <param name="discovererCategory">category of services to fetch</param> /// <returns>the number of media discoverer services (0 on error)</returns> /// <remarks>LibVLC 3.0.0 and later.</remarks> public MediaDiscovererDescription[] MediaDiscoverers(MediaDiscovererCategory discovererCategory) => MarshalUtils.Retrieve(NativeReference, discovererCategory, (IntPtr nativeRef, MediaDiscovererCategory enumType, out IntPtr array) => Native.LibVLCMediaDiscovererListGet(nativeRef, enumType, out array), MarshalUtils.PtrToStructure <MediaDiscovererDescriptionStructure>, m => m.Build(), Native.LibVLCMediaDiscovererListRelease);
/// <summary> /// Register callbacks in order to handle VLC dialogs. /// LibVLC 3.0.0 and later. /// </summary> /// <param name="error">Called when an error message needs to be displayed.</param> /// <param name="login">Called when a login dialog needs to be displayed. /// You can interact with this dialog by calling Dialog.PostLogin() to post an answer or Dialog.Dismiss() to cancel this dialog.</param> /// <param name="question">Called when a question dialog needs to be displayed. /// You can interact with this dialog by calling Dialog.PostLogin() to post an answer or Dialog.Dismiss() to cancel this dialog.</param> /// <param name="displayProgress">Called when a progress dialog needs to be displayed.</param> /// <param name="updateProgress">Called when a progress dialog needs to be updated.</param> public void SetDialogHandlers(DisplayError error, DisplayLogin login, DisplayQuestion question, DisplayProgress displayProgress, UpdateProgress updateProgress) { if (error == null) { throw new ArgumentNullException(nameof(error)); } if (login == null) { throw new ArgumentNullException(nameof(login)); } if (question == null) { throw new ArgumentNullException(nameof(question)); } if (displayProgress == null) { throw new ArgumentNullException(nameof(displayProgress)); } if (updateProgress == null) { throw new ArgumentNullException(nameof(updateProgress)); } var dialogCbs = new DialogCallbacks( displayError: (data, title, text) => error(title, text), displayLogin: (data, id, title, text, username, store) => { var cts = new CancellationTokenSource(); var dlg = new Dialog(new DialogId(id)); _cts[id] = cts; login(dlg, title, text, username, store, cts.Token); }, displayQuestion: (data, id, title, text, type, cancelText, firstActionText, secondActionText) => { var cts = new CancellationTokenSource(); var dlg = new Dialog(new DialogId(id)); _cts[id] = cts; question(dlg, title, text, type, cancelText, firstActionText, secondActionText, cts.Token); }, displayProgress: (data, id, title, text, indeterminate, position, cancelText) => { var cts = new CancellationTokenSource(); var dlg = new Dialog(new DialogId(id)); _cts[id] = cts; displayProgress(dlg, title, text, indeterminate, position, cancelText, cts.Token); }, cancel: (data, id) => { if (_cts.TryGetValue(id, out var token)) { token.Cancel(); _cts.Remove(id); } }, updateProgress: (data, id, position, text) => { var dlg = new Dialog(new DialogId(id)); updateProgress(dlg, position, text); }); _dialogCbsPtr = Marshal.AllocHGlobal(MarshalUtils.SizeOf(dialogCbs)); Marshal.StructureToPtr(dialogCbs, _dialogCbsPtr, true); Native.LibVLCDialogSetCallbacks(NativeReference, _dialogCbsPtr, IntPtr.Zero); }