]> git.sesse.net Git - vlc/blobdiff - lib/media_player.c
decoder: drain the audio output properly
[vlc] / lib / media_player.c
index a41b8c7cc764a424456a967277089999735e13c2..b41d6f36016f66606818a7e662f5facc53a7576b 100644 (file)
 #include <vlc_demux.h>
 #include <vlc_input.h>
 #include <vlc_vout.h>
+#include <vlc_aout.h>
 #include <vlc_keys.h>
 
 #include "libvlc_internal.h"
 #include "media_internal.h" // libvlc_media_set_state()
 #include "media_player_internal.h"
 
-/*
- * mapping of libvlc_navigate_mode_t to vlc_action_t
- */
-static const vlc_action_t libvlc_navigate_to_action[] =
-{
-    ACTIONID_NAV_ACTIVATE,
-    ACTIONID_NAV_UP,
-    ACTIONID_NAV_DOWN,
-    ACTIONID_NAV_LEFT,
-    ACTIONID_NAV_RIGHT
-};
-
-static const uint32_t libvlc_navigate_to_action_size =                        \
-  sizeof( libvlc_navigate_to_action ) / sizeof( libvlc_navigate_to_action[0] );
-
-
 static int
 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval,
@@ -64,10 +49,30 @@ 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_scrambled_changed( vlc_object_t * p_this, char const * psz_cmd,
+                        vlc_value_t oldval, vlc_value_t newval,
+                        void * p_userdata );
+static int
 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
                      vlc_value_t oldval, vlc_value_t newval,
                      void * p_userdata );
 
+static int
+input_es_changed( vlc_object_t * p_this, char const * psz_cmd,
+                  int action, vlc_value_t *p_val,
+                  void *p_userdata);
+
+static int
+input_es_selected( vlc_object_t * p_this, char const * psz_cmd,
+                   vlc_value_t oldval, vlc_value_t newval,
+                   void * p_userdata );
+
+static void
+add_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi );
+
+static void
+del_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi );
+
 static int
 snapshot_was_taken( vlc_object_t *p_this, char const *psz_cmd,
                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
@@ -119,7 +124,7 @@ static inline void unlock_input(libvlc_media_player_t *mp)
  * Object lock is NOT held.
  * Input lock is held or instance is being destroyed.
  */
-static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
+static void release_input_thread( libvlc_media_player_t *p_mi )
 {
     assert( p_mi );
 
@@ -132,11 +137,14 @@ static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abor
                      input_seekable_changed, p_mi );
     var_DelCallback( p_input_thread, "can-pause",
                     input_pausable_changed, p_mi );
+    var_DelCallback( p_input_thread, "program-scrambled",
+                    input_scrambled_changed, p_mi );
     var_DelCallback( p_input_thread, "intf-event",
                      input_event_changed, p_mi );
+    del_es_callbacks( p_input_thread, p_mi );
 
     /* We owned this one */
-    input_Stop( p_input_thread, b_input_abort );
+    input_Stop( p_input_thread );
     input_Close( p_input_thread );
 }
 
@@ -226,6 +234,24 @@ input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
     return VLC_SUCCESS;
 }
 
+static int
+input_scrambled_changed( vlc_object_t * p_this, char const * psz_cmd,
+                        vlc_value_t oldval, vlc_value_t newval,
+                        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;
+
+    event.type = libvlc_MediaPlayerScrambledChanged;
+    event.u.media_player_scrambled_changed.new_scrambled = newval.b_bool;
+
+    libvlc_event_send( p_mi->p_event_manager, &event );
+    return VLC_SUCCESS;
+}
+
 static int
 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
                      vlc_value_t oldval, vlc_value_t newval,
@@ -276,9 +302,9 @@ input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
         set_state( p_mi, libvlc_state, false );
         libvlc_event_send( p_mi->p_event_manager, &event );
     }
-    else if( newval.i_int == INPUT_EVENT_ABORT )
+    else if( newval.i_int == INPUT_EVENT_DEAD )
     {
-        libvlc_state_t libvlc_state = libvlc_Stopped;
+        libvlc_state_t libvlc_state = libvlc_Ended;
         event.type = libvlc_MediaPlayerStopped;
 
         set_state( p_mi, libvlc_state, false );
@@ -338,6 +364,85 @@ input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
     return VLC_SUCCESS;
 }
 
+static int track_type_from_name(const char *psz_name)
+{
+   if( !strcmp( psz_name, "video-es" ) )
+       return libvlc_track_video;
+    else if( !strcmp( psz_name, "audio-es" ) )
+        return libvlc_track_audio;
+    else if( !strcmp( psz_name, "spu-es" ) )
+        return libvlc_track_text;
+    else
+        return libvlc_track_unknown;
+}
+
+static int input_es_changed( vlc_object_t *p_this,
+                             char const *psz_cmd,
+                             int action,
+                             vlc_value_t *p_val,
+                             void *p_userdata )
+{
+    VLC_UNUSED(p_this);
+    libvlc_media_player_t *mp = p_userdata;
+    libvlc_event_t event;
+
+    /* Ignore the "Disable" element */
+    if (p_val && p_val->i_int < 0)
+        return VLC_EGENERIC;
+
+    switch (action)
+    {
+    case VLC_VAR_ADDCHOICE:
+        event.type = libvlc_MediaPlayerESAdded;
+        break;
+    case VLC_VAR_DELCHOICE:
+    case VLC_VAR_CLEARCHOICES:
+        event.type = libvlc_MediaPlayerESDeleted;
+        break;
+    default:
+        return VLC_EGENERIC;
+    }
+
+    event.u.media_player_es_changed.i_type = track_type_from_name(psz_cmd);
+
+    int i_id;
+    if (action != VLC_VAR_CLEARCHOICES)
+    {
+        if (!p_val)
+            return VLC_EGENERIC;
+        i_id = p_val->i_int;
+    }
+    else
+    {
+        /* -1 means all ES tracks of this type were deleted. */
+        i_id = -1;
+    }
+    event.u.media_player_es_changed.i_id = i_id;
+
+    libvlc_event_send(mp->p_event_manager, &event);
+
+    return VLC_SUCCESS;
+}
+
+static int
+input_es_selected( vlc_object_t * p_this, char const * psz_cmd,
+                   vlc_value_t oldval, vlc_value_t newval,
+                   void * p_userdata )
+{
+    VLC_UNUSED(p_this);
+    VLC_UNUSED(oldval);
+    libvlc_media_player_t *mp = p_userdata;
+    libvlc_event_t event;
+
+    event.type = libvlc_MediaPlayerESSelected;
+    event.u.media_player_es_changed.i_type = track_type_from_name(psz_cmd);
+    event.u.media_player_es_changed.i_id = newval.i_int;
+
+    libvlc_event_send(mp->p_event_manager, &event);
+
+    return VLC_SUCCESS;
+}
+
 /**************************************************************************
  * Snapshot Taken Event.
  *
@@ -357,17 +462,6 @@ static int snapshot_was_taken(vlc_object_t *p_this, char const *psz_cmd,
     return VLC_SUCCESS;
 }
 
-static input_thread_t *find_input (vlc_object_t *obj)
-{
-    libvlc_media_player_t *mp = (libvlc_media_player_t *)obj;
-
-    return libvlc_get_input_thread (mp);
-}
-
-/* */
-static void libvlc_media_player_destroy( libvlc_media_player_t * );
-
-
 /**************************************************************************
  * Create a Media Instance object.
  *
@@ -412,8 +506,9 @@ libvlc_media_player_new( libvlc_instance_t *instance )
     var_Create (mp, "vmem-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
     var_Create (mp, "vmem-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
     var_Create (mp, "vmem-pitch", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
+    var_Create (mp, "avcodec-hw", VLC_VAR_STRING);
     var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
-#if defined (WIN32) || defined (__OS2__)
+#if defined (_WIN32) || defined (__OS2__)
     var_Create (mp, "drawable-hwnd", VLC_VAR_INTEGER);
 #endif
 #ifdef __APPLE__
@@ -427,16 +522,15 @@ libvlc_media_player_new( libvlc_instance_t *instance )
     var_SetBool (mp, "mouse-events", true);
 
     var_Create (mp, "fullscreen", VLC_VAR_BOOL);
-    var_Create (mp, "autoscale", VLC_VAR_BOOL);
-    var_SetBool (mp, "autoscale", true);
-    var_Create (mp, "scale", VLC_VAR_FLOAT);
-    var_SetFloat (mp, "scale", 1.);
+    var_Create (mp, "autoscale", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
+    var_Create (mp, "zoom", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
     var_Create (mp, "aspect-ratio", VLC_VAR_STRING);
     var_Create (mp, "crop", VLC_VAR_STRING);
     var_Create (mp, "deinterlace", VLC_VAR_INTEGER);
     var_Create (mp, "deinterlace-mode", VLC_VAR_STRING);
 
     var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
+    var_SetInteger (mp, "vbi-page", 100);
 
     var_Create (mp, "marq-marquee", VLC_VAR_STRING);
     var_Create (mp, "marq-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
@@ -458,7 +552,7 @@ libvlc_media_player_new( libvlc_instance_t *instance )
 
     var_Create (mp, "contrast", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
     var_Create (mp, "brightness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
-    var_Create (mp, "hue", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
+    var_Create (mp, "hue", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
     var_Create (mp, "saturation", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
     var_Create (mp, "gamma", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
 
@@ -466,9 +560,8 @@ libvlc_media_player_new( libvlc_instance_t *instance )
     var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
     var_Create (mp, "mute", VLC_VAR_BOOL);
     var_Create (mp, "volume", VLC_VAR_FLOAT);
-    var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
-    var_SetAddress (mp, "find-input-callback", find_input);
     var_Create (mp, "corks", VLC_VAR_INTEGER);
+    var_Create (mp, "audio-filter", VLC_VAR_STRING);
     var_Create (mp, "amem-data", VLC_VAR_ADDRESS);
     var_Create (mp, "amem-setup", VLC_VAR_ADDRESS);
     var_Create (mp, "amem-cleanup", VLC_VAR_ADDRESS);
@@ -482,16 +575,36 @@ libvlc_media_player_new( libvlc_instance_t *instance )
     var_Create (mp, "amem-rate", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
     var_Create (mp, "amem-channels", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
 
+    /* Video Title */
+    var_Create (mp, "video-title-show", VLC_VAR_BOOL);
+    var_Create (mp, "video-title-position", VLC_VAR_INTEGER);
+    var_Create (mp, "video-title-timeout", VLC_VAR_INTEGER);
+
+    /* Equalizer */
+    var_Create (mp, "equalizer-preamp", VLC_VAR_FLOAT);
+    var_Create (mp, "equalizer-vlcfreqs", VLC_VAR_BOOL);
+    var_Create (mp, "equalizer-bands", VLC_VAR_STRING);
+
     mp->p_md = NULL;
     mp->state = libvlc_NothingSpecial;
     mp->p_libvlc_instance = instance;
     mp->input.p_thread = NULL;
-    mp->input.p_resource = NULL;
+    mp->input.p_resource = input_resource_New(VLC_OBJECT(mp));
+    if (unlikely(mp->input.p_resource == NULL))
+    {
+        vlc_object_release(mp);
+        return NULL;
+    }
+    audio_output_t *aout = input_resource_GetAout(mp->input.p_resource);
+    if( aout != NULL )
+        input_resource_PutAout(mp->input.p_resource, aout);
+
     vlc_mutex_init (&mp->input.lock);
     mp->i_refcount = 1;
     mp->p_event_manager = libvlc_event_manager_new(mp, instance);
     if (unlikely(mp->p_event_manager == NULL))
     {
+        input_resource_Release(mp->input.p_resource);
         vlc_object_release(mp);
         return NULL;
     }
@@ -516,6 +629,10 @@ libvlc_media_player_new( libvlc_instance_t *instance )
     register_event(mp, PausableChanged);
 
     register_event(mp, Vout);
+    register_event(mp, ScrambledChanged);
+    register_event(mp, ESAdded);
+    register_event(mp, ESDeleted);
+    register_event(mp, ESSelected);
 
     /* Snapshot initialization */
     register_event(mp, SnapshotTaken);
@@ -568,13 +685,9 @@ static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
 
     /* No need for lock_input() because no other threads knows us anymore */
     if( p_mi->input.p_thread )
-        release_input_thread(p_mi, true);
-    if( p_mi->input.p_resource )
-    {
-        input_resource_Terminate( p_mi->input.p_resource );
-        input_resource_Release( p_mi->input.p_resource );
-        p_mi->input.p_resource = NULL;
-    }
+        release_input_thread(p_mi);
+    input_resource_Terminate( p_mi->input.p_resource );
+    input_resource_Release( p_mi->input.p_resource );
     vlc_mutex_destroy( &p_mi->input.lock );
 
     libvlc_event_manager_release( p_mi->p_event_manager );
@@ -629,12 +742,7 @@ void libvlc_media_player_set_media(
 {
     lock_input(p_mi);
 
-    /* FIXME I am not sure if it is a user request or on die(eof/error)
-     * request here */
-    release_input_thread( p_mi,
-                          p_mi->input.p_thread &&
-                          !p_mi->input.p_thread->b_eof &&
-                          !p_mi->input.p_thread->b_error );
+    release_input_thread( p_mi );
 
     lock( p_mi );
     set_state( p_mi, libvlc_NothingSpecial, true );
@@ -674,12 +782,13 @@ libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
 {
     libvlc_media_t *p_m;
 
-    lock(p_mi);
+    lock( p_mi );
     p_m = p_mi->p_md;
     if( p_m )
-        libvlc_media_retain( p_mi->p_md );
-    unlock(p_mi);
-    return p_mi->p_md;
+        libvlc_media_retain( p_m );
+    unlock( p_mi );
+
+    return p_m;
 }
 
 /**************************************************************************
@@ -691,6 +800,26 @@ libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
     return p_mi->p_event_manager;
 }
 
+static void add_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi )
+{
+    var_AddListCallback( p_input_thread, "video-es", input_es_changed, p_mi );
+    var_AddListCallback( p_input_thread, "audio-es", input_es_changed, p_mi );
+    var_AddListCallback( p_input_thread, "spu-es", input_es_changed, p_mi );
+    var_AddCallback( p_input_thread, "video-es", input_es_selected, p_mi );
+    var_AddCallback( p_input_thread, "audio-es", input_es_selected, p_mi );
+    var_AddCallback( p_input_thread, "spu-es", input_es_selected, p_mi );
+}
+
+static void del_es_callbacks( input_thread_t *p_input_thread, libvlc_media_player_t *p_mi )
+{
+    var_DelListCallback( p_input_thread, "video-es", input_es_changed, p_mi );
+    var_DelListCallback( p_input_thread, "audio-es", input_es_changed, p_mi );
+    var_DelListCallback( p_input_thread, "spu-es", input_es_changed, p_mi );
+    var_DelCallback( p_input_thread, "video-es", input_es_selected, p_mi );
+    var_DelCallback( p_input_thread, "audio-es", input_es_selected, p_mi );
+    var_DelCallback( p_input_thread, "spu-es", input_es_selected, p_mi );
+}
+
 /**************************************************************************
  * Tell media player to start playing.
  **************************************************************************/
@@ -718,8 +847,6 @@ int libvlc_media_player_play( libvlc_media_player_t *p_mi )
         return -1;
     }
 
-    if( !p_mi->input.p_resource )
-        p_mi->input.p_resource = input_resource_New( VLC_OBJECT( p_mi ) );
     p_input_thread = input_Create( p_mi, p_mi->p_md->p_input_item, NULL,
                                    p_mi->input.p_resource );
     unlock(p_mi);
@@ -732,13 +859,17 @@ int libvlc_media_player_play( libvlc_media_player_t *p_mi )
 
     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
+    var_AddCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
+    add_es_callbacks( p_input_thread, p_mi );
 
     if( input_Start( p_input_thread ) )
     {
         unlock_input(p_mi);
+        del_es_callbacks( p_input_thread, p_mi );
         var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
         var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
+        var_DelCallback( p_input_thread, "program-scrambled", input_scrambled_changed, p_mi );
         var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
         vlc_object_release( p_input_thread );
         libvlc_printerr( "Input initialization failure" );
@@ -763,7 +894,7 @@ void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
             if( libvlc_media_player_can_pause( p_mi ) )
                 input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
             else
-                libvlc_media_player_stop( p_mi );
+                input_Stop( p_input_thread );
         }
     }
     else
@@ -802,14 +933,12 @@ int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
  **************************************************************************/
 void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
 {
-    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
-
     lock_input(p_mi);
-    release_input_thread( p_mi, true ); /* This will stop the input thread */
+    release_input_thread( p_mi ); /* This will stop the input thread */
 
     /* Force to go to stopped state, in case we were in Ended, or Error
      * state. */
-    if( state != libvlc_Stopped )
+    if( p_mi->state != libvlc_Stopped )
     {
         set_state( p_mi, libvlc_Stopped, false );
 
@@ -819,8 +948,7 @@ void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
         libvlc_event_send( p_mi->p_event_manager, &event );
     }
 
-    if( p_mi->input.p_resource != NULL )
-        input_resource_Terminate( p_mi->input.p_resource );
+    input_resource_Terminate( p_mi->input.p_resource );
     unlock_input(p_mi);
 }
 
@@ -836,6 +964,7 @@ void libvlc_video_set_callbacks( libvlc_media_player_t *mp,
     var_SetAddress( mp, "vmem-display", display_cb );
     var_SetAddress( mp, "vmem-data", opaque );
     var_SetString( mp, "vout", "vmem" );
+    var_SetString( mp, "avcodec-hw", "none" );
 }
 
 void libvlc_video_set_format_callbacks( libvlc_media_player_t *mp,
@@ -916,6 +1045,7 @@ void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
 {
     assert (p_mi != NULL);
 
+    var_SetString (p_mi, "avcodec-hw", "");
     var_SetString (p_mi, "vout", drawable ? "xid" : "any");
     var_SetString (p_mi, "window", drawable ? "embed-xid,any" : "any");
     var_SetInteger (p_mi, "drawable-xid", drawable);
@@ -936,7 +1066,7 @@ void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
                                    void *drawable )
 {
     assert (p_mi != NULL);
-#if defined (WIN32) || defined (__OS2__)
+#if defined (_WIN32) || defined (__OS2__)
     var_SetString (p_mi, "window",
                    (drawable != NULL) ? "embed-hwnd,any" : "");
     var_SetInteger (p_mi, "drawable-hwnd", (uintptr_t)drawable);
@@ -951,7 +1081,7 @@ void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
 {
     assert (p_mi != NULL);
-#if defined (WIN32) || defined (__OS2__)
+#if defined (_WIN32) || defined (__OS2__)
     return (void *)(uintptr_t)var_GetInteger (p_mi, "drawable-hwnd");
 #else
     return NULL;
@@ -973,12 +1103,16 @@ void libvlc_audio_set_callbacks( libvlc_media_player_t *mp,
     var_SetAddress( mp, "amem-drain", drain_cb );
     var_SetAddress( mp, "amem-data", opaque );
     var_SetString( mp, "aout", "amem,none" );
+
+    input_resource_ResetAout(mp->input.p_resource);
 }
 
 void libvlc_audio_set_volume_callback( libvlc_media_player_t *mp,
                                        libvlc_audio_set_volume_cb cb )
 {
     var_SetAddress( mp, "amem-set-volume", cb );
+
+    input_resource_ResetAout(mp->input.p_resource);
 }
 
 void libvlc_audio_set_format_callbacks( libvlc_media_player_t *mp,
@@ -987,6 +1121,8 @@ void libvlc_audio_set_format_callbacks( libvlc_media_player_t *mp,
 {
     var_SetAddress( mp, "amem-setup", setup );
     var_SetAddress( mp, "amem-cleanup", cleanup );
+
+    input_resource_ResetAout(mp->input.p_resource);
 }
 
 void libvlc_audio_set_format( libvlc_media_player_t *mp, const char *format,
@@ -995,6 +1131,8 @@ void libvlc_audio_set_format( libvlc_media_player_t *mp, const char *format,
     var_SetString( mp, "amem-format", format );
     var_SetInteger( mp, "amem-rate", rate );
     var_SetInteger( mp, "amem-channels", channels );
+
+    input_resource_ResetAout(mp->input.p_resource);
 }
 
 
@@ -1109,10 +1247,10 @@ int libvlc_media_player_get_chapter_count( libvlc_media_player_t *p_mi )
     if( !p_input_thread )
         return -1;
 
-    var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
+    int i_ret = var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
     vlc_object_release( p_input_thread );
 
-    return val.i_int;
+    return i_ret == VLC_SUCCESS ? val.i_int : -1;
 }
 
 int libvlc_media_player_get_chapter_count_for_title(
@@ -1132,11 +1270,11 @@ int libvlc_media_player_get_chapter_count_for_title(
         vlc_object_release( p_input_thread );
         return -1;
     }
-    var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
+    int i_ret = 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;
+    return i_ret == VLC_SUCCESS ? val.i_int : -1;
 }
 
 void libvlc_media_player_set_title( libvlc_media_player_t *p_mi,
@@ -1182,10 +1320,10 @@ int libvlc_media_player_get_title_count( libvlc_media_player_t *p_mi )
     if( !p_input_thread )
         return -1;
 
-    var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
+    int i_ret = var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
     vlc_object_release( p_input_thread );
 
-    return val.i_int;
+    return i_ret == VLC_SUCCESS ? val.i_int : -1;
 }
 
 void libvlc_media_player_next_chapter( libvlc_media_player_t *p_mi )
@@ -1240,7 +1378,7 @@ int libvlc_media_player_will_play( libvlc_media_player_t *p_mi )
     if ( !p_input_thread )
         return false;
 
-    b_will_play = !p_input_thread->b_die && !p_input_thread->b_dead;
+    b_will_play = !p_input_thread->b_dead;
     vlc_object_release( p_input_thread );
 
     return b_will_play;
@@ -1248,12 +1386,6 @@ int libvlc_media_player_will_play( libvlc_media_player_t *p_mi )
 
 int libvlc_media_player_set_rate( libvlc_media_player_t *p_mi, float rate )
 {
-    if (rate < 0.)
-    {
-        libvlc_printerr ("Playing backward not supported");
-        return -1;
-    }
-
     var_SetFloat (p_mi, "rate", rate);
 
     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
@@ -1294,19 +1426,21 @@ int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi )
 void libvlc_media_player_navigate( libvlc_media_player_t* p_mi,
                                    unsigned navigate )
 {
-    input_thread_t *p_input_thread;
+    static const vlc_action_t map[] =
+    {
+        INPUT_NAV_ACTIVATE, INPUT_NAV_UP, INPUT_NAV_DOWN,
+        INPUT_NAV_LEFT, INPUT_NAV_RIGHT,
+    };
 
-    if ( navigate >= libvlc_navigate_to_action_size)
+    if( navigate >= sizeof(map) / sizeof(map[0]) )
       return;
 
-    p_input_thread = libvlc_get_input_thread ( p_mi );
-    if ( !p_input_thread )
+    input_thread_t *p_input = libvlc_get_input_thread ( p_mi );
+    if ( p_input == NULL )
       return;
 
-    var_SetInteger( p_mi->p_libvlc_instance->p_libvlc_int,
-                    "key-action", libvlc_navigate_to_action[navigate] );
-
-    vlc_object_release( p_input_thread );
+    input_Control( p_input, map[navigate], NULL );
+    vlc_object_release( p_input );
 }
 
 /* internal function, used by audio, video */
@@ -1322,7 +1456,9 @@ libvlc_track_description_t *
         return NULL;
 
     vlc_value_t val_list, text_list;
-    var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
+    int i_ret = var_Change( p_input, psz_variable, VLC_VAR_GETCHOICES, &val_list, &text_list );
+    if( i_ret != VLC_SUCCESS )
+        return NULL;
 
     /* no tracks */
     if( val_list.p_list->i_count <= 0 )
@@ -1400,6 +1536,20 @@ int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi )
     return b_can_pause;
 }
 
+int libvlc_media_player_program_scrambled( libvlc_media_player_t *p_mi )
+{
+    input_thread_t *p_input_thread;
+    bool b_program_scrambled;
+
+    p_input_thread = libvlc_get_input_thread ( p_mi );
+    if ( !p_input_thread )
+        return false;
+    b_program_scrambled = var_GetBool( p_input_thread, "program-scrambled" );
+    vlc_object_release( p_input_thread );
+
+    return b_program_scrambled;
+}
+
 void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi )
 {
     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
@@ -1409,3 +1559,81 @@ void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi )
         vlc_object_release( p_input_thread );
     }
 }
+
+/**
+ * Private lookup table to get subpicture alignment flag values corresponding
+ * to a libvlc_position_t enumerated value.
+ */
+static const unsigned char position_subpicture_alignment[] = {
+    [libvlc_position_center]       = 0,
+    [libvlc_position_left]         = SUBPICTURE_ALIGN_LEFT,
+    [libvlc_position_right]        = SUBPICTURE_ALIGN_RIGHT,
+    [libvlc_position_top]          = SUBPICTURE_ALIGN_TOP,
+    [libvlc_position_top_left]     = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT,
+    [libvlc_position_top_right]    = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT,
+    [libvlc_position_bottom]       = SUBPICTURE_ALIGN_BOTTOM,
+    [libvlc_position_bottom_left]  = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT,
+    [libvlc_position_bottom_right] = SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT
+};
+
+void libvlc_media_player_set_video_title_display( libvlc_media_player_t *p_mi, libvlc_position_t position, unsigned timeout )
+{
+    assert( position >= libvlc_position_disable && position <= libvlc_position_bottom_right );
+
+    if ( position != libvlc_position_disable )
+    {
+        var_SetBool( p_mi, "video-title-show", true );
+        var_SetInteger( p_mi, "video-title-position", position_subpicture_alignment[position] );
+        var_SetInteger( p_mi, "video-title-timeout", timeout );
+    }
+    else
+    {
+        var_SetBool( p_mi, "video-title-show", false );
+    }
+}
+
+/**
+ * Maximum size of a formatted equalizer amplification band frequency value.
+ *
+ * The allowed value range is supposed to be constrained from -20.0 to 20.0.
+ *
+ * The format string " %.07f" with a minimum value of "-20" gives a maximum
+ * string length of e.g. " -19.1234567", i.e. 12 bytes (not including the null
+ * terminator).
+ */
+#define EQZ_BAND_VALUE_SIZE 12
+
+int libvlc_media_player_set_equalizer( libvlc_media_player_t *p_mi, libvlc_equalizer_t *p_equalizer )
+{
+    char bands[EQZ_BANDS_MAX * EQZ_BAND_VALUE_SIZE + 1];
+
+    if( p_equalizer != NULL )
+    {
+        for( unsigned i = 0, c = 0; i < EQZ_BANDS_MAX; i++ )
+        {
+            c += snprintf( bands + c, sizeof(bands) - c, " %.07f",
+                          p_equalizer->f_amp[i] );
+            if( unlikely(c >= sizeof(bands)) )
+                return -1;
+        }
+
+        var_SetFloat( p_mi, "equalizer-preamp", p_equalizer->f_preamp );
+        var_SetString( p_mi, "equalizer-bands", bands );
+    }
+    var_SetString( p_mi, "audio-filter", p_equalizer ? "equalizer" : "" );
+
+    audio_output_t *p_aout = input_resource_HoldAout( p_mi->input.p_resource );
+    if( p_aout != NULL )
+    {
+        if( p_equalizer != NULL )
+        {
+            var_SetFloat( p_aout, "equalizer-preamp", p_equalizer->f_preamp );
+            var_SetString( p_aout, "equalizer-bands", bands );
+        }
+
+        var_SetString( p_aout, "audio-filter", p_equalizer ? "equalizer" : "" );
+        vlc_object_release( p_aout );
+    }
+
+    return 0;
+}