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);
}
}