Media.cs 42.1 KB
Newer Older
1 2
using LibVLCSharp.Shared.Helpers;
using System;
Martin Finkel's avatar
Martin Finkel committed
3
using System.Collections.Concurrent;
Martin Finkel's avatar
Martin Finkel committed
4 5 6 7 8
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

9
namespace LibVLCSharp.Shared
Martin Finkel's avatar
Martin Finkel committed
10
{
11
    public class Media : Internal
Martin Finkel's avatar
Martin Finkel committed
12
    {
Martin Finkel's avatar
Martin Finkel committed
13 14 15
        static readonly ConcurrentDictionary<IntPtr, StreamData> DicStreams = new ConcurrentDictionary<IntPtr, StreamData>();
        static int _streamIndex;
        
16
        internal struct Native
Martin Finkel's avatar
Martin Finkel committed
17
        {
18
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
19
                EntryPoint = "libvlc_media_new_location")]
20
            internal static extern IntPtr LibVLCMediaNewLocation(IntPtr libVLC, IntPtr mrl);
Martin Finkel's avatar
Martin Finkel committed
21

22
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
23
                EntryPoint = "libvlc_media_new_path")]
24
            internal static extern IntPtr LibVLCMediaNewPath(IntPtr libVLC, IntPtr path);
Martin Finkel's avatar
Martin Finkel committed
25

26
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
27
                EntryPoint = "libvlc_media_new_as_node")]
28
            internal static extern IntPtr LibVLCMediaNewAsNode(IntPtr libVLC, IntPtr name);
Martin Finkel's avatar
Martin Finkel committed
29

30
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
31
                EntryPoint = "libvlc_media_new_fd")]
Martin Finkel's avatar
Martin Finkel committed
32
            internal static extern IntPtr LibVLCMediaNewFd(IntPtr libVLC, int fd);
Martin Finkel's avatar
Martin Finkel committed
33 34 35 36 37 38 39 40 41

            /// <summary>
            /// <para>Decrement the reference count of a media descriptor object. If the</para>
            /// <para>reference count is 0, then libvlc_media_release() will release the</para>
            /// <para>media descriptor object. It will send out an libvlc_MediaFreed event</para>
            /// <para>to all listeners. If the media descriptor object has been released it</para>
            /// <para>should not be used again.</para>
            /// </summary>
            /// <param name="media">the media descriptor</param>
42
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
43 44 45
                EntryPoint = "libvlc_media_release")]
            internal static extern void LibVLCMediaRelease(IntPtr media);

46
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
47 48 49
                EntryPoint = "libvlc_media_list_media")]
            internal static extern IntPtr LibVLCMediaListMedia(IntPtr mediaList);

50
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
51
                EntryPoint = "libvlc_media_new_callbacks")]
Martin Finkel's avatar
Martin Finkel committed
52
            internal static extern IntPtr LibVLCMediaNewCallbacks(IntPtr libVLC, IntPtr openCb, IntPtr readCb, IntPtr seekCb, IntPtr closeCb, IntPtr opaque);
Martin Finkel's avatar
Martin Finkel committed
53

54
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
55 56 57
                EntryPoint = "libvlc_media_add_option")]
            internal static extern void LibVLCMediaAddOption(IntPtr media, [MarshalAs(UnmanagedType.LPStr)] string options);

58
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
59 60
                EntryPoint = "libvlc_media_add_option_flag")]
            internal static extern void LibVLCMediaAddOptionFlag(IntPtr media, [MarshalAs(UnmanagedType.LPStr)] string options, uint flags);
61

62
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
63 64 65
                EntryPoint = "libvlc_media_get_mrl")]
            internal static extern IntPtr LibVLCMediaGetMrl(IntPtr media);

66
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
67 68 69
                EntryPoint = "libvlc_media_duplicate")]
            internal static extern IntPtr LibVLCMediaDuplicate(IntPtr media);

70
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
71 72 73
                EntryPoint = "libvlc_media_get_meta")]
            internal static extern IntPtr LibVLCMediaGetMeta(IntPtr media, MetadataType metadataType);

74
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
75 76 77
                EntryPoint = "libvlc_media_set_meta")]
            internal static extern void LibVLCMediaSetMeta(IntPtr media, MetadataType metadataType, [MarshalAs(UnmanagedType.LPStr)] string value);

78
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
79 80 81
                EntryPoint = "libvlc_media_save_meta")]
            internal static extern int LibVLCMediaSaveMeta(IntPtr media);

82
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
83 84 85
                EntryPoint = "libvlc_media_get_state")]
            internal static extern VLCState LibVLCMediaGetState(IntPtr media);

86
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
87 88 89
                EntryPoint = "libvlc_media_event_manager")]
            internal static extern IntPtr LibVLCMediaEventManager(IntPtr media);

90
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
91 92 93
                EntryPoint = "libvlc_media_get_stats")]
            internal static extern int LibVLCMediaGetStats(IntPtr media, out MediaStats statistics);

94
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
95 96 97
                EntryPoint = "libvlc_media_get_duration")]
            internal static extern long LibVLCMediaGetDuration(IntPtr media);

98
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
99 100 101
                EntryPoint = "libvlc_media_parse")]
            internal static extern void LibVLCMediaParse(IntPtr media);

102
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
103 104 105
                EntryPoint = "libvlc_media_parse_async")]
            internal static extern void LibVLCMediaParseAsync(IntPtr media);

106
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
107 108 109
                EntryPoint = "libvlc_media_is_parsed")]
            internal static extern int LibVLCMediaIsParsed(IntPtr media);

110
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
111 112 113
                EntryPoint = "libvlc_media_parse_with_options")]
            internal static extern int LibVLCMediaParseWithOptions(IntPtr media, MediaParseOptions mediaParseOptions, int timeout);

114
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
115 116 117
                EntryPoint = "libvlc_media_get_parsed_status")]
            internal static extern MediaParsedStatus LibVLCMediaGetParsedStatus(IntPtr media);

118
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
119 120 121
                EntryPoint = "libvlc_media_parse_stop")]
            internal static extern void LibVLCMediaParseStop(IntPtr media);

122
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
123 124 125
                EntryPoint = "libvlc_media_set_user_data")]
            internal static extern void LibVLCMediaSetUserData(IntPtr media, IntPtr userData);

126
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
127 128 129
                EntryPoint = "libvlc_media_get_user_data")]
            internal static extern IntPtr LibVLCMediaGetUserData(IntPtr media);

130
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
131 132 133
                EntryPoint = "libvlc_media_tracks_get")]
            internal static extern uint LibVLCMediaTracksGet(IntPtr media, ref IntPtr tracksPtr);

134
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
135 136
                EntryPoint = "libvlc_media_tracks_release")]
            internal static extern void LibVLCMediaTracksRelease(IntPtr tracks, uint count);
137

138
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
139 140
                EntryPoint = "libvlc_media_subitems")]
            internal static extern IntPtr LibVLCMediaSubitems(IntPtr media);
141

142
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
143 144 145
                EntryPoint = "libvlc_media_get_type")]
            internal static extern MediaType LibVLCMediaGetType(IntPtr media);

146
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
147 148 149
                EntryPoint = "libvlc_media_slaves_add")]
            internal static extern int LibVLCMediaAddSlaves(IntPtr media, MediaSlaveType slaveType, uint priority, [MarshalAs(UnmanagedType.LPStr)] string uri);

150
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
151 152 153
                EntryPoint = "libvlc_media_slaves_clear")]
            internal static extern void LibVLCMediaClearSlaves(IntPtr media);

154
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
155 156 157
                EntryPoint = "libvlc_media_slaves_get")]
            internal static extern uint LibVLCMediaGetSlaves(IntPtr media, ref IntPtr slaves);

158
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
159 160 161
                EntryPoint = "libvlc_media_slaves_release")]
            internal static extern void LibVLCMediaReleaseSlaves(IntPtr slaves, uint count);

162
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
163 164
                EntryPoint = "libvlc_media_retain")]
            internal static extern void LibVLCMediaRetain(IntPtr media);
Martin Finkel's avatar
Martin Finkel committed
165

166
            [DllImport(Constants.LibraryName, CallingConvention = CallingConvention.Cdecl,
Martin Finkel's avatar
Martin Finkel committed
167 168
                            EntryPoint = "libvlc_media_get_codec_description")]
            internal static extern string LibvlcMediaGetCodecDescription(TrackType type, uint codec);
Martin Finkel's avatar
Martin Finkel committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
        }

        #region enums
        /// <summary>Meta data types</summary>
        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
        }

        public enum FromType
        {
            /// <summary>
            /// Create a media for a certain file path.
            /// </summary>
            FromPath,
            /// <summary>
            /// 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 <b>must</b> be used (see IETF RFC3986).
            /// We recommend using FromPath instead when dealing with
            ///local files.
            /// </summary>
            FromLocation,
            /// <summary>
            /// Create a media as an empty node with a given name.
220
            /// </summary>
Martin Finkel's avatar
Martin Finkel committed
221 222 223
            AsNode
        }

224
        /// <summary>
225
        /// Parse flags used by libvlc_media_parse_with_options()
Martin Finkel's avatar
Martin Finkel committed
226
        /// </summary>
Martin Finkel's avatar
Martin Finkel committed
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
        /// <remarks>libvlc_media_parse_with_options</remarks>
        [Flags]
        public enum MediaParseOptions
        {
            /// <summary>Parse media if it's a local file</summary>
            ParseLocal = 0,
            /// <summary>Parse media even if it's a network file</summary>
            ParseNetwork = 1,
            /// <summary>Fetch meta and covert art using local resources</summary>
            FetchLocal = 2,
            /// <summary>Fetch meta and covert art using network resources</summary>
            FetchNetwork = 4,
            /// <summary>
            /// 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.
            /// </summary>
            DoInteract = 8
        }

Martin Finkel's avatar
Martin Finkel committed
247
        /// <summary>
Martin Finkel's avatar
Martin Finkel committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
        /// Parse status used sent by libvlc_media_parse_with_options() or returned by
        /// libvlc_media_get_parsed_status()
        /// </summary>
        /// <remarks>
        /// libvlc_media_parse_with_options
        /// libvlc_media_get_parsed_status
        /// </remarks>
        public enum MediaParsedStatus
        {
            Skipped = 1,
            Failed = 2,
            Timeout = 3,
            Done = 4
        }

        /// <summary>Media type</summary>
        /// <remarks>libvlc_media_get_type</remarks>
        public enum MediaType
        {
            Unknown = 0,
            File = 1,
            Directory = 2,
            Disc = 3,
            Stream = 4,
            Playlist = 5
        }

        #endregion

        /// <summary>
        /// Media Constructs a libvlc Media instance
        /// </summary>
Martin Finkel's avatar
Martin Finkel committed
280
        /// <param name="libVLC">A libvlc instance</param>
Martin Finkel's avatar
Martin Finkel committed
281 282
        /// <param name="mrl">A path, location, or node name, depending on the 3rd parameter</param>
        /// <param name="type">The type of the 2nd argument. \sa{FromType}</param>
Martin Finkel's avatar
Martin Finkel committed
283 284
        public Media(LibVLC libVLC, string mrl, FromType type = FromType.FromPath)
            : base(() => SelectNativeCtor(libVLC, mrl, type), Native.LibVLCMediaRelease)
285 286 287
        {
        }

Martin Finkel's avatar
Martin Finkel committed
288
        static IntPtr SelectNativeCtor(LibVLC libVLC, string mrl, FromType type)
Martin Finkel's avatar
Martin Finkel committed
289
        {
Martin Finkel's avatar
Martin Finkel committed
290
            if (libVLC == null) throw new ArgumentNullException(nameof(libVLC));
291
            if (string.IsNullOrEmpty(mrl)) throw new ArgumentNullException(nameof(mrl));
Martin Finkel's avatar
Martin Finkel committed
292

293 294 295 296
            var mrlPtr = Utf8StringMarshaler.GetInstance().MarshalManagedToNative(mrl);
            if (mrlPtr == IntPtr.Zero)
                throw new ArgumentException($"error marshalling {mrl} to UTF-8 for native interop");

Martin Finkel's avatar
Martin Finkel committed
297 298 299
            switch (type)
            {
                case FromType.FromLocation:
300
                    return Native.LibVLCMediaNewLocation(libVLC.NativeReference, mrlPtr);
Martin Finkel's avatar
Martin Finkel committed
301
                case FromType.FromPath:
302
                    return Native.LibVLCMediaNewPath(libVLC.NativeReference, mrlPtr);
Martin Finkel's avatar
Martin Finkel committed
303
                case FromType.AsNode:
304
                    return Native.LibVLCMediaNewAsNode(libVLC.NativeReference, mrlPtr);
305 306
                default:
                    return IntPtr.Zero;
Martin Finkel's avatar
Martin Finkel committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
            }
        }

        /// <summary>
        /// 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 <b>not</b> 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().
        /// </summary>
Martin Finkel's avatar
Martin Finkel committed
326
        /// <param name="libVLC">A libvlc instance</param>
Martin Finkel's avatar
Martin Finkel committed
327
        /// <param name="fd">open file descriptor</param>
Martin Finkel's avatar
Martin Finkel committed
328 329
        public Media(LibVLC libVLC, int fd)
            : base(() => Native.LibVLCMediaNewFd(libVLC.NativeReference, fd), Native.LibVLCMediaRelease)
Martin Finkel's avatar
Martin Finkel committed
330 331 332 333
        {
        }

        public Media(MediaList mediaList)
Martin Finkel's avatar
Martin Finkel committed
334
            : base(() => Native.LibVLCMediaListMedia(mediaList.NativeReference), Native.LibVLCMediaRelease)
Martin Finkel's avatar
Martin Finkel committed
335 336 337 338
        {
        }

        /// <summary>
Martin Finkel's avatar
Martin Finkel committed
339
        /// Create a media from a .NET Stream
Martin Finkel's avatar
Martin Finkel committed
340 341
        /// requires libvlc 3.0 or higher
        /// </summary>
Martin Finkel's avatar
Martin Finkel committed
342 343 344
        /// <param name="libVLC">the libvlc instance</param>
        /// <param name="stream">the .NET Stream to be used by libvlc. LibVLCSharp will NOT dispose or close it.</param>
        /// <param name="options">the libvlc options</param>
Martin Finkel's avatar
Martin Finkel committed
345 346
        public Media(LibVLC libVLC, Stream stream, params string[] options)
            : base(() => CtorFromCallbacks(libVLC, stream), Native.LibVLCMediaRelease)
347 348
        {
            if (options.Any())
349 350 351
                Native.LibVLCMediaAddOption(NativeReference, options.ToString());
        }

Martin Finkel's avatar
Martin Finkel committed
352
        
353

Martin Finkel's avatar
Martin Finkel committed
354
        static IntPtr CtorFromCallbacks(LibVLC libVLC, Stream stream)
Martin Finkel's avatar
Martin Finkel committed
355
        {
Martin Finkel's avatar
Martin Finkel committed
356
            if (libVLC == null) throw new ArgumentNullException(nameof(libVLC));
Martin Finkel's avatar
Martin Finkel committed
357 358
            if (stream == null) throw new ArgumentNullException(nameof(stream));

Martin Finkel's avatar
Martin Finkel committed
359 360 361 362 363 364
            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);
Martin Finkel's avatar
Martin Finkel committed
365

Martin Finkel's avatar
Martin Finkel committed
366 367
            if (opaque == IntPtr.Zero)
                throw new InvalidOperationException("Cannot create opaque parameter");
Martin Finkel's avatar
Martin Finkel committed
368

Martin Finkel's avatar
Martin Finkel committed
369
            return Native.LibVLCMediaNewCallbacks(libVLC.NativeReference,
Martin Finkel's avatar
Martin Finkel committed
370 371 372 373 374
                Marshal.GetFunctionPointerForDelegate(openMedia),
                Marshal.GetFunctionPointerForDelegate(readMedia),
                Marshal.GetFunctionPointerForDelegate(seekMedia),
                Marshal.GetFunctionPointerForDelegate(closeMedia),
                opaque);
Martin Finkel's avatar
Martin Finkel committed
375 376 377
        }

        public Media(IntPtr mediaPtr)
378
            : base(() => mediaPtr, Native.LibVLCMediaRelease)
Martin Finkel's avatar
Martin Finkel committed
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
        {
        }

        /// <summary>Add an option to the media.</summary>
        /// <param name="options">the options (as a string)</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 listed in 'vlc --long-help' from the command line,</para>
        /// <para>e.g. &quot;-sout-all&quot;. Keep in mind that available options and their semantics</para>
        /// <para>vary across LibVLC versions and builds.</para>
        /// <para>Not all options affects libvlc_media_t objects:</para>
        /// <para>Specifically, due to architectural issues most audio and video options,</para>
        /// <para>such as text renderer options, have no effects on an individual media.</para>
        /// <para>These options must be set through libvlc_new() instead.</para>
        /// </remarks>
        public void AddOption(string options)
        {
            if(string.IsNullOrEmpty(options)) throw new ArgumentNullException(nameof(options));

400
            Native.LibVLCMediaAddOption(NativeReference, options);
Martin Finkel's avatar
Martin Finkel committed
401 402
        }

403 404 405 406 407 408 409 410
        /// <summary>
        /// Convenience method for crossplatform media configuration
        /// </summary>
        /// <param name="mediaConfiguration">mediaConfiguration translate to strings parsed by the vlc engine, some are platform specific</param>
        public void AddOption(MediaConfiguration mediaConfiguration)
        {
            if (mediaConfiguration == null) throw new ArgumentNullException(nameof(mediaConfiguration));

Martin Finkel's avatar
Martin Finkel committed
411
            AddOption(mediaConfiguration.Build());
412 413
        }

Martin Finkel's avatar
Martin Finkel committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
        /// <summary>Add an option to the media with configurable flags.</summary>
        /// <param name="options">the options (as a string)</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>&quot;--sout-all&quot;. 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 options, uint flags)
        {
            if (string.IsNullOrEmpty(options)) throw new ArgumentNullException(nameof(options));

431
            Native.LibVLCMediaAddOptionFlag(NativeReference, options, flags);
Martin Finkel's avatar
Martin Finkel committed
432 433 434 435 436 437 438 439 440 441
        }

        string _mrl;
        /// <summary>Get the media resource locator (mrl) from a media descriptor object</summary>
        public string Mrl
        {
            get
            {
                if (string.IsNullOrEmpty(_mrl))
                {
442
                    var mrlPtr = Native.LibVLCMediaGetMrl(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
443 444 445 446 447 448 449 450 451
                    _mrl = Utf8StringMarshaler.GetInstance().MarshalNativeToManaged(mrlPtr) as string;
                }
                return _mrl;
            }
        }

        /// <summary>Duplicate a media descriptor object.</summary>
        public Media Duplicate()
        {
452
            var duplicatePtr = Native.LibVLCMediaDuplicate(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
453 454 455 456 457 458 459 460 461 462 463 464
            if(duplicatePtr == IntPtr.Zero) throw new Exception("Failure to duplicate");
            return new Media(duplicatePtr);
        }

        /// <summary>Read the meta of the media.</summary>
        /// <param name="metadataType">the meta to read</param>
        /// <returns>the media's meta</returns>
        /// <remarks>
        /// If the media has not yet been parsed this will return NULL.
        /// </remarks>
        public string Meta(MetadataType metadataType)
        {
465
            var metaPtr = Native.LibVLCMediaGetMeta(NativeReference, metadataType);
Martin Finkel's avatar
Martin Finkel committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479
            if (metaPtr == IntPtr.Zero) return string.Empty;
            return Utf8StringMarshaler.GetInstance().MarshalNativeToManaged(metaPtr) as string;
        }

        /// <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="value">the media's meta</param>
        public void SetMeta(MetadataType metadataType, string value)
        {
            if(string.IsNullOrEmpty(value)) throw new ArgumentNullException(value);

480
            Native.LibVLCMediaSetMeta(NativeReference, metadataType, value);
Martin Finkel's avatar
Martin Finkel committed
481 482 483 484
        }

        /// <summary>Save the meta previously set</summary>
        /// <returns>true if the write operation was successful</returns>
Martin Finkel's avatar
Martin Finkel committed
485
        public bool SaveMeta() => Native.LibVLCMediaSaveMeta(NativeReference) != 0;
Martin Finkel's avatar
Martin Finkel committed
486 487 488 489

        /// <summary>
        /// Get current <see cref="VLCState"/> of media descriptor object.
        /// </summary>
490
        public VLCState State => Native.LibVLCMediaGetState(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
491 492 493 494

        /// <summary>Get the current statistics about the media
        /// structure that contain the statistics about the media
        /// </summary>
Martin Finkel's avatar
Martin Finkel committed
495 496
        public MediaStats Statistics => Native.LibVLCMediaGetStats(NativeReference, out var mediaStats) == 0 
            ? default(MediaStats) : mediaStats;
Martin Finkel's avatar
Martin Finkel committed
497

Martin Finkel's avatar
Martin Finkel committed
498
        MediaEventManager _eventManager;
Martin Finkel's avatar
Martin Finkel committed
499 500 501 502 503
        /// <summary>
        /// <para>Get event manager from media descriptor object.</para>
        /// <para>NOTE: this function doesn't increment reference counting.</para>
        /// </summary>
        /// <returns>event manager object</returns>
Martin Finkel's avatar
Martin Finkel committed
504
        MediaEventManager EventManager
Martin Finkel's avatar
Martin Finkel committed
505 506 507 508
        {
            get
            {
                if (_eventManager != null) return _eventManager;
509
                var eventManagerPtr = Native.LibVLCMediaEventManager(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
510
                _eventManager = new MediaEventManager(eventManagerPtr);
Martin Finkel's avatar
Martin Finkel committed
511 512 513 514 515 516
                return _eventManager;
            }
        }

        /// <summary>Get duration (in ms) of media descriptor object item.</summary>
        /// <returns>duration of media item or -1 on error</returns>
517
        public long Duration => Native.LibVLCMediaGetDuration(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
518 519 520 521 522 523 524 525 526 527

        /// <summary>Parse a media.
        /// This fetches (local) art, meta data and tracks information.
        /// The method is synchronous.
        /// This function could block indefinitely.
        /// Use libvlc_media_parse_with_options() instead
        /// libvlc_media_parse_with_options
        /// libvlc_media_get_meta
        /// libvlc_media_get_tracks_info
        /// </summary>
Martin Finkel's avatar
Martin Finkel committed
528
        public void Parse() => Native.LibVLCMediaParse(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
529 530 531

        /// <summary>Return true is the media descriptor object is parsed</summary>
        /// <returns>true if media object has been parsed otherwise it returns false</returns>
532
        public bool IsParsed => Native.LibVLCMediaIsParsed(NativeReference) != 0;
Martin Finkel's avatar
Martin Finkel committed
533 534 535 536 537 538 539 540

        /// <summary>Get Parsed status for media descriptor object.</summary>
        /// <returns>a value of the libvlc_media_parsed_status_t enum</returns>
        /// <remarks>
        /// <para>libvlc_MediaParsedChanged</para>
        /// <para>libvlc_media_parsed_status_t</para>
        /// <para>LibVLC 3.0.0 or later</para>
        /// </remarks>
541
        public MediaParsedStatus ParsedStatus => Native.LibVLCMediaGetParsedStatus(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
542 543 544 545 546 547 548 549

        /// <summary>Stop the parsing of the media</summary>
        /// <remarks>
        /// <para>When the media parsing is stopped, the libvlc_MediaParsedChanged event will</para>
        /// <para>be sent with the libvlc_media_parsed_status_timeout status.</para>
        /// <para>libvlc_media_parse_with_options</para>
        /// <para>LibVLC 3.0.0 or later</para>
        /// </remarks>
Martin Finkel's avatar
Martin Finkel committed
550
        public void ParseStop() => Native.LibVLCMediaParseStop(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567

        /// <summary>Get media descriptor's elementary streams description</summary>
        /// <para>address to store an allocated array of Elementary Streams</para>
        /// <para>descriptions (must be freed with libvlc_media_tracks_release</para>
        /// <para>by the caller) [OUT]</para>
        /// <returns>the number of Elementary Streams (zero on error)</returns>
        /// <remarks>
        /// <para>Note, you need to call libvlc_media_parse() or play the media at least once</para>
        /// <para>before calling this function.</para>
        /// <para>Not doing this will result in an empty array.</para>
        /// <para>LibVLC 2.1.0 and later.</para>
        /// </remarks>
        public IEnumerable<MediaTrack> Tracks
        {
            get
            {
                var arrayResultPtr = IntPtr.Zero;
568
                var count = Native.LibVLCMediaTracksGet(NativeReference, ref arrayResultPtr);
Martin Finkel's avatar
Martin Finkel committed
569 570 571 572 573 574
                if (count == 0 || arrayResultPtr == IntPtr.Zero) return Enumerable.Empty<MediaTrack>();

                var tracks = new List<MediaTrack>();
                for (var i = 0; i < count; i++)
                {
                    var ptr = Marshal.ReadIntPtr(arrayResultPtr, i * IntPtr.Size);
575
                    var managedStruct = MarshalUtils.PtrToStructure<MediaTrack>(ptr);
Martin Finkel's avatar
Martin Finkel committed
576 577 578 579
                    tracks.Add(managedStruct);

                }

580
                Native.LibVLCMediaTracksRelease(arrayResultPtr, count);
Martin Finkel's avatar
Martin Finkel committed
581 582 583 584 585 586 587 588 589 590 591

                return tracks;
            }
        }

        /// <summary>
        /// <para>Get subitems of media descriptor object. This will increment</para>
        /// <para>the reference count of supplied media descriptor object. Use</para>
        /// <para>libvlc_media_list_release() to decrement the reference counting.</para>
        /// </summary>
        /// <returns>list of media descriptor subitems or NULL</returns>
Martin Finkel's avatar
Martin Finkel committed
592 593
        public MediaList SubItems => new MediaList(Native.LibVLCMediaSubitems(NativeReference));
       
594
        public MediaType Type => Native.LibVLCMediaGetType(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
595 596 597 598 599 600 601 602 603 604 605 606 607 608

        /// <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>
Martin Finkel's avatar
Martin Finkel committed
609 610 611
        public bool AddSlave(MediaSlaveType type, uint priority, string uri) => 
            Native.LibVLCMediaAddSlaves(NativeReference, type, priority, uri) != 0;

Martin Finkel's avatar
Martin Finkel committed
612 613 614 615 616 617

        /// <summary>
        /// <para>Clear all slaves previously added by libvlc_media_slaves_add() or</para>
        /// <para>internally.</para>
        /// </summary>
        /// <remarks>LibVLC 3.0.0 and later.</remarks>
Martin Finkel's avatar
Martin Finkel committed
618
        public void ClearSlaves() => Native.LibVLCMediaClearSlaves(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
619 620 621 622 623 624 625 626 627 628 629 630

        /// <summary>Get a media descriptor's slave list</summary>
        /// <para>address to store an allocated array of slaves (must be</para>
        /// <para>freed with libvlc_media_slaves_release()) [OUT]</para>
        /// <returns>the number of slaves (zero on error)</returns>
        /// <remarks>
        /// <para>The list will contain slaves parsed by VLC or previously added by</para>
        /// <para>libvlc_media_slaves_add(). The typical use case of this function is to save</para>
        /// <para>a list of slave in a database for a later use.</para>
        /// <para>LibVLC 3.0.0 and later.</para>
        /// <para>libvlc_media_slaves_add</para>
        /// </remarks>
631 632 633 634
        public IEnumerable<MediaSlave> Slaves => MarshalUtils.Retrieve(NativeReference, (nativeRef,  arrayPtr) => Native.LibVLCMediaGetSlaves(nativeRef, ref arrayPtr),
                MarshalUtils.PtrToStructure<MediaSlaveStructure>,
                s => s.Build(),
                Native.LibVLCMediaReleaseSlaves);
Martin Finkel's avatar
Martin Finkel committed
635 636 637 638 639 640 641

        public override bool Equals(object obj)
        {
            return obj is Media media &&
                   EqualityComparer<IntPtr>.Default.Equals(NativeReference, media.NativeReference);
        }

Jérémy VIGNELLES's avatar
Jérémy VIGNELLES committed
642 643 644 645 646
        public override int GetHashCode()
        {
            return this.NativeReference.GetHashCode();
        }

Martin Finkel's avatar
Martin Finkel committed
647 648
        internal class StreamData
        {
Martin Finkel's avatar
Martin Finkel committed
649 650 651 652 653 654 655
            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; }
Martin Finkel's avatar
Martin Finkel committed
656 657 658 659
        }

        #region private

660
        [MonoPInvokeCallback(typeof(OpenMedia))]
661
        static int CallbackOpenMedia(IntPtr opaque, ref IntPtr data, out ulong size)
Martin Finkel's avatar
Martin Finkel committed
662 663 664 665 666
        {
            data = opaque;

            try
            {
Martin Finkel's avatar
Martin Finkel committed
667
                var streamData = GetStream(opaque);
Martin Finkel's avatar
Martin Finkel committed
668 669
                try
                {
Martin Finkel's avatar
Martin Finkel committed
670
                    size = (ulong)streamData.Stream.Length;
Martin Finkel's avatar
Martin Finkel committed
671 672 673 674 675 676 677
                }
                catch (Exception)
                {
                    // byte length of the bitstream or UINT64_MAX if unknown
                    size = ulong.MaxValue;
                }

Martin Finkel's avatar
Martin Finkel committed
678
                if (streamData.Stream.CanSeek)
Martin Finkel's avatar
Martin Finkel committed
679
                {
Martin Finkel's avatar
Martin Finkel committed
680
                    streamData.Stream.Seek(0L, SeekOrigin.Begin);
Martin Finkel's avatar
Martin Finkel committed
681 682 683 684 685 686 687 688 689 690 691
                }

                return 0;
            }
            catch (Exception)
            {
                size = 0UL;
                return -1;
            }
        }

692
        [MonoPInvokeCallback(typeof(ReadMedia))]
693
        static int CallbackReadMedia(IntPtr opaque, IntPtr buf, uint len)
Martin Finkel's avatar
Martin Finkel committed
694 695 696
        {
            try
            {
Martin Finkel's avatar
Martin Finkel committed
697
                var streamData = GetStream(opaque);
Martin Finkel's avatar
Martin Finkel committed
698 699
                int read;

Martin Finkel's avatar
Martin Finkel committed
700
                lock (streamData)
Martin Finkel's avatar
Martin Finkel committed
701
                {
Martin Finkel's avatar
Martin Finkel committed
702 703 704
                    var canRead = Math.Min((int)len, streamData.Buffer.Length);
                    read = streamData.Stream.Read(streamData.Buffer, 0, canRead);
                    Marshal.Copy(streamData.Buffer, 0, buf, read);
Martin Finkel's avatar
Martin Finkel committed
705 706 707 708 709 710 711 712 713 714
                }

                return read;
            }
            catch (Exception)
            {
                return -1;
            }
        }

715 716
        [MonoPInvokeCallback(typeof(SeekMedia))]
        static int CallbackSeekMedia(IntPtr opaque, ulong offset)
Martin Finkel's avatar
Martin Finkel committed
717 718 719
        {
            try
            {
Martin Finkel's avatar
Martin Finkel committed
720 721
                var streamData = GetStream(opaque);
                streamData.Stream.Seek((long)offset, SeekOrigin.Begin);
Martin Finkel's avatar
Martin Finkel committed
722 723 724 725 726 727 728 729
                return 0;
            }
            catch (Exception)
            {
                return -1;
            }
        }

730
        [MonoPInvokeCallback(typeof(CloseMedia))]
731
        static void CallbackCloseMedia(IntPtr opaque)
Martin Finkel's avatar
Martin Finkel committed
732 733 734
        {
            try
            {
Martin Finkel's avatar
Martin Finkel committed
735 736 737 738
                var streamData = GetStream(opaque);

                if (streamData.Stream.CanSeek)
                    streamData.Stream.Seek(0, SeekOrigin.Begin);
Martin Finkel's avatar
Martin Finkel committed
739 740 741 742 743 744 745
            }
            catch (Exception)
            {
                // ignored
            }
        }

Martin Finkel's avatar
Martin Finkel committed
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
        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);
        }

Martin Finkel's avatar
Martin Finkel committed
784 785
        void Retain()
        {
786
            if (NativeReference != IntPtr.Zero)
787
                Native.LibVLCMediaRetain(NativeReference);
Martin Finkel's avatar
Martin Finkel committed
788 789 790
        }

        #endregion
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823

        #region Events

        public event EventHandler<MediaMetaChangedEventArgs> MetaChanged
        {
            add => EventManager.AttachEvent(EventType.MediaMetaChanged, value);
            remove => EventManager.DetachEvent(EventType.MediaMetaChanged, value);
        }

        public event EventHandler<MediaParsedChangedEventArgs> ParsedChanged
        {
            add => EventManager.AttachEvent(EventType.MediaParsedChanged, value);
            remove => EventManager.DetachEvent(EventType.MediaParsedChanged, value);
        }

        public event EventHandler<MediaParsedChangedEventArgs> SubItemAdded
        {
            add => EventManager.AttachEvent(EventType.MediaSubItemAdded, value);
            remove => EventManager.DetachEvent(EventType.MediaSubItemAdded, value);
        }

        public event EventHandler<MediaDurationChangedEventArgs> DurationChanged
        {
            add => EventManager.AttachEvent(EventType.MediaDurationChanged, value);
            remove => EventManager.DetachEvent(EventType.MediaDurationChanged, value);
        }

        public event EventHandler<MediaFreedEventArgs> MediaFreed
        {
            add => EventManager.AttachEvent(EventType.MediaFreed, value);
            remove => EventManager.DetachEvent(EventType.MediaFreed, value);
        }

Martin Finkel's avatar
Martin Finkel committed
824
        public event EventHandler<MediaStateChangedEventArgs> StateChanged
825 826 827 828 829
        {
            add => EventManager.AttachEvent(EventType.MediaStateChanged, value);
            remove => EventManager.DetachEvent(EventType.MediaStateChanged, value);
        }

Martin Finkel's avatar
Martin Finkel committed
830
        public event EventHandler<MediaSubItemAddedEventArgs> SubItemTreeAdded
831 832 833 834
        {
            add => EventManager.AttachEvent(EventType.MediaSubItemTreeAdded, value);
            remove => EventManager.DetachEvent(EventType.MediaSubItemTreeAdded, value);
        }
835

836
        #endregion
837

838 839 840 841
        /// <summary>
        /// Dispose of this media
        /// </summary>
        /// <param name="disposing"></param>
842 843 844 845 846 847 848
        protected override void Dispose(bool disposing)
        {
            if (IsDisposed || NativeReference == IntPtr.Zero)
                return;

            base.Dispose(disposing);
        }
Martin Finkel's avatar
Martin Finkel committed
849 850
    }

851 852
    #region Callbacks

Martin Finkel's avatar
Martin Finkel committed
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
    /// <summary>
    /// <para>It consists of a media location and various optional meta data.</para>
    /// <para>@{</para>
    /// <para></para>
    /// <para>LibVLC media item/descriptor external API</para>
    /// </summary>
    /// <summary>Callback prototype to open a custom bitstream input media.</summary>
    /// <param name="opaque">private pointer as passed to libvlc_media_new_callbacks()</param>
    /// <param name="data">storage space for a private data pointer [OUT]</param>
    /// <param name="size">byte length of the bitstream or UINT64_MAX if unknown [OUT]</param>
    /// <returns>
    /// <para>0 on success, non-zero on error. In case of failure, the other</para>
    /// <para>callbacks will not be invoked and any value stored in *datap and *sizep is</para>
    /// <para>discarded.</para>
    /// </returns>
    /// <remarks>
    /// <para>The same media item can be opened multiple times. Each time, this callback</para>
    /// <para>is invoked. It should allocate and initialize any instance-specific</para>
    /// <para>resources, then store them in *datap. The instance resources can be freed</para>
    /// <para>in the</para>
    /// <para>For convenience, *datap is initially NULL and *sizep is initially 0.</para>
    /// </remarks>
875
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
876
    internal delegate int OpenMedia(IntPtr opaque, ref IntPtr data, out ulong size);
Martin Finkel's avatar
Martin Finkel committed
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892

    /// <summary>Callback prototype to read data from a custom bitstream input media.</summary>
    /// <param name="opaque">private pointer as set by the</param>
    /// <param name="buf">start address of the buffer to read data into</param>
    /// <param name="len">bytes length of the buffer</param>
    /// <returns>
    /// <para>strictly positive number of bytes read, 0 on end-of-stream,</para>
    /// <para>or -1 on non-recoverable error</para>
    /// </returns>
    /// <remarks>
    /// <para>callback</para>
    /// <para>If no data is immediately available, then the callback should sleep.</para>
    /// <para>The application is responsible for avoiding deadlock situations.</para>
    /// <para>In particular, the callback should return an error if playback is stopped;</para>
    /// <para>if it does not return, then libvlc_media_player_stop() will never return.</para>
    /// </remarks>
893
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
894
    internal delegate int ReadMedia(IntPtr opaque, IntPtr buf, uint len);
Martin Finkel's avatar
Martin Finkel committed
895 896 897 898 899 900

    /// <summary>Callback prototype to seek a custom bitstream input media.</summary>
    /// <param name="opaque">private pointer as set by the</param>
    /// <param name="offset">absolute byte offset to seek to</param>
    /// <returns>0 on success, -1 on error.</returns>
    /// <remarks>callback</remarks>
901
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
902
    internal delegate int SeekMedia(IntPtr opaque, ulong offset);
Martin Finkel's avatar
Martin Finkel committed
903 904 905 906

    /// <summary>Callback prototype to close a custom bitstream input media.</summary>
    /// <param name="opaque">private pointer as set by the</param>
    /// <remarks>callback</remarks>
907
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
908
    internal delegate void CloseMedia(IntPtr opaque);
909

910 911
    #endregion

912
    #region enums
913

914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
    /// <summary>Note the order of libvlc_state_t enum must match exactly the order of</summary>
    /// <remarks>
    /// <para>mediacontrol_PlayerStatus,</para>
    /// <para>input_state_e enums,</para>
    /// <para>and VideoLAN.LibVLCSharp.State (at bindings/cil/src/media.cs).</para>
    /// <para>Expected states by web plugins are:</para>
    /// <para>IDLE/CLOSE=0, OPENING=1, PLAYING=3, PAUSED=4,</para>
    /// <para>STOPPING=5, ENDED=6, ERROR=7</para>
    /// </remarks>
    public enum VLCState
    {
        NothingSpecial = 0,
        Opening = 1,
        Buffering = 2,
        Playing = 3,
        Paused = 4,
        Stopped = 5,
        Ended = 6,
        Error = 7
    }

    public enum TrackType
    {
        Unknown = -1,
        Audio = 0,
        Video = 1,
        Text = 2
    }

    public enum VideoOrientation
    {
        /// <summary>Normal. Top line represents top, left column left.</summary>
        TopLeft = 0,
        /// <summary>Flipped horizontally</summary>
        TopRight = 1,
        /// <summary>Flipped vertically</summary>
        BottomLeft = 2,
        /// <summary>Rotated 180 degrees</summary>
        BottomRight = 3,
        /// <summary>Transposed</summary>
        LeftTop = 4,
        /// <summary>Rotated 90 degrees clockwise (or 270 anti-clockwise)</summary>
        LeftBottom = 5,
        /// <summary>Rotated 90 degrees anti-clockwise</summary>
        RightTop = 6,
        /// <summary>Anti-transposed</summary>
        RightBottom = 7
    }

    [Flags]
    public enum VideoProjection
    {
        Rectangular = 0,
        /// <summary>360 spherical</summary>
        Equirectangular = 1,
        CubemapLayoutStandard = 256
    }

    /// <summary>Type of a media slave: subtitle or audio.</summary>
    public enum MediaSlaveType
    {
975 976 977
        /// <summary>
        /// Subtitle
        /// </summary>
978
        Subtitle = 0,
979 980 981 982
        
        /// <summary>
        /// Audio
        /// </summary>
983 984
        Audio = 1
    }
985
    #endregion
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000

    public class MediaConfiguration
    {
        HashSet<string> _options = new HashSet<string>();

        public MediaConfiguration EnableHardwareDecoding()
        {
#if ANDROID
            _options.Add(":codec=mediacodec_ndk");
#endif
            return this;
        }

        public string Build() => string.Join(",", _options);
    }
Martin Finkel's avatar
Martin Finkel committed
1001
}