using LibVLCSharp.Shared.Helpers; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace LibVLCSharp.Shared { /// /// Media is an abstract representation of a playable media. It can be a network stream or a local video/audio file. /// public class Media : Internal { static readonly ConcurrentDictionary DicStreams = new ConcurrentDictionary(); static int _streamIndex; internal struct Native { [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_new_location")] internal static extern IntPtr LibVLCMediaNewLocation(IntPtr libVLC, IntPtr mrl); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_new_path")] internal static extern IntPtr LibVLCMediaNewPath(IntPtr libVLC, IntPtr path); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_new_as_node")] internal static extern IntPtr LibVLCMediaNewAsNode(IntPtr libVLC, IntPtr name); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_new_fd")] internal static extern IntPtr LibVLCMediaNewFd(IntPtr libVLC, int fd); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_release")] internal static extern void LibVLCMediaRelease(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_list_media")] internal static extern IntPtr LibVLCMediaListMedia(IntPtr mediaList); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_new_callbacks")] internal static extern IntPtr LibVLCMediaNewCallbacks(IntPtr libVLC, IntPtr openCb, IntPtr readCb, IntPtr seekCb, IntPtr closeCb, IntPtr opaque); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_add_option")] internal static extern void LibVLCMediaAddOption(IntPtr media, [MarshalAs(UnmanagedType.LPStr)] string options); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_add_option_flag")] internal static extern void LibVLCMediaAddOptionFlag(IntPtr media, [MarshalAs(UnmanagedType.LPStr)] string options, uint flags); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_mrl")] internal static extern IntPtr LibVLCMediaGetMrl(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_duplicate")] internal static extern IntPtr LibVLCMediaDuplicate(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_meta")] internal static extern IntPtr LibVLCMediaGetMeta(IntPtr media, MetadataType metadataType); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_set_meta")] internal static extern void LibVLCMediaSetMeta(IntPtr media, MetadataType metadataType, [MarshalAs(UnmanagedType.LPStr)] string value); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_save_meta")] internal static extern int LibVLCMediaSaveMeta(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_state")] internal static extern VLCState LibVLCMediaGetState(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_event_manager")] internal static extern IntPtr LibVLCMediaEventManager(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_stats")] internal static extern int LibVLCMediaGetStats(IntPtr media, out MediaStats statistics); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_duration")] internal static extern long LibVLCMediaGetDuration(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_is_parsed")] internal static extern int LibVLCMediaIsParsed(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_parse_with_options")] internal static extern int LibVLCMediaParseWithOptions(IntPtr media, MediaParseOptions mediaParseOptions, int timeout); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_parsed_status")] internal static extern MediaParsedStatus LibVLCMediaGetParsedStatus(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_parse_stop")] internal static extern void LibVLCMediaParseStop(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_set_user_data")] internal static extern void LibVLCMediaSetUserData(IntPtr media, IntPtr userData); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_user_data")] internal static extern IntPtr LibVLCMediaGetUserData(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_tracks_get")] internal static extern uint LibVLCMediaTracksGet(IntPtr media, out IntPtr tracksPtr); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_tracks_release")] internal static extern void LibVLCMediaTracksRelease(IntPtr tracks, uint count); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_subitems")] internal static extern IntPtr LibVLCMediaSubitems(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_type")] internal static extern MediaType LibVLCMediaGetType(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_slaves_add")] internal static extern int LibVLCMediaAddSlaves(IntPtr media, MediaSlaveType slaveType, uint priority, [MarshalAs(UnmanagedType.LPStr)] string uri); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_slaves_clear")] internal static extern void LibVLCMediaClearSlaves(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_slaves_get")] internal static extern uint LibVLCMediaGetSlaves(IntPtr media, out IntPtr slaves); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_slaves_release")] internal static extern void LibVLCMediaReleaseSlaves(IntPtr slaves, uint count); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_retain")] internal static extern void LibVLCMediaRetain(IntPtr media); [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "libvlc_media_get_codec_description")] internal static extern string LibvlcMediaGetCodecDescription(TrackType type, uint codec); } /// /// Media Constructs a libvlc Media instance /// /// A libvlc instance /// A path, location, or node name, depending on the 3rd parameter /// The type of the 2nd argument. public Media(LibVLC libVLC, string mrl, FromType type = FromType.FromPath) : base(() => SelectNativeCtor(libVLC, mrl, type), Native.LibVLCMediaRelease) { } static IntPtr SelectNativeCtor(LibVLC libVLC, string mrl, FromType type) { if (libVLC == null) throw new ArgumentNullException(nameof(libVLC)); if (string.IsNullOrEmpty(mrl)) throw new ArgumentNullException(nameof(mrl)); var mrlPtr = Utf8StringMarshaler.GetInstance().MarshalManagedToNative(mrl); if (mrlPtr == IntPtr.Zero) throw new ArgumentException($"error marshalling {mrl} to UTF-8 for native interop"); switch (type) { case FromType.FromLocation: return Native.LibVLCMediaNewLocation(libVLC.NativeReference, mrlPtr); case FromType.FromPath: return Native.LibVLCMediaNewPath(libVLC.NativeReference, mrlPtr); case FromType.AsNode: return Native.LibVLCMediaNewAsNode(libVLC.NativeReference, mrlPtr); default: return IntPtr.Zero; } } /// /// Create a media for an already open file descriptor. /// The file descriptor shall be open for reading(or reading and writing). /// /// Regular file descriptors, pipe read descriptors and character device /// descriptors(including TTYs) are supported on all platforms. /// Block device descriptors are supported where available. /// Directory descriptors are supported on systems that provide fdopendir(). /// Sockets are supported on all platforms where they are file descriptors, /// i.e.all except Windows. /// /// \note This library will not automatically close the file descriptor /// under any circumstance.Nevertheless, a file descriptor can usually only be /// rendered once in a media player.To render it a second time, the file /// descriptor should probably be rewound to the beginning with lseek(). /// /// A libvlc instance /// open file descriptor public Media(LibVLC libVLC, int fd) : base(() => Native.LibVLCMediaNewFd(libVLC.NativeReference, fd), Native.LibVLCMediaRelease) { } /// /// Create a media from a media list /// /// media list to create media from public Media(MediaList mediaList) : base(() => Native.LibVLCMediaListMedia(mediaList.NativeReference), Native.LibVLCMediaRelease) { } /// /// Create a media from a .NET Stream /// requires libvlc 3.0 or higher /// /// the libvlc instance /// the .NET Stream to be used by libvlc. LibVLCSharp will NOT dispose or close it. /// the libvlc options public Media(LibVLC libVLC, Stream stream, params string[] options) : base(() => CtorFromCallbacks(libVLC, stream), Native.LibVLCMediaRelease) { if (options.Any()) Native.LibVLCMediaAddOption(NativeReference, options.ToString()); } static IntPtr CtorFromCallbacks(LibVLC libVLC, Stream stream) { if (libVLC == null) throw new ArgumentNullException(nameof(libVLC)); if (stream == null) throw new ArgumentNullException(nameof(stream)); var openMedia = new OpenMedia(CallbackOpenMedia); var readMedia = new ReadMedia(CallbackReadMedia); var seekMedia = new SeekMedia(CallbackSeekMedia); var closeMedia = new CloseMedia(CallbackCloseMedia); var opaque = AddStream(stream, openMedia, readMedia, seekMedia, closeMedia); if (opaque == IntPtr.Zero) throw new InvalidOperationException("Cannot create opaque parameter"); return Native.LibVLCMediaNewCallbacks(libVLC.NativeReference, Marshal.GetFunctionPointerForDelegate(openMedia), Marshal.GetFunctionPointerForDelegate(readMedia), Marshal.GetFunctionPointerForDelegate(seekMedia), Marshal.GetFunctionPointerForDelegate(closeMedia), opaque); } internal Media(IntPtr mediaPtr) : base(() => mediaPtr, Native.LibVLCMediaRelease) { } /// Add an option to the media. /// the options (as a string) /// /// This option will be used to determine how the media_player will /// read the media. This allows to use VLC's advanced /// reading/streaming options on a per-media basis. /// The options are listed in 'vlc --long-help' from the command line, /// e.g. "-sout-all". Keep in mind that available options and their semantics /// vary across LibVLC versions and builds. /// Not all options affects libvlc_media_t objects: /// Specifically, due to architectural issues most audio and video options, /// such as text renderer options, have no effects on an individual media. /// These options must be set through libvlc_new() instead. /// public void AddOption(string options) { if(string.IsNullOrEmpty(options)) throw new ArgumentNullException(nameof(options)); Native.LibVLCMediaAddOption(NativeReference, options); } /// /// Convenience method for crossplatform media configuration /// /// mediaConfiguration translate to strings parsed by the vlc engine, some are platform specific public void AddOption(MediaConfiguration mediaConfiguration) { if (mediaConfiguration == null) throw new ArgumentNullException(nameof(mediaConfiguration)); AddOption(mediaConfiguration.Build()); } /// Add an option to the media with configurable flags. /// the options (as a string) /// the flags for this option /// /// This option will be used to determine how the media_player will /// read the media. This allows to use VLC's advanced /// reading/streaming options on a per-media basis. /// The options are detailed in vlc --long-help, for instance /// "--sout-all". Note that all options are not usable on medias: /// specifically, due to architectural issues, video-related options /// such as text renderer options cannot be set on a single media. They /// must be set on the whole libvlc instance instead. /// public void AddOptionFlag(string options, uint flags) { if (string.IsNullOrEmpty(options)) throw new ArgumentNullException(nameof(options)); Native.LibVLCMediaAddOptionFlag(NativeReference, options, flags); } string _mrl; /// Get the media resource locator (mrl) from a media descriptor object public string Mrl { get { if (string.IsNullOrEmpty(_mrl)) { var mrlPtr = Native.LibVLCMediaGetMrl(NativeReference); _mrl = Utf8StringMarshaler.GetInstance().MarshalNativeToManaged(mrlPtr) as string; } return _mrl; } } /// Duplicate a media descriptor object. public Media Duplicate() { var duplicatePtr = Native.LibVLCMediaDuplicate(NativeReference); if(duplicatePtr == IntPtr.Zero) throw new Exception("Failure to duplicate"); return new Media(duplicatePtr); } /// Read the meta of the media. /// the meta to read /// the media's meta /// /// If the media has not yet been parsed this will return NULL. /// public string Meta(MetadataType metadataType) { var metaPtr = Native.LibVLCMediaGetMeta(NativeReference, metadataType); if (metaPtr == IntPtr.Zero) return string.Empty; return Utf8StringMarshaler.GetInstance().MarshalNativeToManaged(metaPtr) as string; } /// /// Set the meta of the media (this function will not save the meta, call /// libvlc_media_save_meta in order to save the meta) /// /// the to write /// the media's meta public void SetMeta(MetadataType metadataType, string value) { if(string.IsNullOrEmpty(value)) throw new ArgumentNullException(value); Native.LibVLCMediaSetMeta(NativeReference, metadataType, value); } /// Save the meta previously set /// true if the write operation was successful public bool SaveMeta() => Native.LibVLCMediaSaveMeta(NativeReference) != 0; /// /// Get current of media descriptor object. /// public VLCState State => Native.LibVLCMediaGetState(NativeReference); /// Get the current statistics about the media /// structure that contain the statistics about the media /// public MediaStats Statistics => Native.LibVLCMediaGetStats(NativeReference, out var mediaStats) == 0 ? default(MediaStats) : mediaStats; MediaEventManager _eventManager; /// /// Get event manager from media descriptor object. /// NOTE: this function doesn't increment reference counting. /// /// event manager object MediaEventManager EventManager { get { if (_eventManager != null) return _eventManager; var eventManagerPtr = Native.LibVLCMediaEventManager(NativeReference); _eventManager = new MediaEventManager(eventManagerPtr); return _eventManager; } } /// Get duration (in ms) of media descriptor object item. /// duration of media item or -1 on error public long Duration => Native.LibVLCMediaGetDuration(NativeReference); /// /// Parse the media asynchronously with options. /// It uses a flag to specify parse options (see ). All these flags can be combined. By default, media is parsed if it's a local file. /// Note: Parsing can be aborted with ParseStop(). /// /// parse options /// maximum time allowed to preparse the media. /// If -1, the default "preparse-timeout" option will be used as a timeout. /// If 0, it will wait indefinitely. If > 0, the timeout will be used (in milliseconds). /// /// token to cancel the operation public async Task Parse(MediaParseOptions options = MediaParseOptions.ParseLocal, int timeout = -1, CancellationToken cancellationToken = default) { try { if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested(); _tcs = new TaskCompletionSource(); var timeoutToken = new CancellationTokenSource(timeout); ParsedChanged += OnParsedChanged; cancellationToken.Register(() => { ParsedChanged -= OnParsedChanged; Native.LibVLCMediaParseStop(NativeReference); _tcs?.TrySetCanceled(); }); timeoutToken.Token.Register(() => { ParsedChanged -= OnParsedChanged; Native.LibVLCMediaParseStop(NativeReference); _tcs?.TrySetCanceled(); }); var result = Native.LibVLCMediaParseWithOptions(NativeReference, options, timeout); if (result == -1) { timeoutToken.Cancel(); timeoutToken.Dispose(); _tcs.TrySetResult(false); return false; } return await _tcs.Task; } catch (OperationCanceledException) { _tcs?.TrySetCanceled(); return false; } catch (Exception ex) { _tcs?.TrySetException(ex); return false; } finally { ParsedChanged -= OnParsedChanged; } } TaskCompletionSource _tcs; void OnParsedChanged(object sender, MediaParsedChangedEventArgs mediaParsedChangedEventArgs) { if (ParsedStatus == MediaParsedStatus.Done) _tcs?.TrySetResult(true); else if (ParsedStatus == MediaParsedStatus.Failed) _tcs?.TrySetException(new VLCException($"parsing of {this} failed")); else _tcs?.TrySetResult(false); } /// Return true is the media descriptor object is parsed /// true if media object has been parsed otherwise it returns false public bool IsParsed => Native.LibVLCMediaIsParsed(NativeReference) != 0; /// Get Parsed status for media descriptor object. /// a value of the libvlc_media_parsed_status_t enum /// /// libvlc_MediaParsedChanged /// libvlc_media_parsed_status_t /// LibVLC 3.0.0 or later /// public MediaParsedStatus ParsedStatus => Native.LibVLCMediaGetParsedStatus(NativeReference); /// Stop the parsing of the media /// /// When the media parsing is stopped, the libvlc_MediaParsedChanged event will /// be sent with the libvlc_media_parsed_status_timeout status. /// libvlc_media_parse_with_options /// LibVLC 3.0.0 or later /// public void ParseStop() => Native.LibVLCMediaParseStop(NativeReference); /// Get media descriptor's elementary streams description /// address to store an allocated array of Elementary Streams /// descriptions (must be freed with libvlc_media_tracks_release /// by the caller) [OUT] /// the number of Elementary Streams (zero on error) /// /// Note, you need to call libvlc_media_parse() or play the media at least once /// before calling this function. /// Not doing this will result in an empty array. /// LibVLC 2.1.0 and later. /// /// public MediaTrack[] Tracks => MarshalUtils.Retrieve(NativeReference, (IntPtr nativeRef, out IntPtr array) => Native.LibVLCMediaTracksGet(nativeRef, out array), MarshalUtils.PtrToStructure, m => m.Build(), Native.LibVLCMediaTracksRelease); /// /// Get subitems of media descriptor object. This will increment /// the reference count of supplied media descriptor object. Use /// libvlc_media_list_release() to decrement the reference counting. /// /// list of media descriptor subitems or NULL public MediaList SubItems => new MediaList(Native.LibVLCMediaSubitems(NativeReference)); public MediaType Type => Native.LibVLCMediaGetType(NativeReference); /// Add a slave to the current media. /// subtitle or audio /// from 0 (low priority) to 4 (high priority) /// Uri of the slave (should contain a valid scheme). /// 0 on success, -1 on error. /// /// A slave is an external input source that may contains an additional subtitle /// track (like a .srt) or an additional audio track (like a .ac3). /// This function must be called before the media is parsed (via /// libvlc_media_parse_with_options()) or before the media is played (via /// libvlc_media_player_play()) /// LibVLC 3.0.0 and later. /// public bool AddSlave(MediaSlaveType type, uint priority, string uri) => Native.LibVLCMediaAddSlaves(NativeReference, type, priority, uri) != 0; /// /// Clear all slaves previously added by libvlc_media_slaves_add() or /// internally. /// /// LibVLC 3.0.0 and later. public void ClearSlaves() => Native.LibVLCMediaClearSlaves(NativeReference); /// Get a media descriptor's slave list /// address to store an allocated array of slaves (must be /// freed with libvlc_media_slaves_release()) [OUT] /// the number of slaves (zero on error) /// /// The list will contain slaves parsed by VLC or previously added by /// libvlc_media_slaves_add(). The typical use case of this function is to save /// a list of slave in a database for a later use. /// LibVLC 3.0.0 and later. /// libvlc_media_slaves_add /// public MediaSlave[] Slaves => MarshalUtils.Retrieve(NativeReference, (IntPtr nativeRef, out IntPtr array) => Native.LibVLCMediaGetSlaves(nativeRef, out array), MarshalUtils.PtrToStructure, s => s.Build(), Native.LibVLCMediaReleaseSlaves); public override bool Equals(object obj) { return obj is Media media && EqualityComparer.Default.Equals(NativeReference, media.NativeReference); } public override int GetHashCode() { return this.NativeReference.GetHashCode(); } internal class StreamData { internal IntPtr Handle { get; set; } internal Stream Stream { get; set; } internal byte[] Buffer { get; set; } internal OpenMedia OpenMedia { get; set; } internal ReadMedia ReadMedia { get; set; } internal SeekMedia SeekMedia { get; set; } internal CloseMedia CloseMedia { get; set; } } #region private [MonoPInvokeCallback(typeof(OpenMedia))] static int CallbackOpenMedia(IntPtr opaque, ref IntPtr data, out ulong size) { data = opaque; try { var streamData = GetStream(opaque); try { size = (ulong)streamData.Stream.Length; } catch (Exception) { // byte length of the bitstream or UINT64_MAX if unknown size = ulong.MaxValue; } if (streamData.Stream.CanSeek) { streamData.Stream.Seek(0L, SeekOrigin.Begin); } return 0; } catch (Exception) { size = 0UL; return -1; } } [MonoPInvokeCallback(typeof(ReadMedia))] static int CallbackReadMedia(IntPtr opaque, IntPtr buf, uint len) { try { var streamData = GetStream(opaque); int read; lock (streamData) { var canRead = Math.Min((int)len, streamData.Buffer.Length); read = streamData.Stream.Read(streamData.Buffer, 0, canRead); Marshal.Copy(streamData.Buffer, 0, buf, read); } return read; } catch (Exception) { return -1; } } [MonoPInvokeCallback(typeof(SeekMedia))] static int CallbackSeekMedia(IntPtr opaque, ulong offset) { try { var streamData = GetStream(opaque); streamData.Stream.Seek((long)offset, SeekOrigin.Begin); return 0; } catch (Exception) { return -1; } } [MonoPInvokeCallback(typeof(CloseMedia))] static void CallbackCloseMedia(IntPtr opaque) { try { var streamData = GetStream(opaque); if (streamData.Stream.CanSeek) streamData.Stream.Seek(0, SeekOrigin.Begin); } catch (Exception) { // ignored } } static IntPtr AddStream(Stream stream, OpenMedia openMedia, ReadMedia readMedia, SeekMedia seekMedia, CloseMedia closeMedia) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } IntPtr handle; lock (DicStreams) { _streamIndex++; handle = new IntPtr(_streamIndex); DicStreams[handle] = new StreamData { Buffer = new byte[0x4000], Handle = handle, Stream = stream, OpenMedia = openMedia, ReadMedia = readMedia, SeekMedia = seekMedia, CloseMedia = closeMedia }; } return handle; } static StreamData GetStream(IntPtr handle) { return !DicStreams.TryGetValue(handle, out var result) ? null : result; } static void RemoveStream(IntPtr handle) { DicStreams.TryRemove(handle, out var result); } void Retain() { if (NativeReference != IntPtr.Zero) Native.LibVLCMediaRetain(NativeReference); } #endregion #region Events public event EventHandler MetaChanged { add => EventManager.AttachEvent(EventType.MediaMetaChanged, value); remove => EventManager.DetachEvent(EventType.MediaMetaChanged, value); } public event EventHandler ParsedChanged { add => EventManager.AttachEvent(EventType.MediaParsedChanged, value); remove => EventManager.DetachEvent(EventType.MediaParsedChanged, value); } public event EventHandler SubItemAdded { add => EventManager.AttachEvent(EventType.MediaSubItemAdded, value); remove => EventManager.DetachEvent(EventType.MediaSubItemAdded, value); } public event EventHandler DurationChanged { add => EventManager.AttachEvent(EventType.MediaDurationChanged, value); remove => EventManager.DetachEvent(EventType.MediaDurationChanged, value); } public event EventHandler MediaFreed { add => EventManager.AttachEvent(EventType.MediaFreed, value); remove => EventManager.DetachEvent(EventType.MediaFreed, value); } public event EventHandler StateChanged { add => EventManager.AttachEvent(EventType.MediaStateChanged, value); remove => EventManager.DetachEvent(EventType.MediaStateChanged, value); } public event EventHandler SubItemTreeAdded { add => EventManager.AttachEvent(EventType.MediaSubItemTreeAdded, value); remove => EventManager.DetachEvent(EventType.MediaSubItemTreeAdded, value); } #endregion /// /// Dispose of this media /// /// protected override void Dispose(bool disposing) { if (IsDisposed || NativeReference == IntPtr.Zero) return; base.Dispose(disposing); } } #region Callbacks /// /// It consists of a media location and various optional meta data. /// @{ /// /// LibVLC media item/descriptor external API /// /// Callback prototype to open a custom bitstream input media. /// private pointer as passed to libvlc_media_new_callbacks() /// storage space for a private data pointer [OUT] /// byte length of the bitstream or UINT64_MAX if unknown [OUT] /// /// 0 on success, non-zero on error. In case of failure, the other /// callbacks will not be invoked and any value stored in *datap and *sizep is /// discarded. /// /// /// The same media item can be opened multiple times. Each time, this callback /// is invoked. It should allocate and initialize any instance-specific /// resources, then store them in *datap. The instance resources can be freed /// in the /// For convenience, *datap is initially NULL and *sizep is initially 0. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int OpenMedia(IntPtr opaque, ref IntPtr data, out ulong size); /// Callback prototype to read data from a custom bitstream input media. /// private pointer as set by the /// start address of the buffer to read data into /// bytes length of the buffer /// /// strictly positive number of bytes read, 0 on end-of-stream, /// or -1 on non-recoverable error /// /// /// callback /// If no data is immediately available, then the callback should sleep. /// The application is responsible for avoiding deadlock situations. /// In particular, the callback should return an error if playback is stopped; /// if it does not return, then libvlc_media_player_stop() will never return. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int ReadMedia(IntPtr opaque, IntPtr buf, uint len); /// Callback prototype to seek a custom bitstream input media. /// private pointer as set by the /// absolute byte offset to seek to /// 0 on success, -1 on error. /// callback [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int SeekMedia(IntPtr opaque, ulong offset); /// Callback prototype to close a custom bitstream input media. /// private pointer as set by the /// callback [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void CloseMedia(IntPtr opaque); #endregion #region enums /// Note the order of libvlc_state_t enum must match exactly the order of /// /// mediacontrol_PlayerStatus, /// input_state_e enums, /// and VideoLAN.LibVLCSharp.State (at bindings/cil/src/media.cs). /// Expected states by web plugins are: /// IDLE/CLOSE=0, OPENING=1, PLAYING=3, PAUSED=4, /// STOPPING=5, ENDED=6, ERROR=7 /// public enum VLCState { /// /// Nothing special happening /// NothingSpecial = 0, /// /// Opening media /// Opening = 1, /// /// Buffering media /// Buffering = 2, /// /// Playing media /// Playing = 3, /// /// Paused media /// Paused = 4, /// /// Stopped media /// Stopped = 5, /// /// Ended media /// Ended = 6, /// /// Error media /// Error = 7 } /// /// Media track type such as Audio, Video or Text /// public enum TrackType { /// /// Unknown track /// Unknown = -1, /// /// Audio track /// Audio = 0, /// /// Video track /// Video = 1, /// /// Text track /// Text = 2 } /// /// Video orientation /// public enum VideoOrientation { /// Normal. Top line represents top, left column left. TopLeft = 0, /// Flipped horizontally TopRight = 1, /// Flipped vertically BottomLeft = 2, /// Rotated 180 degrees BottomRight = 3, /// Transposed LeftTop = 4, /// Rotated 90 degrees clockwise (or 270 anti-clockwise) LeftBottom = 5, /// Rotated 90 degrees anti-clockwise RightTop = 6, /// Anti-transposed RightBottom = 7 } /// /// Video projection /// [Flags] public enum VideoProjection { /// /// Rectangular /// Rectangular = 0, /// 360 spherical Equirectangular = 1, /// /// Cubemap layout standard /// CubemapLayoutStandard = 256 } /// Type of a media slave: subtitle or audio. public enum MediaSlaveType { /// /// Subtitle /// Subtitle = 0, /// /// Audio /// Audio = 1 } /// Meta data types public enum MetadataType { Title = 0, Artist = 1, Genre = 2, Copyright = 3, Album = 4, TrackNumber = 5, Description = 6, Rating = 7, Date = 8, Setting = 9, URL = 10, Language = 11, NowPlaying = 12, Publisher = 13, EncodedBy = 14, ArtworkURL = 15, TrackID = 16, TrackTotal = 17, Director = 18, Season = 19, Episode = 20, ShowName = 21, Actors = 22, AlbumArtist = 23, DiscNumber = 24, DiscTotal = 25 } /// /// The FromType enum is used to drive the media creation. /// A media is usually created using a string, which can represent one of 3 things: FromPath, FromLocation, AsNode. /// public enum FromType { /// /// Create a media for a certain file path. /// FromPath, /// /// Create a media with a certain given media resource location, /// for instance a valid URL. /// note To refer to a local file with this function, /// the file://... URI syntax must be used (see IETF RFC3986). /// We recommend using FromPath instead when dealing with ///local files. /// FromLocation, /// /// Create a media as an empty node with a given name. /// AsNode } /// /// Parse flags used by libvlc_media_parse_with_options() /// /// libvlc_media_parse_with_options [Flags] public enum MediaParseOptions { /// Parse media if it's a local file ParseLocal = 0, /// Parse media even if it's a network file ParseNetwork = 1, /// Fetch meta and covert art using local resources FetchLocal = 2, /// Fetch meta and covert art using network resources FetchNetwork = 4, /// /// Interact with the user (via libvlc_dialog_cbs) when preparsing this item /// (and not its sub items). Set this flag in order to receive a callback /// when the input is asking for credentials. /// DoInteract = 8 } /// /// Parse status used sent by libvlc_media_parse_with_options() or returned by /// libvlc_media_get_parsed_status() /// /// /// libvlc_media_parse_with_options /// libvlc_media_get_parsed_status /// public enum MediaParsedStatus { Skipped = 1, Failed = 2, Timeout = 3, Done = 4 } /// Media type /// libvlc_media_get_type public enum MediaType { Unknown = 0, File = 1, Directory = 2, Disc = 3, Stream = 4, Playlist = 5 } #endregion /// /// Small configuration helper /// public class MediaConfiguration { HashSet _options = new HashSet(); public MediaConfiguration EnableHardwareDecoding() { #if ANDROID _options.Add(":codec=mediacodec_ndk"); #endif return this; } public string Build() => string.Join(",", _options); } }