]> git.sesse.net Git - vlc/blobdiff - src/control/media_player.c
Updated to intf-event.
[vlc] / src / control / media_player.c
index a42e655cd78193cef72956052540a4cfeba6b602..682aa8193e4070da8b6dce3d89c8dfaf2594a43f 100644 (file)
 #include <vlc/libvlc.h>
 #include <vlc_demux.h>
 #include <vlc_input.h>
+#include <vlc_vout.h>
 #include "libvlc.h"
 
-static int
-input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
-                     vlc_value_t oldval, vlc_value_t newval,
-                     void * p_userdata );
+static void
+input_state_changed( const vlc_event_t * event, void * p_userdata );
+
 static int
 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval,
@@ -41,21 +41,18 @@ input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval,
                         void * p_userdata );
 static int
-input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
-                     vlc_value_t oldval, vlc_value_t newval,
-                     void * p_userdata );
-static int
-input_time_changed( vlc_object_t * p_this, char const * psz_cmd,
+input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
                      vlc_value_t oldval, vlc_value_t newval,
                      void * p_userdata );
 
 static const libvlc_state_t vlc_to_libvlc_state_array[] =
 {
-    [INIT_S]        = libvlc_Opening,
+    [INIT_S]        = libvlc_NothingSpecial,
     [OPENING_S]     = libvlc_Opening,
     [BUFFERING_S]   = libvlc_Buffering,
     [PLAYING_S]     = libvlc_Playing,
     [PAUSE_S]       = libvlc_Paused,
+    [STOP_S]        = libvlc_Stopped,
     [END_S]         = libvlc_Ended,
     [ERROR_S]       = libvlc_Error,
 };
@@ -63,7 +60,7 @@ static const libvlc_state_t vlc_to_libvlc_state_array[] =
 static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state )
 {
     if( vlc_state < 0 || vlc_state > 6 )
-        return libvlc_Stopped;
+        return libvlc_Ended;
 
     return vlc_to_libvlc_state_array[vlc_state];
 }
@@ -85,14 +82,15 @@ static void release_input_thread( libvlc_media_player_t *p_mi )
     /* No one is tracking this input_thread appart us. Destroy it */
     if( p_mi->b_own_its_input_thread )
     {
-        var_DelCallback( p_input_thread, "state", input_state_changed, p_mi );
+        vlc_event_manager_t * p_em = input_get_event_manager( p_input_thread );
+        vlc_event_detach( p_em, vlc_InputStateChanged, input_state_changed, p_mi );
         var_DelCallback( p_input_thread, "seekable", input_seekable_changed, p_mi );
         var_DelCallback( p_input_thread, "pausable", input_pausable_changed, p_mi );
-        var_DelCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
-        var_DelCallback( p_input_thread, "intf-change", input_time_changed, p_mi );
+        var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
 
         /* We owned this one */
         input_StopThread( p_input_thread );
+        vlc_thread_join( p_input_thread );
 
         var_Destroy( p_input_thread, "drawable" );
     }
@@ -124,7 +122,7 @@ input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi,
     }
 
     p_input_thread = p_mi->p_input_thread;
-    vlc_object_yield( p_input_thread );
+    vlc_object_hold( p_input_thread );
 
     vlc_mutex_unlock( &p_mi->object_lock );
 
@@ -132,44 +130,56 @@ input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi,
 }
 
 /*
- * input_state_changed (Private) (input var "state" Callback)
+ * input_state_changed (Private) (vlc_InputStateChanged callback)
  */
-static int
-input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
-                     vlc_value_t oldval, vlc_value_t newval,
-                     void * p_userdata )
+static void
+input_state_changed( const vlc_event_t * event, void * p_userdata )
 {
-    VLC_UNUSED(oldval);
-    VLC_UNUSED(p_this);
-    VLC_UNUSED(psz_cmd);
     libvlc_media_player_t * p_mi = p_userdata;
-    libvlc_event_t event;
-    libvlc_event_type_t type = newval.i_int;
+    libvlc_event_t forwarded_event;
+    libvlc_event_type_t type = event->u.input_state_changed.new_state;
 
     switch ( type )
     {
-        case END_S:
+        case INIT_S:
             libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
-            event.type = libvlc_MediaPlayerEndReached;
+            forwarded_event.type = libvlc_MediaPlayerNothingSpecial;
             break;
-        case PAUSE_S:
-            libvlc_media_set_state( p_mi->p_md, libvlc_Playing, NULL);
-            event.type = libvlc_MediaPlayerPaused;
+        case OPENING_S:
+            libvlc_media_set_state( p_mi->p_md, libvlc_Opening, NULL);
+            forwarded_event.type = libvlc_MediaPlayerOpening;
+            break;
+        case BUFFERING_S:
+            libvlc_media_set_state( p_mi->p_md, libvlc_Buffering, NULL);
+            forwarded_event.type = libvlc_MediaPlayerBuffering;
             break;
         case PLAYING_S:
             libvlc_media_set_state( p_mi->p_md, libvlc_Playing, NULL);
-            event.type = libvlc_MediaPlayerPlayed;
+            forwarded_event.type = libvlc_MediaPlayerPlaying;
+            break;
+        case PAUSE_S:
+            libvlc_media_set_state( p_mi->p_md, libvlc_Paused, NULL);
+            forwarded_event.type = libvlc_MediaPlayerPaused;
+            break;
+        case STOP_S:
+            libvlc_media_set_state( p_mi->p_md, libvlc_Stopped, NULL);
+            forwarded_event.type = libvlc_MediaPlayerStopped;
+            break;
+        case END_S:
+            libvlc_media_set_state( p_mi->p_md, libvlc_Ended, NULL);
+            forwarded_event.type = libvlc_MediaPlayerEndReached;
             break;
         case ERROR_S:
             libvlc_media_set_state( p_mi->p_md, libvlc_Error, NULL);
-            event.type = libvlc_MediaPlayerEncounteredError;
+            forwarded_event.type = libvlc_MediaPlayerEncounteredError;
             break;
+
         default:
-            return VLC_SUCCESS;
+            return;
     }
 
-    libvlc_event_send( p_mi->p_event_manager, &event );
-    return VLC_SUCCESS;
+    libvlc_event_send( p_mi->p_event_manager, &forwarded_event );
+    return;
 }
 
 static int
@@ -210,71 +220,39 @@ input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
     return VLC_SUCCESS;
 }
 
-/*
- * input_position_changed (Private) (input var "intf-change" Callback)
- */
 static int
-input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
+input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
                      vlc_value_t oldval, vlc_value_t newval,
                      void * p_userdata )
 {
     VLC_UNUSED(oldval);
+    input_thread_t * p_input = (input_thread_t *)p_this;
     libvlc_media_player_t * p_mi = p_userdata;
-    vlc_value_t val;
+    libvlc_event_t event;
 
-    if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */))
-    {
-        input_thread_t * p_input = (input_thread_t *)p_this;
+    if( newval.i_int != INPUT_EVENT_TIMES )
+        return VLC_EGENERIC;
 
-        var_Get( p_input, "state", &val );
-        if( val.i_int != PLAYING_S )
-            return VLC_SUCCESS; /* Don't send the position while stopped */
+    assert( !strcmp(psz_cmd, "intf-event" ) );
 
-        var_Get( p_input, "position", &val );
-    }
-    else
-        val.i_time = newval.i_time;
+    if( var_GetInteger( p_input, "state" ) != PLAYING_S )
+        return VLC_SUCCESS; /* Don't send the position while stopped */
 
-    libvlc_event_t event;
+    /* */
     event.type = libvlc_MediaPlayerPositionChanged;
-    event.u.media_player_position_changed.new_position = val.f_float;
-
+    event.u.media_player_position_changed.new_position = var_GetFloat( p_input, "position" );;
     libvlc_event_send( p_mi->p_event_manager, &event );
-    return VLC_SUCCESS;
-}
-
-/*
- * input_time_changed (Private) (input var "intf-change" Callback)
- */
-static int
-input_time_changed( vlc_object_t * p_this, char const * psz_cmd,
-                     vlc_value_t oldval, vlc_value_t newval,
-                     void * p_userdata )
-{
-    VLC_UNUSED(oldval);
-    libvlc_media_player_t * p_mi = p_userdata;
-    vlc_value_t val;
-
-    if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */))
-    {
-        input_thread_t * p_input = (input_thread_t *)p_this;
-
-        var_Get( p_input, "state", &val );
-        if( val.i_int != PLAYING_S )
-            return VLC_SUCCESS; /* Don't send the position while stopped */
 
-        var_Get( p_input, "time", &val );
-    }
-    else
-        val.i_time = newval.i_time;
-
-    libvlc_event_t event;
+    /* */
     event.type = libvlc_MediaPlayerTimeChanged;
-    event.u.media_player_time_changed.new_time = val.i_time;
+    event.u.media_player_time_changed.new_time = var_GetTime( p_input, "time" );
     libvlc_event_send( p_mi->p_event_manager, &event );
+
     return VLC_SUCCESS;
+
 }
 
+
 /**************************************************************************
  * Create a Media Instance object
  **************************************************************************/
@@ -321,19 +299,32 @@ libvlc_media_player_new( libvlc_instance_t * p_libvlc_instance,
     }
 
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
-            libvlc_MediaPlayerEndReached, p_e );
+            libvlc_MediaPlayerNothingSpecial, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
-            libvlc_MediaPlayerStopped, p_e );
+            libvlc_MediaPlayerOpening, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
-            libvlc_MediaPlayerEncounteredError, p_e );
+            libvlc_MediaPlayerBuffering, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaPlayerPlaying, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaPlayerPaused, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
-            libvlc_MediaPlayerPlayed, p_e );
+            libvlc_MediaPlayerStopped, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaPlayerForward, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaPlayerBackward, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaPlayerEndReached, p_e );
+    libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaPlayerEncounteredError, p_e );
+
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaPlayerPositionChanged, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaPlayerTimeChanged, p_e );
+     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
+            libvlc_MediaPlayerTitleChanged, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
             libvlc_MediaPlayerSeekableChanged, p_e );
     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
@@ -394,7 +385,7 @@ libvlc_media_player_t * libvlc_media_player_new_from_input_thread(
     }
 
     /* will be released in media_player_release() */
-    vlc_object_yield( p_input );
+    vlc_object_hold( p_input );
 
     p_mi->p_input_thread = p_input;
     p_mi->b_own_its_input_thread = false;
@@ -591,11 +582,13 @@ void libvlc_media_player_play( libvlc_media_player_t *p_mi,
         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
         var_Set( p_input_thread, "drawable", val );
     }
-    var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
+
+    vlc_event_manager_t * p_em = input_get_event_manager( p_input_thread );
+    vlc_event_attach( p_em, vlc_InputStateChanged, input_state_changed, p_mi );
+
     var_AddCallback( p_input_thread, "seekable", input_seekable_changed, p_mi );
     var_AddCallback( p_input_thread, "pausable", input_pausable_changed, p_mi );
-    var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
-    var_AddCallback( p_input_thread, "intf-change", input_time_changed, p_mi );
+    var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
 
     vlc_mutex_unlock( &p_mi->object_lock );
 }
@@ -637,12 +630,11 @@ void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
     if( state == libvlc_Playing || state == libvlc_Paused )
     {
         /* Send a stop notification event only of we are in playing or paused states */
-
-        libvlc_media_set_state( p_mi->p_md, libvlc_Stopped, p_e );
+        libvlc_media_set_state( p_mi->p_md, libvlc_Ended, p_e );
 
         /* Construct and send the event */
         libvlc_event_t event;
-        event.type = libvlc_MediaPlayerStopped;
+        event.type = libvlc_MediaPlayerEndReached;
         libvlc_event_send( p_mi->p_event_manager, &event );
     }
 
@@ -668,11 +660,32 @@ void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
  * Set Drawable
  **************************************************************************/
 void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi,
-                                         libvlc_drawable_t drawable,
-                                         libvlc_exception_t *p_e )
+                                       libvlc_drawable_t drawable,
+                                       libvlc_exception_t *p_e )
 {
-    VLC_UNUSED(p_e);
+    input_thread_t *p_input_thread;
+    vout_thread_t *p_vout = NULL;
+
     p_mi->drawable = drawable;
+
+    /* Allow on the fly drawable changing. This is tricky has this may
+     * not be supported by every vout. We though can't disable it
+     * because of some creepy drawable type that are not flexible enough
+     * (Win32 HWND for instance) */
+    p_input_thread = libvlc_get_input_thread( p_mi, p_e );
+    if( !p_input_thread ) {
+        /* No input, nothing more to do, we are fine */
+        libvlc_exception_clear( p_e );
+        return;
+    }
+
+    p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD );
+    if( p_vout )
+    {
+        vout_Control( p_vout , VOUT_REPARENT, drawable);
+        vlc_object_release( p_vout );
+    }
+    vlc_object_release( p_input_thread );
 }
 
 /**************************************************************************
@@ -798,7 +811,7 @@ int libvlc_media_player_get_chapter(
 
     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
     if( !p_input_thread )
-        return -1.0;
+        return -1;
 
     var_Get( p_input_thread, "chapter", &val );
     vlc_object_release( p_input_thread );
@@ -815,7 +828,7 @@ int libvlc_media_player_get_chapter_count(
 
     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
     if( !p_input_thread )
-        return -1.0;
+        return -1;
 
     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
     vlc_object_release( p_input_thread );
@@ -823,6 +836,126 @@ int libvlc_media_player_get_chapter_count(
     return val.i_int;
 }
 
+int libvlc_media_player_get_chapter_count_for_title(
+                                 libvlc_media_player_t *p_mi,
+                                 int i_title,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if( !p_input_thread )
+        return -1;
+
+    char *psz_name;
+    if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
+    {
+        vlc_object_release( p_input_thread );
+        return -1;
+    }
+    var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
+    vlc_object_release( p_input_thread );
+    free( psz_name );
+
+    return val.i_int;
+}
+
+void libvlc_media_player_set_title(
+                                 libvlc_media_player_t *p_mi,
+                                 int i_title,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+    val.i_int = i_title;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
+    if( !p_input_thread )
+        return;
+
+    var_Set( p_input_thread, "title", val );
+    vlc_object_release( p_input_thread );
+
+    //send event
+    libvlc_event_t event;
+    event.type = libvlc_MediaPlayerTitleChanged;
+    event.u.media_player_title_changed.new_title = i_title;
+    libvlc_event_send( p_mi->p_event_manager, &event );
+}
+
+int libvlc_media_player_get_title(
+                                 libvlc_media_player_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if( !p_input_thread )
+        return -1;
+
+    var_Get( p_input_thread, "title", &val );
+    vlc_object_release( p_input_thread );
+
+    return val.i_int;
+}
+
+int libvlc_media_player_get_title_count(
+                                 libvlc_media_player_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+    vlc_value_t val;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if( !p_input_thread )
+        return -1;
+
+    var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
+    vlc_object_release( p_input_thread );
+
+    return val.i_int;
+}
+
+void libvlc_media_player_next_chapter(
+                                 libvlc_media_player_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
+    if( !p_input_thread )
+        return;
+
+    int i_type = var_Type( p_input_thread, "next-chapter" );
+    vlc_value_t val;
+    val.b_bool = true;
+    var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
+                            "next-chapter":"next-title", val );
+
+    vlc_object_release( p_input_thread );
+}
+
+void libvlc_media_player_previous_chapter(
+                                 libvlc_media_player_t *p_mi,
+                                 libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input_thread;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
+    if( !p_input_thread )
+        return;
+
+    int i_type = var_Type( p_input_thread, "next-chapter" );
+    vlc_value_t val;
+    val.b_bool = true;
+    var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
+                            "prev-chapter":"prev-title", val );
+
+    vlc_object_release( p_input_thread );
+}
+
 float libvlc_media_player_get_fps(
                                  libvlc_media_player_t *p_mi,
                                  libvlc_exception_t *p_e)
@@ -863,16 +996,24 @@ void libvlc_media_player_set_rate(
 {
     input_thread_t *p_input_thread;
     vlc_value_t val;
+    bool b_can_rewind;
 
-    if( rate <= 0 )
+    if( rate != 0 )
         RAISEVOID( "Rate value is invalid" );
 
-    val.i_int = 1000.0f/rate;
-
     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
-    if ( !p_input_thread )
+    if( !p_input_thread )
+        return;
+
+    b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
+    if( (rate < 0) && !b_can_rewind )
+    {
+        vlc_object_release( p_input_thread );
+        libvlc_exception_raise( p_e, "Rate value is invalid" );
         return;
+    }
 
+    val.i_int = 1000.0f/rate;
     var_Set( p_input_thread, "rate", val );
     vlc_object_release( p_input_thread );
 }
@@ -883,12 +1024,19 @@ float libvlc_media_player_get_rate(
 {
     input_thread_t *p_input_thread;
     vlc_value_t val;
+    bool b_can_rewind;
 
-    p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
-    if ( !p_input_thread )
-        return -1.0;
+    p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
+    if( !p_input_thread )
+        return 0.0;  /* rate < 0 indicates rewind */
 
     var_Get( p_input_thread, "rate", &val );
+    b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
+    if( (val.i_int < 0) && !b_can_rewind )
+    {
+        libvlc_exception_raise( p_e, "invalid rate" );
+        return 0.0;
+    }
     vlc_object_release( p_input_thread );
 
     return (float)1000.0f/val.i_int;
@@ -907,7 +1055,7 @@ libvlc_state_t libvlc_media_player_get_state(
         /* We do return the right value, no need to throw an exception */
         if( libvlc_exception_raised( p_e ) )
             libvlc_exception_clear( p_e );
-        return libvlc_Stopped;
+        return libvlc_Ended;
     }
 
     var_Get( p_input_thread, "state", &val );
@@ -936,6 +1084,78 @@ int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
     return val.b_bool;
 }
 
+/* internal function, used by audio, video */
+libvlc_track_description_t *
+        libvlc_get_track_description( libvlc_media_player_t *p_mi,
+                                      const char *psz_variable,
+                                      libvlc_exception_t *p_e )
+{
+    input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
+
+    if( !p_input )
+        return NULL;
+
+    vlc_value_t val_list, text_list;
+    var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
+
+    if( val_list.p_list->i_count <= 0 ) /* no tracks */
+        return NULL;
+
+    libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
+    p_track_description = ( libvlc_track_description_t * )
+        malloc( sizeof( libvlc_track_description_t ) );
+    if ( !p_track_description )
+    {
+        var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
+        vlc_object_release( p_input );
+        libvlc_exception_raise( p_e, "no enough memory" );
+        return NULL;
+    }
+    p_actual = p_track_description;
+    p_previous = NULL;
+    for( int i = 0; i < val_list.p_list->i_count; i++ )
+    {
+        if( !p_actual )
+        {
+            p_actual = ( libvlc_track_description_t * )
+                malloc( sizeof( libvlc_track_description_t ) );
+            if ( !p_actual )
+            {
+                libvlc_track_description_release( p_track_description );
+                var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
+                vlc_object_release( p_input );
+                libvlc_exception_raise( p_e, "no enough memory" );
+                return NULL;
+            }
+        }
+        p_actual->i_id = val_list.p_list->p_values[i].i_int;
+        p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
+        p_actual->p_next = NULL;
+        if( p_previous )
+            p_previous->p_next = p_actual;
+        p_previous = p_actual;
+        p_actual =  NULL;
+    }
+    var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
+    vlc_object_release( p_input );
+
+    return p_track_description;
+}
+
+void libvlc_track_description_release( libvlc_track_description_t *p_track_description )
+{
+    libvlc_track_description_t *p_actual, *p_before;
+    p_actual = p_track_description;
+
+    while ( p_actual )
+    {
+        free( p_actual->psz_name );
+        p_before = p_actual;
+        p_actual = p_before->p_next;
+        free( p_before );
+    }
+}
+
 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
                                      libvlc_exception_t *p_e )
 {