libvlc: media: add projection in libvlc_video_track_t
[vlc.git] / lib / media.c
index 2c01c80..fdd2445 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 
 #include <assert.h>
+#include <errno.h>
 
 #include <vlc/libvlc.h>
 #include <vlc/libvlc_media.h>
@@ -42,6 +43,7 @@
 
 #include "libvlc_internal.h"
 #include "media_internal.h"
+#include "media_list_internal.h"
 
 static const vlc_meta_type_t libvlc_to_vlc_meta[] =
 {
@@ -61,7 +63,16 @@ static const vlc_meta_type_t libvlc_to_vlc_meta[] =
     [libvlc_meta_Publisher]    = vlc_meta_Publisher,
     [libvlc_meta_EncodedBy]    = vlc_meta_EncodedBy,
     [libvlc_meta_ArtworkURL]   = vlc_meta_ArtworkURL,
-    [libvlc_meta_TrackID]      = vlc_meta_TrackID
+    [libvlc_meta_TrackID]      = vlc_meta_TrackID,
+    [libvlc_meta_TrackTotal]   = vlc_meta_TrackTotal,
+    [libvlc_meta_Director]     = vlc_meta_Director,
+    [libvlc_meta_Season]       = vlc_meta_Season,
+    [libvlc_meta_Episode]      = vlc_meta_Episode,
+    [libvlc_meta_ShowName]     = vlc_meta_ShowName,
+    [libvlc_meta_Actors]       = vlc_meta_Actors,
+    [libvlc_meta_AlbumArtist]  = vlc_meta_AlbumArtist,
+    [libvlc_meta_DiscNumber]   = vlc_meta_DiscNumber,
+    [libvlc_meta_DiscTotal]    = vlc_meta_DiscTotal
 };
 
 static const libvlc_meta_t vlc_to_libvlc_meta[] =
@@ -79,12 +90,59 @@ static const libvlc_meta_t vlc_to_libvlc_meta[] =
     [vlc_meta_URL]          = libvlc_meta_URL,
     [vlc_meta_Language]     = libvlc_meta_Language,
     [vlc_meta_NowPlaying]   = libvlc_meta_NowPlaying,
+    [vlc_meta_ESNowPlaying] = libvlc_meta_NowPlaying,
     [vlc_meta_Publisher]    = libvlc_meta_Publisher,
     [vlc_meta_EncodedBy]    = libvlc_meta_EncodedBy,
     [vlc_meta_ArtworkURL]   = libvlc_meta_ArtworkURL,
-    [vlc_meta_TrackID]      = libvlc_meta_TrackID
+    [vlc_meta_TrackID]      = libvlc_meta_TrackID,
+    [vlc_meta_TrackTotal]   = libvlc_meta_TrackTotal,
+    [vlc_meta_Director]     = libvlc_meta_Director,
+    [vlc_meta_Season]       = libvlc_meta_Season,
+    [vlc_meta_Episode]      = libvlc_meta_Episode,
+    [vlc_meta_ShowName]     = libvlc_meta_ShowName,
+    [vlc_meta_Actors]       = libvlc_meta_Actors,
+    [vlc_meta_AlbumArtist]  = libvlc_meta_AlbumArtist,
+    [vlc_meta_DiscNumber]   = libvlc_meta_DiscNumber,
+    [vlc_meta_DiscTotal]    = libvlc_meta_DiscTotal
 };
 
+static_assert(
+    ORIENT_TOP_LEFT     == (int) libvlc_video_orient_top_left &&
+    ORIENT_TOP_RIGHT    == (int) libvlc_video_orient_top_right &&
+    ORIENT_BOTTOM_LEFT  == (int) libvlc_video_orient_bottom_left &&
+    ORIENT_BOTTOM_RIGHT == (int) libvlc_video_orient_bottom_right &&
+    ORIENT_LEFT_TOP     == (int) libvlc_video_orient_left_top &&
+    ORIENT_LEFT_BOTTOM  == (int) libvlc_video_orient_left_bottom &&
+    ORIENT_RIGHT_TOP    == (int) libvlc_video_orient_right_top &&
+    ORIENT_RIGHT_BOTTOM == (int) libvlc_video_orient_right_bottom,
+    "Mismatch between libvlc_video_orient_t and video_orientation_t" );
+
+static_assert(
+    PROJECTION_MODE_RECTANGULAR             == (int) libvlc_video_projection_rectangular &&
+    PROJECTION_MODE_EQUIRECTANGULAR         == (int) libvlc_video_projection_equirectangular &&
+    PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD == (int) libvlc_video_projection_cubemap_layout_standard,
+    "Mismatch between libvlc_video_projection_t and video_projection_mode_t" );
+
+static libvlc_media_list_t *media_get_subitems( libvlc_media_t * p_md,
+                                                bool b_create )
+{
+    libvlc_media_list_t *p_subitems = NULL;
+
+    vlc_mutex_lock( &p_md->subitems_lock );
+    if( p_md->p_subitems == NULL && b_create )
+    {
+        p_md->p_subitems = libvlc_media_list_new( p_md->p_libvlc_instance );
+        if( p_md->p_subitems != NULL )
+        {
+            p_md->p_subitems->b_read_only = true;
+            p_md->p_subitems->p_internal_md = p_md;
+        }
+    }
+    p_subitems = p_md->p_subitems;
+    vlc_mutex_unlock( &p_md->subitems_lock );
+    return p_subitems;
+}
+
 /**************************************************************************
  * input_item_subitem_added (Private) (vlc event Callback)
  **************************************************************************/
@@ -93,6 +151,7 @@ static void input_item_subitem_added( const vlc_event_t *p_event,
 {
     libvlc_media_t * p_md = user_data;
     libvlc_media_t * p_md_child;
+    libvlc_media_list_t *p_subitems;
     libvlc_event_t event;
 
     p_md_child = libvlc_media_new_from_input_item(
@@ -100,14 +159,12 @@ static void input_item_subitem_added( const vlc_event_t *p_event,
                 p_event->u.input_item_subitem_added.p_new_child );
 
     /* Add this to our media list */
-    if( !p_md->p_subitems )
+    p_subitems = media_get_subitems( p_md, true );
+    if( p_subitems != NULL )
     {
-        p_md->p_subitems = libvlc_media_list_new( p_md->p_libvlc_instance );
-        libvlc_media_list_set_media( p_md->p_subitems, p_md );
-    }
-    if( p_md->p_subitems )
-    {
-        libvlc_media_list_add_media( p_md->p_subitems, p_md_child );
+        libvlc_media_list_lock( p_subitems );
+        libvlc_media_list_internal_add_media( p_subitems, p_md_child );
+        libvlc_media_list_unlock( p_subitems );
     }
 
     /* Construct the event */
@@ -119,6 +176,24 @@ static void input_item_subitem_added( const vlc_event_t *p_event,
     libvlc_media_release( p_md_child );
 }
 
+/**************************************************************************
+ * input_item_subitemtree_added (Private) (vlc event Callback)
+ **************************************************************************/
+static void input_item_subitemtree_added( const vlc_event_t * p_event,
+                                          void * user_data )
+{
+    VLC_UNUSED( p_event );
+    libvlc_media_t * p_md = user_data;
+    libvlc_event_t event;
+
+    /* Construct the event */
+    event.type = libvlc_MediaSubItemTreeAdded;
+    event.u.media_subitemtree_added.item = p_md;
+
+    /* Send the event */
+    libvlc_event_send( p_md->p_event_manager, &event );
+}
+
 /**************************************************************************
  * input_item_meta_changed (Private) (vlc event Callback)
  **************************************************************************/
@@ -155,29 +230,78 @@ static void input_item_duration_changed( const vlc_event_t *p_event,
     libvlc_event_send( p_md->p_event_manager, &event );
 }
 
-/**************************************************************************
- * input_item_preparsed_changed (Private) (vlc event Callback)
- **************************************************************************/
-static void input_item_preparsed_changed(const vlc_event_t *p_event,
-                                         void * user_data)
+static void send_parsed_changed( libvlc_media_t *p_md,
+                                 libvlc_media_parsed_status_t new_status )
 {
-    libvlc_media_t *media = user_data;
     libvlc_event_t event;
 
-    /* Eventually notify libvlc_media_parse() */
-    vlc_mutex_lock(&media->parsed_lock);
-    media->is_parsed = true;
-    vlc_cond_broadcast(&media->parsed_cond);
-    vlc_mutex_unlock(&media->parsed_lock);
+    vlc_mutex_lock( &p_md->parsed_lock );
+    if( p_md->parsed_status == new_status )
+    {
+        vlc_mutex_unlock( &p_md->parsed_lock );
+        return;
+    }
+
+    /* Legacy: notify libvlc_media_parse */
+    if( !p_md->is_parsed )
+    {
+        p_md->is_parsed = true;
+        vlc_cond_broadcast( &p_md->parsed_cond );
+    }
 
+    p_md->parsed_status = new_status;
+    if( p_md->parsed_status == libvlc_media_parsed_status_skipped )
+        p_md->has_asked_preparse = false;
+
+    vlc_mutex_unlock( &p_md->parsed_lock );
+
+    if( new_status == libvlc_media_parsed_status_done )
+    {
+        libvlc_media_list_t *p_subitems = media_get_subitems( p_md, false );
+        if( p_subitems != NULL )
+        {
+            /* notify the media list */
+            libvlc_media_list_lock( p_subitems );
+            libvlc_media_list_internal_end_reached( p_subitems );
+            libvlc_media_list_unlock( p_subitems );
+        }
+    }
 
     /* Construct the event */
     event.type = libvlc_MediaParsedChanged;
-    event.u.media_parsed_changed.new_status =
-        p_event->u.input_item_preparsed_changed.new_status;
+    event.u.media_parsed_changed.new_status = new_status;
 
     /* Send the event */
-    libvlc_event_send(media->p_event_manager, &event);
+    libvlc_event_send( p_md->p_event_manager, &event );
+}
+
+/**************************************************************************
+ * input_item_preparse_ended (Private) (vlc event Callback)
+ **************************************************************************/
+static void input_item_preparse_ended( const vlc_event_t * p_event,
+                                       void * user_data )
+{
+    libvlc_media_t * p_md = user_data;
+    libvlc_media_parsed_status_t new_status;
+
+    switch( p_event->u.input_item_preparse_ended.new_status )
+    {
+        case ITEM_PREPARSE_SKIPPED:
+            new_status = libvlc_media_parsed_status_skipped;
+            break;
+        case ITEM_PREPARSE_FAILED:
+            new_status = libvlc_media_parsed_status_failed;
+            break;
+        case ITEM_PREPARSE_TIMEOUT:
+            new_status = libvlc_media_parsed_status_timeout;
+            break;
+        case ITEM_PREPARSE_DONE:
+            new_status = libvlc_media_parsed_status_done;
+            break;
+        default:
+            return;
+    }
+    send_parsed_changed( p_md, new_status );
 }
 
 /**************************************************************************
@@ -198,8 +322,12 @@ static void install_input_item_observer( libvlc_media_t *p_md )
                       input_item_duration_changed,
                       p_md );
     vlc_event_attach( &p_md->p_input_item->event_manager,
-                      vlc_InputItemPreparsedChanged,
-                      input_item_preparsed_changed,
+                      vlc_InputItemSubItemTreeAdded,
+                      input_item_subitemtree_added,
+                      p_md );
+    vlc_event_attach( &p_md->p_input_item->event_manager,
+                      vlc_InputItemPreparseEnded,
+                      input_item_preparse_ended,
                       p_md );
 }
 
@@ -221,8 +349,12 @@ static void uninstall_input_item_observer( libvlc_media_t *p_md )
                       input_item_duration_changed,
                       p_md );
     vlc_event_detach( &p_md->p_input_item->event_manager,
-                      vlc_InputItemPreparsedChanged,
-                      input_item_preparsed_changed,
+                      vlc_InputItemSubItemTreeAdded,
+                      input_item_subitemtree_added,
+                      p_md );
+    vlc_event_detach( &p_md->p_input_item->event_manager,
+                      vlc_InputItemPreparseEnded,
+                      input_item_preparse_ended,
                       p_md );
 }
 
@@ -256,6 +388,7 @@ libvlc_media_t * libvlc_media_new_from_input_item(
 
     vlc_cond_init(&p_md->parsed_cond);
     vlc_mutex_init(&p_md->parsed_lock);
+    vlc_mutex_init(&p_md->subitems_lock);
 
     p_md->state = libvlc_NothingSpecial;
 
@@ -263,25 +396,18 @@ libvlc_media_t * libvlc_media_new_from_input_item(
      * It can give a bunch of item to read. */
     p_md->p_subitems        = NULL;
 
-    p_md->p_event_manager = libvlc_event_manager_new( p_md, p_instance );
+    p_md->p_event_manager = libvlc_event_manager_new( p_md );
     if( unlikely(p_md->p_event_manager == NULL) )
     {
         free(p_md);
         return NULL;
     }
 
-    libvlc_event_manager_t *em = p_md->p_event_manager;
-    libvlc_event_manager_register_event_type(em, libvlc_MediaMetaChanged);
-    libvlc_event_manager_register_event_type(em, libvlc_MediaSubItemAdded);
-    libvlc_event_manager_register_event_type(em, libvlc_MediaFreed);
-    libvlc_event_manager_register_event_type(em, libvlc_MediaDurationChanged);
-    libvlc_event_manager_register_event_type(em, libvlc_MediaStateChanged);
-    libvlc_event_manager_register_event_type(em, libvlc_MediaParsedChanged);
-
     vlc_gc_incref( p_md->p_input_item );
 
     install_input_item_observer( p_md );
 
+    libvlc_retain( p_instance );
     return p_md;
 }
 
@@ -313,10 +439,10 @@ libvlc_media_t *libvlc_media_new_location( libvlc_instance_t *p_instance,
 libvlc_media_t *libvlc_media_new_path( libvlc_instance_t *p_instance,
                                        const char *path )
 {
-    char *mrl = make_URI( path, "file" );
+    char *mrl = vlc_path2uri( path, NULL );
     if( unlikely(mrl == NULL) )
     {
-        libvlc_printerr( "Not enough memory" );
+        libvlc_printerr( "%s", vlc_strerror_c(errno) );
         return NULL;
     }
 
@@ -333,6 +459,26 @@ libvlc_media_t *libvlc_media_new_fd( libvlc_instance_t *p_instance, int fd )
     return libvlc_media_new_location( p_instance, mrl );
 }
 
+libvlc_media_t *libvlc_media_new_callbacks(libvlc_instance_t *p_instance,
+                                           libvlc_media_open_cb open_cb,
+                                           libvlc_media_read_cb read_cb,
+                                           libvlc_media_seek_cb seek_cb,
+                                           libvlc_media_close_cb close_cb,
+                                           void *opaque)
+{
+    libvlc_media_t *m = libvlc_media_new_location(p_instance, "imem://");
+    if (unlikely(m == NULL))
+        return NULL;
+
+    assert(read_cb != NULL);
+    input_item_AddOpaque(m->p_input_item, "imem-data", opaque);
+    input_item_AddOpaque(m->p_input_item, "imem-open", open_cb);
+    input_item_AddOpaque(m->p_input_item, "imem-read", read_cb);
+    input_item_AddOpaque(m->p_input_item, "imem-seek", seek_cb);
+    input_item_AddOpaque(m->p_input_item, "imem-close", close_cb);
+    return m;
+}
+
 /**************************************************************************
  * Create a new media descriptor object
  **************************************************************************/
@@ -341,6 +487,7 @@ libvlc_media_t * libvlc_media_new_as_node( libvlc_instance_t *p_instance,
 {
     input_item_t * p_input_item;
     libvlc_media_t * p_md;
+    libvlc_media_list_t * p_subitems;
 
     p_input_item = input_item_New( "vlc://nop", psz_name );
 
@@ -351,8 +498,13 @@ libvlc_media_t * libvlc_media_new_as_node( libvlc_instance_t *p_instance,
     }
 
     p_md = libvlc_media_new_from_input_item( p_instance, p_input_item );
+    input_item_Release( p_input_item );
 
-    p_md->p_subitems = libvlc_media_list_new( p_md->p_libvlc_instance );
+    p_subitems = media_get_subitems( p_md, true );
+    if( p_subitems == NULL) {
+        libvlc_media_release( p_md );
+        return NULL;
+    }
 
     return p_md;
 }
@@ -395,14 +547,19 @@ void libvlc_media_release( libvlc_media_t *p_md )
     if( p_md->i_refcount > 0 )
         return;
 
+    uninstall_input_item_observer( p_md );
+
+    /* Cancel asynchronous parsing (if any) */
+    libvlc_MetadataCancel( p_md->p_libvlc_instance->p_libvlc_int, p_md );
+
     if( p_md->p_subitems )
         libvlc_media_list_release( p_md->p_subitems );
 
-    uninstall_input_item_observer( p_md );
     vlc_gc_decref( p_md->p_input_item );
 
     vlc_cond_destroy( &p_md->parsed_cond );
     vlc_mutex_destroy( &p_md->parsed_lock );
+    vlc_mutex_destroy( &p_md->subitems_lock );
 
     /* Construct the event */
     libvlc_event_t event;
@@ -413,7 +570,7 @@ void libvlc_media_release( libvlc_media_t *p_md )
     libvlc_event_send( p_md->p_event_manager, &event );
 
     libvlc_event_manager_release( p_md->p_event_manager );
-
+    libvlc_release( p_md->p_libvlc_instance );
     free( p_md );
 }
 
@@ -452,13 +609,21 @@ libvlc_media_get_mrl( libvlc_media_t * p_md )
 
 char *libvlc_media_get_meta( libvlc_media_t *p_md, libvlc_meta_t e_meta )
 {
-    char *psz_meta = input_item_GetMeta( p_md->p_input_item,
-                                         libvlc_to_vlc_meta[e_meta] );
-    /* Should be integrated in core */
-    if( psz_meta == NULL && e_meta == libvlc_meta_Title
-     && p_md->p_input_item->psz_name != NULL )
-        psz_meta = strdup( p_md->p_input_item->psz_name );
+    char *psz_meta = NULL;
 
+    if( e_meta == libvlc_meta_NowPlaying )
+    {
+        psz_meta = input_item_GetNowPlayingFb( p_md->p_input_item );
+    }
+    else
+    {
+        psz_meta = input_item_GetMeta( p_md->p_input_item,
+                                             libvlc_to_vlc_meta[e_meta] );
+        /* Should be integrated in core */
+        if( psz_meta == NULL && e_meta == libvlc_meta_Title
+         && p_md->p_input_item->psz_name != NULL )
+            psz_meta = strdup( p_md->p_input_item->psz_name );
+    }
     return psz_meta;
 }
 
@@ -475,8 +640,7 @@ void libvlc_media_set_meta( libvlc_media_t *p_md, libvlc_meta_t e_meta, const ch
 int libvlc_media_save_meta( libvlc_media_t *p_md )
 {
     assert( p_md );
-    vlc_object_t *p_obj = VLC_OBJECT(libvlc_priv(
-                            p_md->p_libvlc_instance->p_libvlc_int)->p_playlist);
+    vlc_object_t *p_obj = VLC_OBJECT(p_md->p_libvlc_instance->p_libvlc_int);
     return input_item_WriteMeta( p_obj, p_md->p_input_item ) == VLC_SUCCESS;
 }
 
@@ -518,9 +682,10 @@ libvlc_media_set_state( libvlc_media_t *p_md,
 libvlc_media_list_t *
 libvlc_media_subitems( libvlc_media_t * p_md )
 {
-    if( p_md->p_subitems )
-        libvlc_media_list_retain( p_md->p_subitems );
-    return p_md->p_subitems;
+    libvlc_media_list_t *p_subitems = media_get_subitems( p_md, true );
+    if( p_subitems )
+        libvlc_media_list_retain( p_subitems );
+    return p_subitems;
 }
 
 /**************************************************************************
@@ -589,15 +754,55 @@ libvlc_media_get_duration( libvlc_media_t * p_md )
     return from_mtime(input_item_GetDuration( p_md->p_input_item ));
 }
 
-static int media_parse(libvlc_media_t *media)
+static int media_parse(libvlc_media_t *media, bool b_async,
+                       libvlc_media_parse_flag_t parse_flag, int timeout)
 {
-    /* TODO: fetcher and parser independent of playlist */
-    playlist_t *playlist =
-        libvlc_priv (media->p_libvlc_instance->p_libvlc_int)->p_playlist;
+    bool needed;
+
+    vlc_mutex_lock(&media->parsed_lock);
+    needed = !media->has_asked_preparse;
+    media->has_asked_preparse = true;
+    if (needed)
+        media->is_parsed = false;
+    vlc_mutex_unlock(&media->parsed_lock);
+
+    if (needed)
+    {
+        libvlc_int_t *libvlc = media->p_libvlc_instance->p_libvlc_int;
+        input_item_t *item = media->p_input_item;
+        input_item_meta_request_option_t art_scope = META_REQUEST_OPTION_NONE;
+        input_item_meta_request_option_t parse_scope = META_REQUEST_OPTION_SCOPE_LOCAL;
+        int ret;
+
+        if (parse_flag & libvlc_media_fetch_local)
+            art_scope |= META_REQUEST_OPTION_SCOPE_LOCAL;
+        if (parse_flag & libvlc_media_fetch_network)
+            art_scope |= META_REQUEST_OPTION_SCOPE_NETWORK;
+        if (art_scope != META_REQUEST_OPTION_NONE) {
+            ret = libvlc_ArtRequest(libvlc, item, art_scope);
+            if (ret != VLC_SUCCESS)
+                return ret;
+        }
+
+        if (parse_flag & libvlc_media_parse_network)
+            parse_scope |= META_REQUEST_OPTION_SCOPE_NETWORK;
+        if (parse_flag & libvlc_media_do_interact)
+            parse_scope |= META_REQUEST_OPTION_DO_INTERACT;
+        ret = libvlc_MetadataRequest(libvlc, item, parse_scope, timeout, media);
+        if (ret != VLC_SUCCESS)
+            return ret;
+    }
+    else
+        return VLC_EGENERIC;
 
-    /* TODO: Fetch art on need basis. But how not to break compatibility? */
-    playlist_AskForArtEnqueue(playlist, media->p_input_item );
-    return playlist_PreparseEnqueue(playlist, media->p_input_item);
+    if (!b_async)
+    {
+        vlc_mutex_lock(&media->parsed_lock);
+        while (!media->is_parsed)
+            vlc_cond_wait(&media->parsed_cond, &media->parsed_lock);
+        vlc_mutex_unlock(&media->parsed_lock);
+    }
+    return VLC_SUCCESS;
 }
 
 /**************************************************************************
@@ -606,21 +811,7 @@ static int media_parse(libvlc_media_t *media)
 void
 libvlc_media_parse(libvlc_media_t *media)
 {
-    vlc_mutex_lock(&media->parsed_lock);
-    if (!media->has_asked_preparse)
-    {
-        media->has_asked_preparse = true;
-        vlc_mutex_unlock(&media->parsed_lock);
-
-        if (media_parse(media))
-            /* Parse failed: do not wait! */
-            return;
-        vlc_mutex_lock(&media->parsed_lock);
-    }
-
-    while (!media->is_parsed)
-        vlc_cond_wait(&media->parsed_cond, &media->parsed_lock);
-    vlc_mutex_unlock(&media->parsed_lock);
+    media_parse( media, false, libvlc_media_fetch_local, -1 );
 }
 
 /**************************************************************************
@@ -629,15 +820,24 @@ libvlc_media_parse(libvlc_media_t *media)
 void
 libvlc_media_parse_async(libvlc_media_t *media)
 {
-    bool needed;
+    media_parse( media, true, libvlc_media_fetch_local, -1 );
+}
 
-    vlc_mutex_lock(&media->parsed_lock);
-    needed = !media->has_asked_preparse;
-    media->has_asked_preparse = true;
-    vlc_mutex_unlock(&media->parsed_lock);
+/**************************************************************************
+ * Parse the media asynchronously with options.
+ **************************************************************************/
+int
+libvlc_media_parse_with_options( libvlc_media_t *media,
+                                 libvlc_media_parse_flag_t parse_flag,
+                                 int timeout )
+{
+    return media_parse( media, true, parse_flag, timeout ) == VLC_SUCCESS ? 0 : -1;
+}
 
-    if (needed)
-        media_parse(media);
+void
+libvlc_media_parse_stop( libvlc_media_t *media )
+{
+    libvlc_MetadataCancel( media->p_libvlc_instance->p_libvlc_int, media );
 }
 
 /**************************************************************************
@@ -654,6 +854,17 @@ libvlc_media_is_parsed(libvlc_media_t *media)
     return parsed;
 }
 
+libvlc_media_parsed_status_t
+libvlc_media_get_parsed_status(libvlc_media_t *media)
+{
+    libvlc_media_parsed_status_t status;
+
+    vlc_mutex_lock(&media->parsed_lock);
+    status = media->parsed_status;
+    vlc_mutex_unlock(&media->parsed_lock);
+    return status;
+}
+
 /**************************************************************************
  * Sets media descriptor's user_data. user_data is specialized data
  * accessed by the host application, VLC.framework uses it as a pointer to
@@ -718,8 +929,8 @@ libvlc_media_get_tracks_info( libvlc_media_t *p_md, libvlc_media_track_info_t **
             break;
         case VIDEO_ES:
             p_mes->i_type = libvlc_track_video;
-            p_mes->u.video.i_height = p_es->video.i_height;
-            p_mes->u.video.i_width = p_es->video.i_width;
+            p_mes->u.video.i_height = p_es->video.i_visible_height;
+            p_mes->u.video.i_width = p_es->video.i_visible_width;
             break;
         case AUDIO_ES:
             p_mes->i_type = libvlc_track_audio;
@@ -735,3 +946,330 @@ libvlc_media_get_tracks_info( libvlc_media_t *p_md, libvlc_media_track_info_t **
     vlc_mutex_unlock( &p_input_item->lock );
     return i_es;
 }
+
+unsigned
+libvlc_media_tracks_get( libvlc_media_t *p_md, libvlc_media_track_t *** pp_es )
+{
+    assert( p_md );
+
+    input_item_t *p_input_item = p_md->p_input_item;
+    vlc_mutex_lock( &p_input_item->lock );
+
+    const int i_es = p_input_item->i_es;
+    *pp_es = (i_es > 0) ? calloc( i_es, sizeof(**pp_es) ) : NULL;
+
+    if( !*pp_es ) /* no ES, or OOM */
+    {
+        vlc_mutex_unlock( &p_input_item->lock );
+        return 0;
+    }
+
+    /* Fill array */
+    for( int i = 0; i < i_es; i++ )
+    {
+        libvlc_media_track_t *p_mes = calloc( 1, sizeof(*p_mes) );
+        if ( p_mes )
+        {
+            p_mes->audio = malloc( __MAX(__MAX(sizeof(*p_mes->audio),
+                                               sizeof(*p_mes->video)),
+                                               sizeof(*p_mes->subtitle)) );
+        }
+        if ( !p_mes || !p_mes->audio )
+        {
+            libvlc_media_tracks_release( *pp_es, i_es );
+            *pp_es = NULL;
+            free( p_mes );
+            vlc_mutex_unlock( &p_input_item->lock );
+            return 0;
+        }
+        (*pp_es)[i] = p_mes;
+
+        const es_format_t *p_es = p_input_item->es[i];
+
+        p_mes->i_codec = p_es->i_codec;
+        p_mes->i_original_fourcc = p_es->i_original_fourcc;
+        p_mes->i_id = p_es->i_id;
+
+        p_mes->i_profile = p_es->i_profile;
+        p_mes->i_level = p_es->i_level;
+
+        p_mes->i_bitrate = p_es->i_bitrate;
+        p_mes->psz_language = p_es->psz_language != NULL ? strdup(p_es->psz_language) : NULL;
+        p_mes->psz_description = p_es->psz_description != NULL ? strdup(p_es->psz_description) : NULL;
+
+        switch(p_es->i_cat)
+        {
+        case UNKNOWN_ES:
+        default:
+            p_mes->i_type = libvlc_track_unknown;
+            break;
+        case VIDEO_ES:
+            p_mes->i_type = libvlc_track_video;
+            p_mes->video->i_height = p_es->video.i_visible_height;
+            p_mes->video->i_width = p_es->video.i_visible_width;
+            p_mes->video->i_sar_num = p_es->video.i_sar_num;
+            p_mes->video->i_sar_den = p_es->video.i_sar_den;
+            p_mes->video->i_frame_rate_num = p_es->video.i_frame_rate;
+            p_mes->video->i_frame_rate_den = p_es->video.i_frame_rate_base;
+
+            assert( p_es->video.orientation >= ORIENT_TOP_LEFT &&
+                    p_es->video.orientation <= ORIENT_RIGHT_BOTTOM );
+            p_mes->video->i_orientation = (int) p_es->video.orientation;
+
+            assert( ( p_es->video.projection_mode >= PROJECTION_MODE_RECTANGULAR &&
+                    p_es->video.projection_mode <= PROJECTION_MODE_EQUIRECTANGULAR ) ||
+                    ( p_es->video.projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD ) );
+            p_mes->video->i_projection = (int) p_es->video.projection_mode;
+
+            p_mes->video->pose.f_yaw_degrees = p_es->video.pose.f_yaw_degrees;
+            p_mes->video->pose.f_pitch_degrees = p_es->video.pose.f_pitch_degrees;
+            p_mes->video->pose.f_roll_degrees = p_es->video.pose.f_roll_degrees;
+            p_mes->video->pose.f_fov_degrees = p_es->video.pose.f_fov_degrees;
+            break;
+        case AUDIO_ES:
+            p_mes->i_type = libvlc_track_audio;
+            p_mes->audio->i_channels = p_es->audio.i_channels;
+            p_mes->audio->i_rate = p_es->audio.i_rate;
+            break;
+        case SPU_ES:
+            p_mes->i_type = libvlc_track_text;
+            p_mes->subtitle->psz_encoding = p_es->subs.psz_encoding != NULL ?
+                                            strdup(p_es->subs.psz_encoding) : NULL;
+            break;
+        }
+    }
+
+    vlc_mutex_unlock( &p_input_item->lock );
+    return i_es;
+}
+
+/**************************************************************************
+ * Get codec description from media elementary stream
+ **************************************************************************/
+const char *
+libvlc_media_get_codec_description( libvlc_track_type_t i_type,
+                                    uint32_t i_codec )
+{
+    switch( i_type )
+    {
+        case libvlc_track_audio:
+            return vlc_fourcc_GetDescription( AUDIO_ES, i_codec );
+        case libvlc_track_video:
+            return vlc_fourcc_GetDescription( VIDEO_ES, i_codec );
+        case libvlc_track_text:
+            return vlc_fourcc_GetDescription( SPU_ES, i_codec );
+        case libvlc_track_unknown:
+        default:
+            return vlc_fourcc_GetDescription( UNKNOWN_ES, i_codec );
+    }
+}
+
+/**************************************************************************
+ * Release media descriptor's elementary streams description array
+ **************************************************************************/
+void libvlc_media_tracks_release( libvlc_media_track_t **p_tracks, unsigned i_count )
+{
+    for( unsigned i = 0; i < i_count; ++i )
+    {
+        if ( !p_tracks[i] )
+            continue;
+        free( p_tracks[i]->psz_language );
+        free( p_tracks[i]->psz_description );
+        switch( p_tracks[i]->i_type )
+        {
+        case libvlc_track_audio:
+            break;
+        case libvlc_track_video:
+            break;
+        case libvlc_track_text:
+            free( p_tracks[i]->subtitle->psz_encoding );
+            break;
+        case libvlc_track_unknown:
+        default:
+            break;
+        }
+        free( p_tracks[i]->audio );
+        free( p_tracks[i] );
+    }
+    free( p_tracks );
+}
+
+/**************************************************************************
+ * Get the media type of the media descriptor object
+ **************************************************************************/
+libvlc_media_type_t libvlc_media_get_type( libvlc_media_t *p_md )
+{
+    assert( p_md );
+
+    int i_type;
+    input_item_t *p_input_item = p_md->p_input_item;
+
+    vlc_mutex_lock( &p_input_item->lock );
+    i_type = p_md->p_input_item->i_type;
+    vlc_mutex_unlock( &p_input_item->lock );
+
+    switch( i_type )
+    {
+    case ITEM_TYPE_FILE:
+        return libvlc_media_type_file;
+    case ITEM_TYPE_NODE:
+    case ITEM_TYPE_DIRECTORY:
+        return libvlc_media_type_directory;
+    case ITEM_TYPE_DISC:
+        return libvlc_media_type_disc;
+    case ITEM_TYPE_STREAM:
+        return libvlc_media_type_stream;
+    case ITEM_TYPE_PLAYLIST:
+        return libvlc_media_type_playlist;
+    default:
+        return libvlc_media_type_unknown;
+    }
+}
+
+int libvlc_media_slaves_add( libvlc_media_t *p_md,
+                             libvlc_media_slave_type_t i_type,
+                             unsigned int i_priority,
+                             const char *psz_uri )
+{
+    assert( p_md && psz_uri );
+    input_item_t *p_input_item = p_md->p_input_item;
+
+    enum slave_type i_input_slave_type;
+    switch( i_type )
+    {
+    case libvlc_media_slave_type_subtitle:
+        i_input_slave_type = SLAVE_TYPE_SPU;
+        break;
+    case libvlc_media_slave_type_audio:
+        i_input_slave_type = SLAVE_TYPE_AUDIO;
+        break;
+    default:
+        vlc_assert_unreachable();
+        return -1;
+    }
+
+    enum slave_priority i_input_slave_priority;
+    switch( i_priority )
+    {
+    case 0:
+        i_input_slave_priority = SLAVE_PRIORITY_MATCH_NONE;
+        break;
+    case 1:
+        i_input_slave_priority = SLAVE_PRIORITY_MATCH_RIGHT;
+        break;
+    case 2:
+        i_input_slave_priority = SLAVE_PRIORITY_MATCH_LEFT;
+        break;
+    case 3:
+        i_input_slave_priority = SLAVE_PRIORITY_MATCH_ALL;
+        break;
+    default:
+    case 4:
+        i_input_slave_priority = SLAVE_PRIORITY_USER;
+        break;
+    }
+
+    input_item_slave_t *p_slave = input_item_slave_New( psz_uri,
+                                                      i_input_slave_type,
+                                                      i_input_slave_priority );
+    if( p_slave == NULL )
+        return -1;
+    return input_item_AddSlave( p_input_item, p_slave ) == VLC_SUCCESS ? 0 : -1;
+}
+
+void libvlc_media_slaves_clear( libvlc_media_t *p_md )
+{
+    assert( p_md );
+    input_item_t *p_input_item = p_md->p_input_item;
+
+    vlc_mutex_lock( &p_input_item->lock );
+    for( int i = 0; i < p_input_item->i_slaves; i++ )
+        input_item_slave_Delete( p_input_item->pp_slaves[i] );
+    TAB_CLEAN( p_input_item->i_slaves, p_input_item->pp_slaves );
+    vlc_mutex_unlock( &p_input_item->lock );
+}
+
+unsigned int libvlc_media_slaves_get( libvlc_media_t *p_md,
+                                      libvlc_media_slave_t ***ppp_slaves )
+{
+    assert( p_md && ppp_slaves );
+    input_item_t *p_input_item = p_md->p_input_item;
+
+    vlc_mutex_lock( &p_input_item->lock );
+
+    int i_count = p_input_item->i_slaves;
+    if( i_count <= 0 )
+        return vlc_mutex_unlock( &p_input_item->lock ), 0;
+
+    libvlc_media_slave_t **pp_slaves = calloc( i_count, sizeof(*pp_slaves) );
+    if( pp_slaves == NULL )
+        return vlc_mutex_unlock( &p_input_item->lock ), 0;
+
+    for( int i = 0; i < i_count; ++i )
+    {
+        input_item_slave_t *p_item_slave = p_input_item->pp_slaves[i];
+        assert( p_item_slave->i_priority >= SLAVE_PRIORITY_MATCH_NONE );
+
+        /* also allocate psz_uri buffer at the end of the struct */
+        libvlc_media_slave_t *p_slave = malloc( sizeof(*p_slave) +
+                                                strlen( p_item_slave->psz_uri )
+                                                + 1 );
+        if( p_slave == NULL )
+        {
+            libvlc_media_slaves_release(pp_slaves, i);
+            return vlc_mutex_unlock( &p_input_item->lock ), 0;
+        }
+        p_slave->psz_uri = (char *) ((uint8_t *)p_slave) + sizeof(*p_slave);
+        strcpy( p_slave->psz_uri, p_item_slave->psz_uri );
+
+        switch( p_item_slave->i_type )
+        {
+        case SLAVE_TYPE_SPU:
+            p_slave->i_type = libvlc_media_slave_type_subtitle;
+            break;
+        case SLAVE_TYPE_AUDIO:
+            p_slave->i_type = libvlc_media_slave_type_audio;
+            break;
+        default:
+            vlc_assert_unreachable();
+        }
+
+        switch( p_item_slave->i_priority )
+        {
+        case SLAVE_PRIORITY_MATCH_NONE:
+            p_slave->i_priority = 0;
+            break;
+        case SLAVE_PRIORITY_MATCH_RIGHT:
+            p_slave->i_priority = 1;
+            break;
+        case SLAVE_PRIORITY_MATCH_LEFT:
+            p_slave->i_priority = 2;
+            break;
+        case SLAVE_PRIORITY_MATCH_ALL:
+            p_slave->i_priority = 3;
+            break;
+        case SLAVE_PRIORITY_USER:
+            p_slave->i_priority = 4;
+            break;
+        default:
+            vlc_assert_unreachable();
+        }
+        pp_slaves[i] = p_slave;
+    }
+    vlc_mutex_unlock( &p_input_item->lock );
+
+    *ppp_slaves = pp_slaves;
+    return i_count;
+}
+
+void libvlc_media_slaves_release( libvlc_media_slave_t **pp_slaves,
+                                  unsigned int i_count )
+{
+    if( i_count > 0 )
+    {
+        assert( pp_slaves );
+        for( unsigned int i = 0; i < i_count; ++i )
+            free( pp_slaves[i] );
+        free( pp_slaves );
+    }
+}