]> git.sesse.net Git - vlc/blobdiff - modules/control/rc.c
Simple: fix memleak on quit
[vlc] / modules / control / rc.c
index 423d6dbac6639de928ac51de7d057774ea493826..d51ddc9840d3b1ba92c8c822337439813fa54c8d 100644 (file)
 #include <vlc_plugin.h>
 
 #include <errno.h>                                                 /* ENOMEM */
-#include <ctype.h>
 #include <signal.h>
 #include <assert.h>
+#include <math.h>
 
 #include <vlc_interface.h>
 #include <vlc_aout.h>
 #include <vlc_vout.h>
-#include <vlc_osd.h>
 #include <vlc_playlist.h>
 #include <vlc_keys.h>
 
 #ifdef HAVE_UNISTD_H
 #    include <unistd.h>
 #endif
-
-#ifdef HAVE_SYS_TIME_H
-#    include <sys/time.h>
-#endif
 #include <sys/types.h>
 
 #include <vlc_network.h>
@@ -85,14 +80,14 @@ static const char *ppsz_input_state[] = {
  *****************************************************************************/
 static int  Activate     ( vlc_object_t * );
 static void Deactivate   ( vlc_object_t * );
-static void Run          ( intf_thread_t * );
+static void *Run         ( void * );
 
 static void Help         ( intf_thread_t *, bool );
 static void RegisterCallbacks( intf_thread_t * );
 
 static bool ReadCommand( intf_thread_t *, char *, int * );
 
-static input_item_t *parse_MRL( intf_thread_t *, char * );
+static input_item_t *parse_MRL( const char * );
 
 static int  Input        ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
@@ -108,9 +103,9 @@ static int  VolumeMove   ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
 static int  VideoConfig  ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
-static int  AudioConfig  ( vlc_object_t *, char const *,
+static int  AudioDevice  ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
-static int  Menu         ( vlc_object_t *, char const *,
+static int  AudioChannel ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
 static int  Statistics   ( vlc_object_t *, char const *,
                            vlc_value_t, vlc_value_t, void * );
@@ -118,25 +113,24 @@ static int  Statistics   ( vlc_object_t *, char const *,
 static int updateStatistics( intf_thread_t *, input_item_t *);
 
 /* Status Callbacks */
-static int TimeOffsetChanged( vlc_object_t *, char const *,
-                              vlc_value_t, vlc_value_t , void * );
-static int VolumeChanged    ( vlc_object_t *, char const *,
-                              vlc_value_t, vlc_value_t, void * );
-static int StateChanged     ( vlc_object_t *, char const *,
-                              vlc_value_t, vlc_value_t, void * );
-static int RateChanged      ( vlc_object_t *, char const *,
-                              vlc_value_t, vlc_value_t, void * );
+static int VolumeChanged( vlc_object_t *, char const *,
+                          vlc_value_t, vlc_value_t, void * );
+static int InputEvent( vlc_object_t *, char const *,
+                       vlc_value_t, vlc_value_t, void * );
 
 struct intf_sys_t
 {
     int *pi_socket_listen;
     int i_socket;
     char *psz_unix_path;
+    vlc_thread_t thread;
 
     /* status changes */
     vlc_mutex_t       status_lock;
-    playlist_status_t i_last_state;
-    playlist_t *p_playlist;
+    int               i_last_state;
+    playlist_t        *p_playlist;
+    input_thread_t    *p_input;
+    bool              b_input_buffering;
 
 #ifdef WIN32
     HANDLE hConsoleIn;
@@ -144,10 +138,8 @@ struct intf_sys_t
 #endif
 };
 
-#define msg_rc( ... ) __msg_rc( p_intf, __VA_ARGS__ )
-
-LIBVLC_FORMAT(2, 3)
-static void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
+VLC_FORMAT(2, 3)
+static void msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
 {
     va_list args;
     char fmt_eol[strlen (psz_fmt) + 3];
@@ -161,6 +153,7 @@ static void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
         net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, fmt_eol, args );
     va_end( args );
 }
+#define msg_rc( ... ) msg_rc( p_intf, __VA_ARGS__ )
 
 /*****************************************************************************
  * Module descriptor
@@ -194,21 +187,24 @@ vlc_module_begin ()
     set_category( CAT_INTERFACE )
     set_subcategory( SUBCAT_INTERFACE_MAIN )
     set_description( N_("Remote control interface") )
-    add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, true )
+    add_bool( "rc-show-pos", false, POS_TEXT, POS_LONGTEXT, true )
 
 #ifdef WIN32
-    add_bool( "rc-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, false )
+    add_bool( "rc-quiet", false, QUIET_TEXT, QUIET_LONGTEXT, false )
 #else
 #if defined (HAVE_ISATTY)
-    add_bool( "rc-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, true )
+    add_bool( "rc-fake-tty", false, TTY_TEXT, TTY_LONGTEXT, true )
 #endif
-    add_string( "rc-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, true )
+    add_string( "rc-unix", NULL, UNIX_TEXT, UNIX_LONGTEXT, true )
 #endif
-    add_string( "rc-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, true )
+    add_string( "rc-host", NULL, HOST_TEXT, HOST_LONGTEXT, true )
 
     set_capability( "interface", 20 )
 
     set_callbacks( Activate, Deactivate )
+#ifdef WIN32
+    add_shortcut( "rc" )
+#endif
 vlc_module_end ()
 
 /*****************************************************************************
@@ -216,21 +212,23 @@ vlc_module_end ()
  *****************************************************************************/
 static int Activate( vlc_object_t *p_this )
 {
+    /* FIXME: This function is full of memory leaks and bugs in error paths. */
     intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    playlist_t *p_playlist = pl_Get( p_intf );
     char *psz_host, *psz_unix_path = NULL;
     int  *pi_socket = NULL;
 
 #ifndef WIN32
 #if defined(HAVE_ISATTY)
     /* Check that stdin is a TTY */
-    if( !config_GetInt( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
+    if( !var_InheritBool( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
     {
         msg_Warn( p_intf, "fd 0 is not a TTY" );
         return VLC_EGENERIC;
     }
 #endif
 
-    psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
+    psz_unix_path = var_InheritString( p_intf, "rc-unix" );
     if( psz_unix_path )
     {
         int i_socket;
@@ -246,7 +244,7 @@ static int Activate( vlc_object_t *p_this )
 
         msg_Dbg( p_intf, "trying UNIX socket" );
 
-        if( (i_socket = socket( PF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
+        if( (i_socket = vlc_socket( PF_LOCAL, SOCK_STREAM, 0, false ) ) < 0 )
         {
             msg_Warn( p_intf, "can't open socket: %m" );
             free( psz_unix_path );
@@ -298,7 +296,7 @@ static int Activate( vlc_object_t *p_this )
 #endif /* !WIN32 */
 
     if( ( pi_socket == NULL ) &&
-        ( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
+        ( psz_host = var_InheritString( p_intf, "rc-host" ) ) != NULL )
     {
         vlc_url_t url;
 
@@ -320,29 +318,38 @@ static int Activate( vlc_object_t *p_this )
         free( psz_host );
     }
 
-    p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
-    if( !p_intf->p_sys )
+    intf_sys_t *p_sys = malloc( sizeof( *p_sys ) );
+    if( unlikely(p_sys == NULL) )
         return VLC_ENOMEM;
 
-    p_intf->p_sys->pi_socket_listen = pi_socket;
-    p_intf->p_sys->i_socket = -1;
-    p_intf->p_sys->psz_unix_path = psz_unix_path;
-    vlc_mutex_init( &p_intf->p_sys->status_lock );
-    p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
+    p_intf->p_sys = p_sys;
+    p_sys->pi_socket_listen = pi_socket;
+    p_sys->i_socket = -1;
+    p_sys->psz_unix_path = psz_unix_path;
+    vlc_mutex_init( &p_sys->status_lock );
+    p_sys->i_last_state = PLAYLIST_STOPPED;
+    p_sys->b_input_buffering = false;
+    p_sys->p_playlist = p_playlist;
+    p_sys->p_input = NULL;
 
     /* Non-buffered stdout */
     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
 
-    p_intf->pf_run = Run;
-
 #ifdef WIN32
-    p_intf->p_sys->b_quiet = config_GetInt( p_intf, "rc-quiet" );
-    if( !p_intf->p_sys->b_quiet ) { CONSOLE_INTRO_MSG; }
-#else
-    CONSOLE_INTRO_MSG;
+    p_sys->b_quiet = var_InheritBool( p_intf, "rc-quiet" );
+    if( !p_sys->b_quiet )
 #endif
+    {
+        CONSOLE_INTRO_MSG;
+    }
+
+    if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
+        abort();
 
     msg_rc( "%s", _("Remote control interface initialized. Type `help' for help.") );
+
+    /* Listen to audio volume updates */
+    var_AddCallback( p_sys->p_playlist, "volume", VolumeChanged, p_intf );
     return VLC_SUCCESS;
 }
 
@@ -352,19 +359,30 @@ static int Activate( vlc_object_t *p_this )
 static void Deactivate( vlc_object_t *p_this )
 {
     intf_thread_t *p_intf = (intf_thread_t*)p_this;
+    intf_sys_t *p_sys = p_intf->p_sys;
+
+    vlc_cancel( p_sys->thread );
+    var_DelCallback( p_sys->p_playlist, "volume", VolumeChanged, p_intf );
+    vlc_join( p_sys->thread, NULL );
+
+    if( p_sys->p_input != NULL )
+    {
+        var_DelCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
+        vlc_object_release( p_sys->p_input );
+    }
 
-    net_ListenClose( p_intf->p_sys->pi_socket_listen );
-    if( p_intf->p_sys->i_socket != -1 )
-        net_Close( p_intf->p_sys->i_socket );
-    if( p_intf->p_sys->psz_unix_path != NULL )
+    net_ListenClose( p_sys->pi_socket_listen );
+    if( p_sys->i_socket != -1 )
+        net_Close( p_sys->i_socket );
+    if( p_sys->psz_unix_path != NULL )
     {
 #if defined(AF_LOCAL) && !defined(WIN32)
-        unlink( p_intf->p_sys->psz_unix_path );
+        unlink( p_sys->psz_unix_path );
 #endif
-        free( p_intf->p_sys->psz_unix_path );
+        free( p_sys->psz_unix_path );
     }
-    vlc_mutex_destroy( &p_intf->p_sys->status_lock );
-    free( p_intf->p_sys );
+    vlc_mutex_destroy( &p_sys->status_lock );
+    free( p_sys );
 }
 
 /*****************************************************************************
@@ -394,9 +412,6 @@ static void RegisterCallbacks( intf_thread_t *p_intf )
     ADD( "goto", INTEGER, Playlist )
     ADD( "status", INTEGER, Playlist )
 
-    /* OSD menu commands */
-    ADD(  "menu", STRING, Menu )
-
     /* DVD commands */
     ADD( "pause", VOID, Input )
     ADD( "seek", INTEGER, Input )
@@ -428,8 +443,8 @@ static void RegisterCallbacks( intf_thread_t *p_intf )
     ADD( "volume", STRING, Volume )
     ADD( "volup", STRING, VolumeMove )
     ADD( "voldown", STRING, VolumeMove )
-    ADD( "adev", STRING, AudioConfig )
-    ADD( "achan", STRING, AudioConfig )
+    ADD( "adev", STRING, AudioDevice )
+    ADD( "achan", STRING, AudioChannel )
 
     /* misc menu commands */
     ADD( "stats", BOOL, Statistics )
@@ -443,131 +458,110 @@ static void RegisterCallbacks( intf_thread_t *p_intf )
  * This part of the interface is in a separate thread so that we can call
  * exec() from within it without annoying the rest of the program.
  *****************************************************************************/
-static void Run( intf_thread_t *p_intf )
+static void *Run( void *data )
 {
-    input_thread_t * p_input = NULL;
-    playlist_t *     p_playlist = pl_Hold( p_intf );
+    intf_thread_t *p_intf = data;
+    intf_sys_t *p_sys = p_intf->p_sys;
 
     char p_buffer[ MAX_LINE_LENGTH + 1 ];
-    bool b_showpos = config_GetInt( p_intf, "rc-show-pos" );
+    bool b_showpos = var_InheritBool( p_intf, "rc-show-pos" );
     bool b_longhelp = false;
 
     int  i_size = 0;
     int  i_oldpos = 0;
     int  i_newpos;
-    int  canc = vlc_savecancel();
+    int  canc = vlc_savecancel( );
 
     p_buffer[0] = 0;
 
-    /* Register commands that will be cleaned up upon object destruction */
-    p_intf->p_sys->p_playlist = p_playlist;
-    RegisterCallbacks( p_intf );
-
-    /* status callbacks */
-    /* Listen to audio volume updates */
-    var_AddCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, p_intf );
-
 #ifdef WIN32
     /* Get the file descriptor of the console input */
     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
     {
         msg_Err( p_intf, "couldn't find user input handle" );
-        vlc_object_kill( p_intf );
+        return;
     }
 #endif
 
-    while( vlc_object_alive( p_intf ) )
+    /* Register commands that will be cleaned up upon object destruction */
+    RegisterCallbacks( p_intf );
+
+    /* status callbacks */
+
+    for( ;; )
     {
         char *psz_cmd, *psz_arg;
         bool b_complete;
 
-        if( p_intf->p_sys->pi_socket_listen != NULL &&
-            p_intf->p_sys->i_socket == -1 )
+        vlc_restorecancel( canc );
+
+        if( p_sys->pi_socket_listen != NULL && p_sys->i_socket == -1 )
         {
-            p_intf->p_sys->i_socket =
-                net_Accept( p_intf, p_intf->p_sys->pi_socket_listen,
-                            INTF_IDLE_SLEEP );
-            if( p_intf->p_sys->i_socket == -1 ) continue;
+            p_sys->i_socket =
+                net_Accept( p_intf, p_sys->pi_socket_listen );
+            if( p_sys->i_socket == -1 ) continue;
         }
 
         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
+        canc = vlc_savecancel( );
 
         /* Manage the input part */
-        if( p_input == NULL )
+        if( p_sys->p_input == NULL )
         {
-            p_input = playlist_CurrentInput( p_playlist );
+            p_sys->p_input = playlist_CurrentInput( p_sys->p_playlist );
             /* New input has been registered */
-            if( p_input )
+            if( p_sys->p_input )
             {
-                if( !p_input->b_dead || vlc_object_alive (p_input) )
-                {
-                    char *psz_uri =
-                            input_item_GetURI( input_GetItem( p_input ) );
-                    msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
-                    free( psz_uri );
-                    msg_rc( STATUS_CHANGE "( audio volume: %d )",
-                            config_GetInt( p_intf, "volume" ));
-                }
-                var_AddCallback( p_input, "state", StateChanged, p_intf );
-                var_AddCallback( p_input, "rate-faster", RateChanged, p_intf );
-                var_AddCallback( p_input, "rate-slower", RateChanged, p_intf );
-                var_AddCallback( p_input, "rate", RateChanged, p_intf );
-                var_AddCallback( p_input, "time-offset", TimeOffsetChanged,
-                                 p_intf );
-                var_AddCallback( p_input, "frame-next", RateChanged, p_intf );
+                char *psz_uri = input_item_GetURI( input_GetItem( p_sys->p_input ) );
+                msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
+                free( psz_uri );
+
+                var_AddCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
             }
         }
-        else if( p_input->b_dead )
+#warning This is not reliable...
+        else if( p_sys->p_input->b_dead )
         {
-            var_DelCallback( p_input, "state", StateChanged, p_intf );
-            var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
-            var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
-            var_DelCallback( p_input, "rate", RateChanged, p_intf );
-            var_DelCallback( p_input, "time-offset", TimeOffsetChanged,
-                             p_intf );
-            var_DelCallback( p_input, "frame-next", RateChanged, p_intf );
-            vlc_object_release( p_input );
-            p_input = NULL;
+            var_DelCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
+            vlc_object_release( p_sys->p_input );
+            p_sys->p_input = NULL;
 
-            if( p_playlist )
-            {
-                PL_LOCK;
-                p_intf->p_sys->i_last_state = (int) PLAYLIST_STOPPED;
-                msg_rc( STATUS_CHANGE "( stop state: 0 )" );
-                PL_UNLOCK;
-            }
+            p_sys->i_last_state = PLAYLIST_STOPPED;
+            msg_rc( STATUS_CHANGE "( stop state: 0 )" );
         }
 
-        if( (p_input != NULL) && !p_input->b_dead && vlc_object_alive (p_input) &&
-            (p_playlist != NULL) )
+        if( p_sys->p_input != NULL )
         {
+            playlist_t *p_playlist = p_sys->p_playlist;
+
             PL_LOCK;
             int status = playlist_Status( p_playlist );
-            if( p_intf->p_sys->i_last_state != status )
+            PL_UNLOCK;
+
+            if( p_sys->i_last_state != status )
             {
                 if( status == PLAYLIST_STOPPED )
                 {
-                    p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
+                    p_sys->i_last_state = PLAYLIST_STOPPED;
                     msg_rc( STATUS_CHANGE "( stop state: 5 )" );
                 }
                 else if( status == PLAYLIST_RUNNING )
                 {
-                    p_intf->p_sys->i_last_state = PLAYLIST_RUNNING;
+                    p_sys->i_last_state = PLAYLIST_RUNNING;
                     msg_rc( STATUS_CHANGE "( play state: 3 )" );
                 }
                 else if( status == PLAYLIST_PAUSED )
                 {
-                    p_intf->p_sys->i_last_state = PLAYLIST_PAUSED;
+                    p_sys->i_last_state = PLAYLIST_PAUSED;
                     msg_rc( STATUS_CHANGE "( pause state: 4 )" );
                 }
             }
-            PL_UNLOCK;
         }
 
-        if( p_input && b_showpos )
+        if( p_sys->p_input && b_showpos )
         {
-            i_newpos = 100 * var_GetFloat( p_input, "position" );
+            i_newpos = 100 * var_GetFloat( p_sys->p_input, "position" );
             if( i_oldpos != i_newpos )
             {
                 i_oldpos = i_newpos;
@@ -635,9 +629,12 @@ static void Run( intf_thread_t *p_intf )
         {
             vlc_value_t val;
             int i_ret;
-
             val.psz_string = psz_arg;
-            i_ret = var_Set( p_intf, psz_cmd, val );
+
+            if ((var_Type( p_intf, psz_cmd) & VLC_VAR_CLASS) == VLC_VAR_VOID)
+                i_ret = var_TriggerCallback( p_intf, psz_cmd );
+            else
+                i_ret = var_Set( p_intf, psz_cmd, val );
             msg_rc( "%s: returned %i (%s)",
                     psz_cmd, i_ret, vlc_error( i_ret ) );
         }
@@ -650,7 +647,10 @@ static void Run( intf_thread_t *p_intf )
             val.psz_string = psz_arg;
             /* FIXME: it's a global command, but we should pass the
              * local object as an argument, not p_intf->p_libvlc. */
-            i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
+            if ((var_Type( p_intf->p_libvlc, psz_cmd) & VLC_VAR_CLASS) == VLC_VAR_VOID)
+                i_ret = var_TriggerCallback( p_intf, psz_cmd );
+            else
+                i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
             if( i_ret != 0 )
             {
                 msg_rc( "%s: returned %i (%s)",
@@ -660,21 +660,21 @@ static void Run( intf_thread_t *p_intf )
         else if( !strcmp( psz_cmd, "logout" ) )
         {
             /* Close connection */
-            if( p_intf->p_sys->i_socket != -1 )
+            if( p_sys->i_socket != -1 )
             {
-                net_Close( p_intf->p_sys->i_socket );
+                net_Close( p_sys->i_socket );
+                p_sys->i_socket = -1;
             }
-            p_intf->p_sys->i_socket = -1;
         }
         else if( !strcmp( psz_cmd, "info" ) )
         {
-            if( p_input )
+            if( p_sys->p_input )
             {
                 int i, j;
-                vlc_mutex_lock( &input_GetItem(p_input)->lock );
-                for ( i = 0; i < input_GetItem(p_input)->i_categories; i++ )
+                vlc_mutex_lock( &input_GetItem(p_sys->p_input)->lock );
+                for ( i = 0; i < input_GetItem(p_sys->p_input)->i_categories; i++ )
                 {
-                    info_category_t *p_category = input_GetItem(p_input)
+                    info_category_t *p_category = input_GetItem(p_sys->p_input)
                                                         ->pp_categories[i];
 
                     msg_rc( "+----[ %s ]", p_category->psz_name );
@@ -688,7 +688,7 @@ static void Run( intf_thread_t *p_intf )
                     msg_rc( "| " );
                 }
                 msg_rc( "+----[ end of stream info ]" );
-                vlc_mutex_unlock( &input_GetItem(p_input)->lock );
+                vlc_mutex_unlock( &input_GetItem(p_sys->p_input)->lock );
             }
             else
             {
@@ -697,7 +697,7 @@ static void Run( intf_thread_t *p_intf )
         }
         else if( !strcmp( psz_cmd, "is_playing" ) )
         {
-            if( ! p_input )
+            if( p_sys->p_input == NULL )
             {
                 msg_rc( "0" );
             }
@@ -708,39 +708,39 @@ static void Run( intf_thread_t *p_intf )
         }
         else if( !strcmp( psz_cmd, "get_time" ) )
         {
-            if( ! p_input )
+            if( p_sys->p_input == NULL )
             {
                 msg_rc("0");
             }
             else
             {
                 vlc_value_t time;
-                var_Get( p_input, "time", &time );
+                var_Get( p_sys->p_input, "time", &time );
                 msg_rc( "%"PRIu64, time.i_time / 1000000);
             }
         }
         else if( !strcmp( psz_cmd, "get_length" ) )
         {
-            if( ! p_input )
+            if( p_sys->p_input == NULL )
             {
                 msg_rc("0");
             }
             else
             {
                 vlc_value_t time;
-                var_Get( p_input, "length", &time );
+                var_Get( p_sys->p_input, "length", &time );
                 msg_rc( "%"PRIu64, time.i_time / 1000000);
             }
         }
         else if( !strcmp( psz_cmd, "get_title" ) )
         {
-            if( ! p_input )
+            if( p_sys->p_input == NULL )
             {
                 msg_rc("%s", "");
             }
             else
             {
-                msg_rc( "%s", input_GetItem(p_input)->psz_name );
+                msg_rc( "%s", input_GetItem(p_sys->p_input)->psz_name );
             }
         }
         else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
@@ -754,45 +754,34 @@ static void Run( intf_thread_t *p_intf )
         }
         else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) )
         {
-            var_SetInteger( p_intf->p_libvlc, "key-pressed",
-                            config_GetInt( p_intf, psz_arg ) );
+            var_SetInteger( p_intf->p_libvlc, "key-action",
+                            vlc_GetActionId( psz_arg ) );
         }
         else switch( psz_cmd[0] )
         {
         case 'f':
         case 'F':
-            if( p_input )
-            {
-                vout_thread_t *p_vout;
-                p_vout = input_GetVout( p_input );
+        {
+            bool fs;
 
+            if( !strncasecmp( psz_arg, "on", 2 ) )
+                var_SetBool( p_sys->p_playlist, "fullscreen", fs = true );
+            else if( !strncasecmp( psz_arg, "off", 3 ) )
+                var_SetBool( p_sys->p_playlist, "fullscreen", fs = false );
+            else
+                fs = var_ToggleBool( p_sys->p_playlist, "fullscreen" );
+
+            if( p_sys->p_input == NULL )
+            {
+                vout_thread_t *p_vout = input_GetVout( p_sys->p_input );
                 if( p_vout )
                 {
-                    vlc_value_t val;
-                    bool b_update = false;
-                    var_Get( p_vout, "fullscreen", &val );
-                    val.b_bool = !val.b_bool;
-                    if( !strncmp( psz_arg, "on", 2 )
-                        && ( val.b_bool == true ) )
-                    {
-                        b_update = true;
-                        val.b_bool = true;
-                    }
-                    else if( !strncmp( psz_arg, "off", 3 )
-                             && ( val.b_bool == false ) )
-                    {
-                        b_update = true;
-                        val.b_bool = false;
-                    }
-                    else if( strncmp( psz_arg, "off", 3 )
-                             && strncmp( psz_arg, "on", 2 ) )
-                        b_update = true;
-                    if( b_update ) var_Set( p_vout, "fullscreen", val );
+                    var_SetBool( p_vout, "fullscreen", fs );
                     vlc_object_release( p_vout );
                 }
             }
             break;
-
+        }
         case 's':
         case 'S':
             ;
@@ -814,21 +803,9 @@ static void Run( intf_thread_t *p_intf )
     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
     msg_rc( STATUS_CHANGE "( quit )" );
 
-    if( p_input )
-    {
-        var_DelCallback( p_input, "state", StateChanged, p_intf );
-        var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
-        var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
-        var_DelCallback( p_input, "rate", RateChanged, p_intf );
-        var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
-        var_DelCallback( p_input, "frame-next", RateChanged, p_intf );
-        vlc_object_release( p_input );
-    }
-
-    pl_Release( p_intf );
-
-    var_DelCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, p_intf );
     vlc_restorecancel( canc );
+
+    return NULL;
 }
 
 static void Help( intf_thread_t *p_intf, bool b_longhelp)
@@ -874,7 +851,7 @@ static void Help( intf_thread_t *p_intf, bool b_longhelp)
     msg_rc("%s", _("| volume [X] . . . . . . . . . .  set/get audio volume"));
     msg_rc("%s", _("| volup [X]  . . . . . . .  raise audio volume X steps"));
     msg_rc("%s", _("| voldown [X]  . . . . . .  lower audio volume X steps"));
-    msg_rc("%s", _("| adev [X] . . . . . . . . . . .  set/get audio device"));
+    msg_rc("%s", _("| adev [device]  . . . . . . . .  set/get audio device"));
     msg_rc("%s", _("| achan [X]. . . . . . . . . .  set/get audio channels"));
     msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track"));
     msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track"));
@@ -882,7 +859,7 @@ static void Help( intf_thread_t *p_intf, bool b_longhelp)
     msg_rc("%s", _("| vcrop [X]  . . . . . . . . . . .  set/get video crop"));
     msg_rc("%s", _("| vzoom [X]  . . . . . . . . . . .  set/get video zoom"));
     msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot"));
-    msg_rc("%s", _("| strack [X] . . . . . . . . . set/get subtitles track"));
+    msg_rc("%s", _("| strack [X] . . . . . . . . .  set/get subtitle track"));
     msg_rc("%s", _("| key [hotkey name] . . . . . .  simulate hotkey press"));
     msg_rc("%s", _("| menu . . [on|off|up|down|left|right|select] use menu"));
     msg_rc(  "| ");
@@ -931,89 +908,104 @@ static void Help( intf_thread_t *p_intf, bool b_longhelp)
 /********************************************************************
  * Status callback routines
  ********************************************************************/
-static int TimeOffsetChanged( vlc_object_t *p_this, char const *psz_cmd,
-    vlc_value_t oldval, vlc_value_t newval, void *p_data )
-{
-    VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd);
-    VLC_UNUSED(oldval); VLC_UNUSED(newval);
-    intf_thread_t *p_intf = (intf_thread_t*)p_data;
-    input_thread_t *p_input =
-        playlist_CurrentInput( p_intf->p_sys->p_playlist );
-
-    if( p_input )
-    {
-        vlc_mutex_lock( &p_intf->p_sys->status_lock );
-        msg_rc( STATUS_CHANGE "( time-offset: %"PRId64"s )",
-                (var_GetTime( p_input, "time-offset" )/1000000) );
-        vlc_mutex_unlock( &p_intf->p_sys->status_lock );
-        vlc_object_release( p_input );
-    }
-    return VLC_SUCCESS;
-}
-
 static int VolumeChanged( vlc_object_t *p_this, char const *psz_cmd,
     vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
+    (void) p_this;
     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval);
     intf_thread_t *p_intf = (intf_thread_t*)p_data;
 
     vlc_mutex_lock( &p_intf->p_sys->status_lock );
-    msg_rc( STATUS_CHANGE "( audio volume: %d )",
-            config_GetInt( p_this, "volume") );
+    msg_rc( STATUS_CHANGE "( audio volume: %ld )",
+            lroundf(newval.f_float * AOUT_VOLUME_DEFAULT) );
     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
     return VLC_SUCCESS;
 }
 
-static int StateChanged( vlc_object_t *p_this, char const *psz_cmd,
-    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+static void StateChanged( intf_thread_t *p_intf, input_thread_t *p_input )
 {
-    VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
-    intf_thread_t *p_intf = (intf_thread_t*)p_data;
-    playlist_t    *p_playlist = p_intf->p_sys->p_playlist;
-    int i_status;
-    char cmd[6];
+    playlist_t *p_playlist = p_intf->p_sys->p_playlist;
 
     PL_LOCK;
-    i_status = playlist_Status( p_playlist );
+    const int i_status = playlist_Status( p_playlist );
     PL_UNLOCK;
 
+    /* */
+    const char *psz_cmd;
     switch( i_status )
     {
     case PLAYLIST_STOPPED:
-        strcpy( cmd, "stop" );
+        psz_cmd = "stop";
         break;
     case PLAYLIST_RUNNING:
-        strcpy( cmd, "play" );
+        psz_cmd = "play";
         break;
     case PLAYLIST_PAUSED:
-        strcpy( cmd, "pause" );
+        psz_cmd = "pause";
         break;
     default:
-        cmd[0] = '\0';
-    } /* var_GetInteger( p_input, "state" )  */
-    vlc_mutex_lock( &p_intf->p_sys->status_lock );
-    msg_rc( STATUS_CHANGE "( %s state: %d ): %s", cmd, newval.i_int,
-            ppsz_input_state[ newval.i_int ] );
+        psz_cmd = "";
+        break;
+    }
 
+    /* */
+    const int i_state = var_GetInteger( p_input, "state" );
+
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    msg_rc( STATUS_CHANGE "( %s state: %d ): %s", psz_cmd,
+            i_state, ppsz_input_state[i_state] );
+    vlc_mutex_unlock( &p_intf->p_sys->status_lock );
+}
+static void RateChanged( intf_thread_t *p_intf,
+                         input_thread_t *p_input )
+{
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    msg_rc( STATUS_CHANGE "( new rate: %.3f )",
+            var_GetFloat( p_input, "rate" ) );
+    vlc_mutex_unlock( &p_intf->p_sys->status_lock );
+}
+static void PositionChanged( intf_thread_t *p_intf,
+                             input_thread_t *p_input )
+{
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    if( p_intf->p_sys->b_input_buffering )
+        msg_rc( STATUS_CHANGE "( time: %"PRId64"s )",
+                (var_GetTime( p_input, "time" )/1000000) );
+    p_intf->p_sys->b_input_buffering = false;
+    vlc_mutex_unlock( &p_intf->p_sys->status_lock );
+}
+static void CacheChanged( intf_thread_t *p_intf )
+{
+    vlc_mutex_lock( &p_intf->p_sys->status_lock );
+    p_intf->p_sys->b_input_buffering = true;
     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
-    return VLC_SUCCESS;
 }
 
-static int RateChanged( vlc_object_t *p_this, char const *psz_cmd,
-    vlc_value_t oldval, vlc_value_t newval, void *p_data )
+static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
+                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
-    VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd);
-    VLC_UNUSED(oldval); VLC_UNUSED(newval);
-    intf_thread_t *p_intf = (intf_thread_t*)p_data;
-    input_thread_t *p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist );
+    VLC_UNUSED(psz_cmd);
+    VLC_UNUSED(oldval);
+    input_thread_t *p_input = (input_thread_t*)p_this;
+    intf_thread_t *p_intf = p_data;
 
-    if( p_input )
+    switch( newval.i_int )
     {
-        vlc_mutex_lock( &p_intf->p_sys->status_lock );
-        msg_rc( STATUS_CHANGE "( new rate: %d )",
-                var_GetInteger( p_input, "rate" ) );
-        vlc_mutex_unlock( &p_intf->p_sys->status_lock );
-        vlc_object_release( p_input );
+    case INPUT_EVENT_STATE:
+    case INPUT_EVENT_DEAD:
+        StateChanged( p_intf, p_input );
+        break;
+    case INPUT_EVENT_RATE:
+        RateChanged( p_intf, p_input );
+        break;
+    case INPUT_EVENT_POSITION:
+        PositionChanged( p_intf, p_input );
+        break;
+    case INPUT_EVENT_CACHE:
+        CacheChanged( p_intf );
+        break;
+    default:
+        break;
     }
     return VLC_SUCCESS;
 }
@@ -1065,9 +1057,9 @@ static int Input( vlc_object_t *p_this, char const *psz_cmd,
     {
         if( var_GetBool( p_input, "can-rate" ) )
         {
-            int i_rate = var_GetInteger( p_input, "rate" );
-            i_rate = (i_rate < 0) ? -i_rate : i_rate * 2;
-            var_SetInteger( p_input, "rate", i_rate );
+            float f_rate = var_GetFloat( p_input, "rate" );
+            f_rate = (f_rate < 0) ? -f_rate : f_rate * 2;
+            var_SetFloat( p_input, "rate", f_rate );
         }
         else
         {
@@ -1079,9 +1071,9 @@ static int Input( vlc_object_t *p_this, char const *psz_cmd,
     {
         if( var_GetBool( p_input, "can-rewind" ) )
         {
-            int i_rate = var_GetInteger( p_input, "rate" );
-            i_rate = (i_rate > 0) ? -i_rate : i_rate / 2;
-            var_SetInteger( p_input, "rate", i_rate );
+            float f_rate = var_GetFloat( p_input, "rate" );
+            f_rate = (f_rate > 0) ? -f_rate : f_rate * 2;
+            var_SetFloat( p_input, "rate", f_rate );
         }
         else
         {
@@ -1091,17 +1083,17 @@ static int Input( vlc_object_t *p_this, char const *psz_cmd,
     }
     else if ( !strcmp( psz_cmd, "faster" ) )
     {
-        var_TriggerCallback( p_input, "rate-faster" );
+        var_TriggerCallback( p_intf->p_sys->p_playlist, "rate-faster" );
         i_error = VLC_SUCCESS;
     }
     else if ( !strcmp( psz_cmd, "slower" ) )
     {
-        var_TriggerCallback( p_input, "rate-slower" );
+        var_TriggerCallback( p_intf->p_sys->p_playlist, "rate-slower" );
         i_error = VLC_SUCCESS;
     }
     else if ( !strcmp( psz_cmd, "normal" ) )
     {
-        var_SetInteger( p_input, "rate", INPUT_RATE_DEFAULT );
+        var_SetFloat( p_intf->p_sys->p_playlist, "rate", 1. );
         i_error = VLC_SUCCESS;
     }
     else if ( !strcmp( psz_cmd, "frame" ) )
@@ -1209,17 +1201,18 @@ static int Input( vlc_object_t *p_this, char const *psz_cmd,
             for ( i = 0; i < val.p_list->i_count; i++ )
             {
                 if ( i_value == val.p_list->p_values[i].i_int )
-                    msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
+                    msg_rc( "| %"PRId64" - %s *",
+                            val.p_list->p_values[i].i_int,
                             text.p_list->p_values[i].psz_string );
                 else
-                    msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
+                    msg_rc( "| %"PRId64" - %s",
+                            val.p_list->p_values[i].i_int,
                             text.p_list->p_values[i].psz_string );
             }
             var_FreeList( &val, &text );
             msg_rc( "+----[ end of %s ]", val_name.psz_string );
-
-            free( val_name.psz_string );
         }
+        free( val_name.psz_string );
     }
 out:
     vlc_object_release( p_input );
@@ -1289,8 +1282,8 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
 
         if( strlen( newval.psz_string ) > 0 )
         {
-            if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
-                 ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
+            if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
+                 ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
             {
                 b_update = false;
             }
@@ -1311,8 +1304,8 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
 
         if( strlen( newval.psz_string ) > 0 )
         {
-            if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
-                 ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
+            if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
+                 ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
             {
                 b_update = false;
             }
@@ -1333,8 +1326,8 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
 
         if( strlen( newval.psz_string ) > 0 )
         {
-            if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
-                 ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
+            if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
+                 ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
             {
                 b_update = false;
             }
@@ -1349,23 +1342,26 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
     }
     else if (!strcmp( psz_cmd, "goto" ) )
     {
-        int i_pos = atoi( newval.psz_string );
-        /* The playlist stores 2 times the same item: onelevel & category */
-        int i_size = p_playlist->items.i_size / 2;
+        PL_LOCK;
+        unsigned i_pos = atoi( newval.psz_string );
+        unsigned i_size = p_playlist->items.i_size;
 
         if( i_pos <= 0 )
             msg_rc( "%s", _("Error: `goto' needs an argument greater than zero.") );
         else if( i_pos <= i_size )
         {
             playlist_item_t *p_item, *p_parent;
-            p_item = p_parent = p_playlist->items.p_elems[i_pos*2-1];
+            p_item = p_parent = p_playlist->items.p_elems[i_pos-1];
             while( p_parent->p_parent )
                 p_parent = p_parent->p_parent;
-            playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Unlocked,
+            playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked,
                     p_parent, p_item );
         }
         else
-            msg_rc( _("Playlist has only %d elements"), i_size );
+            msg_rc( vlc_ngettext("Playlist has only %u element",
+                                 "Playlist has only %u elements", i_size),
+                     i_size );
+        PL_UNLOCK;
     }
     else if( !strcmp( psz_cmd, "stop" ) )
     {
@@ -1379,7 +1375,7 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
     else if( !strcmp( psz_cmd, "add" ) &&
              newval.psz_string && *newval.psz_string )
     {
-        input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
+        input_item_t *p_item = parse_MRL( newval.psz_string );
 
         if( p_item )
         {
@@ -1397,7 +1393,7 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
     else if( !strcmp( psz_cmd, "enqueue" ) &&
              newval.psz_string && *newval.psz_string )
     {
-        input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
+        input_item_t *p_item = parse_MRL( newval.psz_string );
 
         if( p_item )
         {
@@ -1419,8 +1415,10 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
 
     else if( !strcmp( psz_cmd, "sort" ))
     {
+        PL_LOCK;
         playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_onelevel,
                                     SORT_ARTIST, ORDER_NORMAL );
+        PL_UNLOCK;
     }
     else if( !strcmp( psz_cmd, "status" ) )
     {
@@ -1430,29 +1428,37 @@ static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
             /* Replay the current state of the system. */
             char *psz_uri =
                     input_item_GetURI( input_GetItem( p_input ) );
-            msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
-            free( psz_uri );
-            msg_rc( STATUS_CHANGE "( audio volume: %d )",
-                    config_GetInt( p_intf, "volume" ));
-
-            PL_LOCK;
-            switch( playlist_Status(p_playlist) )
+            vlc_object_release( p_input );
+            if( likely(psz_uri != NULL) )
             {
-                case PLAYLIST_STOPPED:
-                    msg_rc( STATUS_CHANGE "( stop state: 5 )" );
-                    break;
-                case PLAYLIST_RUNNING:
-                    msg_rc( STATUS_CHANGE "( play state: 3 )" );
-                    break;
-                case PLAYLIST_PAUSED:
-                    msg_rc( STATUS_CHANGE "( pause state: 4 )" );
-                    break;
-                default:
-                    msg_rc( STATUS_CHANGE "( unknown state: -1 )" );
-                    break;
+                msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
+                free( psz_uri );
             }
-            PL_UNLOCK;
-            vlc_object_release( p_input );
+        }
+
+        float volume = playlist_VolumeGet( p_playlist );
+        if( volume >= 0.f )
+            msg_rc( STATUS_CHANGE "( audio volume: %ld )",
+                    lroundf(volume * AOUT_VOLUME_DEFAULT) );
+
+        int status;
+        PL_LOCK;
+        status = playlist_Status(p_playlist);
+        PL_UNLOCK;
+        switch( status )
+        {
+            case PLAYLIST_STOPPED:
+                msg_rc( STATUS_CHANGE "( stop state: 5 )" );
+                break;
+            case PLAYLIST_RUNNING:
+                msg_rc( STATUS_CHANGE "( play state: 3 )" );
+                break;
+            case PLAYLIST_PAUSED:
+                msg_rc( STATUS_CHANGE "( pause state: 4 )" );
+                break;
+            default:
+                msg_rc( STATUS_CHANGE "( unknown state: -1 )" );
+                break;
         }
     }
 
@@ -1511,32 +1517,19 @@ static int Volume( vlc_object_t *p_this, char const *psz_cmd,
     if ( *newval.psz_string )
     {
         /* Set. */
-        audio_volume_t i_volume = atoi( newval.psz_string );
-        if ( (i_volume > (audio_volume_t)AOUT_VOLUME_MAX) )
-        {
-            msg_rc( "Volume must be in the range %d-%d.", AOUT_VOLUME_MIN,
-                    AOUT_VOLUME_MAX );
-            i_error = VLC_EBADVAR;
-        }
-        else
-        {
-            if( i_volume == AOUT_VOLUME_MIN )
-                aout_ToggleMute( p_playlist, NULL );
-            if( !aout_VolumeSet( p_playlist, i_volume ) )
-                i_error = VLC_SUCCESS;
-            osd_Volume( p_this );
-            msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
-        }
+        int i_volume = atoi( newval.psz_string );
+        if( !playlist_VolumeSet( p_playlist,
+                             i_volume / (float)AOUT_VOLUME_DEFAULT ) )
+            i_error = VLC_SUCCESS;
+        playlist_MuteSet( p_playlist, i_volume == 0 );
+        msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
     }
     else
     {
         /* Get. */
-        audio_volume_t i_volume;
-        if ( !aout_VolumeGet( p_playlist, &i_volume ) )
-        {
-            msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
-            i_error = VLC_SUCCESS;
-        }
+        msg_rc( STATUS_CHANGE "( audio volume: %ld )",
+               lroundf( playlist_VolumeGet( p_playlist ) * AOUT_VOLUME_DEFAULT ) );
+        i_error = VLC_SUCCESS;
     }
 
     return i_error;
@@ -1547,12 +1540,11 @@ static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
 {
     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
     intf_thread_t *p_intf = (intf_thread_t*)p_this;
-    audio_volume_t i_volume;
+    float volume;
     input_thread_t *p_input =
         playlist_CurrentInput( p_intf->p_sys->p_playlist );
     int i_nb_steps = atoi(newval.psz_string);
     int i_error = VLC_SUCCESS;
-    int i_volume_step = 0;
 
     if( !p_input )
         return VLC_ENOOBJ;
@@ -1565,25 +1557,14 @@ static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
         return VLC_EGENERIC;
     }
 
-    i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
-    if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/i_volume_step) )
-    {
-        i_nb_steps = 1;
-    }
-
-    if ( !strcmp(psz_cmd, "volup") )
-    {
-        if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
-            i_error = VLC_EGENERIC;
-    }
-    else
-    {
-        if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
-            i_error = VLC_EGENERIC;
-    }
-    osd_Volume( p_this );
+    if( !strcmp(psz_cmd, "voldown") )
+        i_nb_steps *= -1;
+    if( playlist_VolumeUp( p_intf->p_sys->p_playlist, i_nb_steps, &volume ) < 0 )
+        i_error = VLC_EGENERIC;
 
-    if ( !i_error ) msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
+    if ( !i_error )
+        msg_rc( STATUS_CHANGE "( audio volume: %ld )",
+                lroundf( volume * AOUT_VOLUME_DEFAULT ) );
     return i_error;
 }
 
@@ -1597,7 +1578,7 @@ static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
         playlist_CurrentInput( p_intf->p_sys->p_playlist );
     vout_thread_t * p_vout;
     const char * psz_variable = NULL;
-    int i_error;
+    int i_error = VLC_SUCCESS;
 
     if( !p_input )
         return VLC_ENOOBJ;
@@ -1643,7 +1624,7 @@ static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
     }
     else if( !strcmp( psz_cmd, "snapshot" ) )
     {
-        i_error = var_SetBool( p_vout, psz_variable, true );
+        var_TriggerCallback( p_vout, psz_variable );
     }
     else
     {
@@ -1711,167 +1692,102 @@ static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
         msg_rc( "+----[ end of %s ]", val_name.psz_string );
 
         free( val_name.psz_string );
-
-        i_error = VLC_SUCCESS;
     }
     vlc_object_release( p_vout );
     return i_error;
 }
 
-static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
-                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
+static int AudioDevice( vlc_object_t *obj, char const *cmd,
+                        vlc_value_t old, vlc_value_t cur, void *dummy )
 {
-    VLC_UNUSED(oldval); VLC_UNUSED(p_data);
-    intf_thread_t *p_intf = (intf_thread_t*)p_this;
-    input_thread_t *p_input =
-        playlist_CurrentInput( p_intf->p_sys->p_playlist );
-    aout_instance_t * p_aout;
-    const char * psz_variable;
-    vlc_value_t val_name;
-    int i_error;
-
-    if( !p_input )
+    intf_thread_t *p_intf = (intf_thread_t *)obj;
+    audio_output_t *p_aout = playlist_GetAout( pl_Get(p_intf) );
+    if( p_aout == NULL )
         return VLC_ENOOBJ;
 
-    int state = var_GetInteger( p_input, "state" );
-    if( state == PAUSE_S )
+    if( !*cur.psz_string )
     {
-        msg_rc( "%s", _("Type 'menu select' or 'pause' to continue.") );
-        return VLC_EGENERIC;
-    }
+        char **ids, **names;
+        int n = aout_DevicesList( p_aout, &ids, &names );
+        if( n < 0 )
+            goto out;
 
-    p_aout = input_GetAout( p_input );
-    vlc_object_release( p_input );
-    if ( p_aout == NULL )
-         return VLC_ENOOBJ;
+        char *dev = aout_DeviceGet( p_aout );
+        const char *devstr = (dev != NULL) ? dev : "";
 
-    if ( !strcmp( psz_cmd, "adev" ) )
-    {
-        psz_variable = "audio-device";
+        msg_rc( "+----[ %s ]", cmd );
+        for ( int i = 0; i < n; i++ )
+        {
+            const char *fmt = "| %s - %s";
+
+            if( !strcmp(devstr, ids[i]) )
+                fmt = "| %s - %s *";
+            msg_rc( fmt, ids[i], names[i] );
+            free( names[i] );
+            free( ids[i] );
+        }
+        msg_rc( "+----[ end of %s ]", cmd );
+
+        free( dev );
+        free( names );
+        free( ids );
     }
     else
-    {
-        psz_variable = "audio-channels";
-    }
+        aout_DeviceSet( p_aout, cur.psz_string );
+out:
+    vlc_object_release( p_aout );
+    (void) old; (void) dummy;
+    return VLC_SUCCESS;
+}
 
-    /* Get the descriptive name of the variable */
-    var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_GETTEXT,
-                 &val_name, NULL );
-    if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
+static int AudioChannel( vlc_object_t *obj, char const *cmd,
+                         vlc_value_t old, vlc_value_t cur, void *dummy )
+{
+    intf_thread_t *p_intf = (intf_thread_t*)obj;
+    vlc_object_t *p_aout = (vlc_object_t *)playlist_GetAout( pl_Get(p_intf) );
+    if ( p_aout == NULL )
+         return VLC_ENOOBJ;
 
-    if ( !*newval.psz_string )
+    int ret = VLC_SUCCESS;
+
+    if ( !*cur.psz_string )
     {
         /* Retrieve all registered ***. */
         vlc_value_t val, text;
-        int i, i_value;
-
-        if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
-        {
-            vlc_object_release( (vlc_object_t *)p_aout );
-            return VLC_EGENERIC;
-        }
-        i_value = val.i_int;
-
-        if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
+        if ( var_Change( p_aout, "stereo-mode",
                          VLC_VAR_GETLIST, &val, &text ) < 0 )
         {
-            vlc_object_release( (vlc_object_t *)p_aout );
-            return VLC_EGENERIC;
+            ret = VLC_ENOVAR;
+            goto out;
         }
 
-        msg_rc( "+----[ %s ]", val_name.psz_string );
-        for ( i = 0; i < val.p_list->i_count; i++ )
+        int i_value = var_GetInteger( p_aout, "stereo-mode" );
+
+        msg_rc( "+----[ %s ]", cmd );
+        for ( int i = 0; i < val.p_list->i_count; i++ )
         {
             if ( i_value == val.p_list->p_values[i].i_int )
-                msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
+                msg_rc( "| %"PRId64" - %s *", val.p_list->p_values[i].i_int,
                         text.p_list->p_values[i].psz_string );
             else
-                msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
+                msg_rc( "| %"PRId64" - %s", val.p_list->p_values[i].i_int,
                         text.p_list->p_values[i].psz_string );
         }
         var_FreeList( &val, &text );
-        msg_rc( "+----[ end of %s ]", val_name.psz_string );
-
-        free( val_name.psz_string );
-        i_error = VLC_SUCCESS;
-    }
-    else
-    {
-        vlc_value_t val;
-        val.i_int = atoi( newval.psz_string );
-
-        i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
-    }
-    vlc_object_release( (vlc_object_t *)p_aout );
-
-    return i_error;
-}
-
-/* OSD menu commands */
-static int Menu( vlc_object_t *p_this, char const *psz_cmd,
-    vlc_value_t oldval, vlc_value_t newval, void *p_data )
-{
-    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
-    intf_thread_t *p_intf = (intf_thread_t*)p_this;
-    playlist_t    *p_playlist = p_intf->p_sys->p_playlist;
-    int i_error = VLC_SUCCESS;
-    vlc_value_t val;
-
-    if ( !*newval.psz_string )
-    {
-        msg_rc( "%s", _("Please provide one of the following parameters:") );
-        msg_rc( "[on|off|up|down|left|right|select]" );
-        return VLC_EGENERIC;
-    }
-
-    input_thread_t * p_input = playlist_CurrentInput( p_playlist );
-
-    if( p_input )
-    {
-        var_Get( p_input, "state", &val );
-        vlc_object_release( p_input );
-
-        if( ( val.i_int == PAUSE_S ) &&
-            ( strcmp( newval.psz_string, "select" ) != 0 ) )
-        {
-            msg_rc( "%s", _("Type 'menu select' or 'pause' to continue.") );
-            return VLC_EGENERIC;
-        }
+        msg_rc( "+----[ end of %s ]", cmd );
     }
-
-    val.psz_string = strdup( newval.psz_string );
-    if( !val.psz_string )
-        return VLC_ENOMEM;
-    if( !strcmp( val.psz_string, "on" ) || !strcmp( val.psz_string, "show" ))
-        osd_MenuShow( p_this );
-    else if( !strcmp( val.psz_string, "off" )
-          || !strcmp( val.psz_string, "hide" ) )
-        osd_MenuHide( p_this );
-    else if( !strcmp( val.psz_string, "up" ) )
-        osd_MenuUp( p_this );
-    else if( !strcmp( val.psz_string, "down" ) )
-        osd_MenuDown( p_this );
-    else if( !strcmp( val.psz_string, "left" ) )
-        osd_MenuPrev( p_this );
-    else if( !strcmp( val.psz_string, "right" ) )
-        osd_MenuNext( p_this );
-    else if( !strcmp( val.psz_string, "select" ) )
-        osd_MenuActivate( p_this );
     else
-    {
-        msg_rc( "%s", _("Please provide one of the following parameters:") );
-        msg_rc( "[on|off|up|down|left|right|select]" );
-        i_error = VLC_EGENERIC;
-    }
-
-    free( val.psz_string );
-    return i_error;
+        ret = var_SetInteger( p_aout, "stereo-mode", atoi( cur.psz_string ) );
+out:
+    vlc_object_release( p_aout );
+    (void) old; (void) dummy;
+    return ret;
 }
 
 static int Statistics ( vlc_object_t *p_this, char const *psz_cmd,
     vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
-    VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
+    VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
     intf_thread_t *p_intf = (intf_thread_t*)p_this;
     input_thread_t *p_input =
         playlist_CurrentInput( p_intf->p_sys->p_playlist );
@@ -1879,20 +1795,7 @@ static int Statistics ( vlc_object_t *p_this, char const *psz_cmd,
     if( !p_input )
         return VLC_ENOOBJ;
 
-    if( !strcmp( psz_cmd, "stats" ) )
-    {
-        vlc_mutex_lock( &input_GetItem(p_input)->lock );
-        updateStatistics( p_intf, input_GetItem(p_input) );
-        vlc_mutex_unlock( &input_GetItem(p_input)->lock );
-    }
-    /*
-     * sanity check
-     */
-    else
-    {
-        msg_rc("%s", _("Unknown command!") );
-    }
-
+    updateStatistics( p_intf, input_GetItem(p_input) );
     vlc_object_release( p_input );
     return VLC_SUCCESS;
 }
@@ -1901,54 +1804,61 @@ static int updateStatistics( intf_thread_t *p_intf, input_item_t *p_item )
 {
     if( !p_item ) return VLC_EGENERIC;
 
+    vlc_mutex_lock( &p_item->lock );
     vlc_mutex_lock( &p_item->p_stats->lock );
     msg_rc( "+----[ begin of statistical info ]" );
 
     /* Input */
     msg_rc("%s", _("+-[Incoming]"));
-    msg_rc(_("| input bytes read : %8.0f kB"),
-            (float)(p_item->p_stats->i_read_bytes)/1000 );
+    msg_rc(_("| input bytes read : %8.0f KiB"),
+            (float)(p_item->p_stats->i_read_bytes)/1024 );
     msg_rc(_("| input bitrate    :   %6.0f kb/s"),
             (float)(p_item->p_stats->f_input_bitrate)*8000 );
-    msg_rc(_("| demux bytes read : %8.0f kB"),
-            (float)(p_item->p_stats->i_demux_read_bytes)/1000 );
+    msg_rc(_("| demux bytes read : %8.0f KiB"),
+            (float)(p_item->p_stats->i_demux_read_bytes)/1024 );
     msg_rc(_("| demux bitrate    :   %6.0f kb/s"),
             (float)(p_item->p_stats->f_demux_bitrate)*8000 );
+    msg_rc(_("| demux corrupted  :    %5"PRIi64),
+            p_item->p_stats->i_demux_corrupted );
+    msg_rc(_("| discontinuities  :    %5"PRIi64),
+            p_item->p_stats->i_demux_discontinuity );
     msg_rc("|");
     /* Video */
     msg_rc("%s", _("+-[Video Decoding]"));
-    msg_rc(_("| video decoded    :    %5i"),
+    msg_rc(_("| video decoded    :    %5"PRIi64),
             p_item->p_stats->i_decoded_video );
-    msg_rc(_("| frames displayed :    %5i"),
+    msg_rc(_("| frames displayed :    %5"PRIi64),
             p_item->p_stats->i_displayed_pictures );
-    msg_rc(_("| frames lost      :    %5i"),
+    msg_rc(_("| frames lost      :    %5"PRIi64),
             p_item->p_stats->i_lost_pictures );
     msg_rc("|");
     /* Audio*/
     msg_rc("%s", _("+-[Audio Decoding]"));
-    msg_rc(_("| audio decoded    :    %5i"),
+    msg_rc(_("| audio decoded    :    %5"PRIi64),
             p_item->p_stats->i_decoded_audio );
-    msg_rc(_("| buffers played   :    %5i"),
+    msg_rc(_("| buffers played   :    %5"PRIi64),
             p_item->p_stats->i_played_abuffers );
-    msg_rc(_("| buffers lost     :    %5i"),
+    msg_rc(_("| buffers lost     :    %5"PRIi64),
             p_item->p_stats->i_lost_abuffers );
     msg_rc("|");
     /* Sout */
     msg_rc("%s", _("+-[Streaming]"));
-    msg_rc(_("| packets sent     :    %5i"), p_item->p_stats->i_sent_packets );
-    msg_rc(_("| bytes sent       : %8.0f kB"),
-            (float)(p_item->p_stats->i_sent_bytes)/1000 );
+    msg_rc(_("| packets sent     :    %5"PRIi64),
+           p_item->p_stats->i_sent_packets );
+    msg_rc(_("| bytes sent       : %8.0f KiB"),
+            (float)(p_item->p_stats->i_sent_bytes)/1024 );
     msg_rc(_("| sending bitrate  :   %6.0f kb/s"),
             (float)(p_item->p_stats->f_send_bitrate*8)*1000 );
     msg_rc("|");
     msg_rc( "+----[ end of statistical info ]" );
     vlc_mutex_unlock( &p_item->p_stats->lock );
+    vlc_mutex_unlock( &p_item->lock );
 
     return VLC_SUCCESS;
 }
 
 #ifdef WIN32
-bool ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
+static bool ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
 {
     INPUT_RECORD input_record;
     DWORD i_dw;
@@ -1957,7 +1867,7 @@ bool ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
     while( WaitForSingleObject( p_intf->p_sys->hConsoleIn,
                                 INTF_IDLE_SLEEP/1000 ) == WAIT_OBJECT_0 )
     {
-        while( vlc_object_alive( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
+        while( *pi_size < MAX_LINE_LENGTH &&
                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
                                  1, &i_dw ) )
         {
@@ -2027,7 +1937,7 @@ bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
     }
 #endif
 
-    while( vlc_object_alive( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
+    while( *pi_size < MAX_LINE_LENGTH &&
            (i_read = net_Read( p_intf, p_intf->p_sys->i_socket == -1 ?
                        0 /*STDIN_FILENO*/ : p_intf->p_sys->i_socket, NULL,
                   (uint8_t *)p_buffer + *pi_size, 1, false ) ) > 0 )
@@ -2074,20 +1984,22 @@ bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
  * We don't check for '"' or '\'', we just assume that a ':' that follows a
  * space is a new option. Should be good enough for our purpose.
  *****************************************************************************/
-static input_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
+static input_item_t *parse_MRL( const char *mrl )
 {
 #define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; }
 #define SKIPTRAILINGSPACE( p, d ) \
     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
 
     input_item_t *p_item = NULL;
-    char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig;
+    char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig, *psz_mrl;
     char **ppsz_options = NULL;
     int i, i_options = 0;
 
-    if( !psz_mrl ) return 0;
+    if( !mrl ) return 0;
 
-    psz_mrl = psz_orig = strdup( psz_mrl );
+    psz_mrl = psz_orig = strdup( mrl );
+    if( !psz_mrl )
+        return NULL;
     while( *psz_mrl )
     {
         SKIPSPACE( psz_mrl );
@@ -2117,11 +2029,22 @@ static input_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
 
-        if( !psz_item_mrl ) psz_item_mrl = psz_item;
+        if( !psz_item_mrl )
+        {
+            if( strstr( psz_item, "://" ) != NULL )
+                psz_item_mrl = strdup( psz_item );
+            else
+                psz_item_mrl = vlc_path2uri( psz_item, NULL );
+            if( psz_item_mrl == NULL )
+            {
+                free( psz_orig );
+                return NULL;
+            }
+        }
         else if( *psz_item )
         {
             i_options++;
-            ppsz_options = realloc( ppsz_options, i_options * sizeof(char *) );
+            ppsz_options = xrealloc( ppsz_options, i_options * sizeof(char *) );
             ppsz_options[i_options - 1] = &psz_item[1];
         }
 
@@ -2131,11 +2054,12 @@ static input_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
     /* Now create a playlist item */
     if( psz_item_mrl )
     {
-        p_item = input_item_New( p_intf, psz_item_mrl, NULL );
+        p_item = input_item_New( psz_item_mrl, NULL );
         for( i = 0; i < i_options; i++ )
         {
             input_item_AddOption( p_item, ppsz_options[i], VLC_INPUT_OPTION_TRUSTED );
         }
+        free( psz_item_mrl );
     }
 
     if( i_options ) free( ppsz_options );