]> git.sesse.net Git - vlc/blob - modules/control/rc.c
efa1893f00c281c47e0e278afc4ae03c468c8dc8
[vlc] / modules / control / rc.c
1 /*****************************************************************************
2  * rc.c : remote control stdin/stdout module for vlc
3  *****************************************************************************
4  * Copyright (C) 2004-2009 the VideoLAN team
5  * $Id$
6  *
7  * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
8  *         Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <signal.h>
35 #include <assert.h>
36 #include <math.h>
37
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_interface.h>
41 #include <vlc_input.h>
42 #include <vlc_aout.h>
43 #include <vlc_vout.h>
44 #include <vlc_playlist.h>
45 #include <vlc_keys.h>
46
47 #include <sys/types.h>
48 #include <unistd.h>
49
50
51 #include <vlc_network.h>
52 #include <vlc_url.h>
53 #include <vlc_charset.h>
54
55 #if defined(PF_UNIX) && !defined(PF_LOCAL)
56 #    define PF_LOCAL PF_UNIX
57 #endif
58
59 #if defined(AF_LOCAL) && ! defined(_WIN32)
60 #    include <sys/un.h>
61 #endif
62
63 #define MAX_LINE_LENGTH 1024
64 #define STATUS_CHANGE "status change: "
65
66 /* input_state_e from <vlc_input.h> */
67 static const char *ppsz_input_state[] = {
68     [INIT_S] = N_("Initializing"),
69     [OPENING_S] = N_("Opening"),
70     [PLAYING_S] = N_("Play"),
71     [PAUSE_S] = N_("Pause"),
72     [END_S] = N_("End"),
73     [ERROR_S] = N_("Error"),
74 };
75
76 /*****************************************************************************
77  * Local prototypes
78  *****************************************************************************/
79 static int  Activate     ( vlc_object_t * );
80 static void Deactivate   ( vlc_object_t * );
81 static void *Run         ( void * );
82
83 static void Help         ( intf_thread_t * );
84 static void RegisterCallbacks( intf_thread_t * );
85
86 static bool ReadCommand( intf_thread_t *, char *, int * );
87
88 static input_item_t *parse_MRL( const char * );
89
90 static int  Input        ( vlc_object_t *, char const *,
91                            vlc_value_t, vlc_value_t, void * );
92 static int  Playlist     ( vlc_object_t *, char const *,
93                            vlc_value_t, vlc_value_t, void * );
94 static int  Quit         ( vlc_object_t *, char const *,
95                            vlc_value_t, vlc_value_t, void * );
96 static int  Intf         ( vlc_object_t *, char const *,
97                            vlc_value_t, vlc_value_t, void * );
98 static int  Volume       ( vlc_object_t *, char const *,
99                            vlc_value_t, vlc_value_t, void * );
100 static int  VolumeMove   ( vlc_object_t *, char const *,
101                            vlc_value_t, vlc_value_t, void * );
102 static int  VideoConfig  ( vlc_object_t *, char const *,
103                            vlc_value_t, vlc_value_t, void * );
104 static int  AudioDevice  ( vlc_object_t *, char const *,
105                            vlc_value_t, vlc_value_t, void * );
106 static int  AudioChannel ( vlc_object_t *, char const *,
107                            vlc_value_t, vlc_value_t, void * );
108 static int  Statistics   ( vlc_object_t *, char const *,
109                            vlc_value_t, vlc_value_t, void * );
110
111 static int updateStatistics( intf_thread_t *, input_item_t *);
112
113 /* Status Callbacks */
114 static int VolumeChanged( vlc_object_t *, char const *,
115                           vlc_value_t, vlc_value_t, void * );
116 static int InputEvent( vlc_object_t *, char const *,
117                        vlc_value_t, vlc_value_t, void * );
118
119 struct intf_sys_t
120 {
121     int *pi_socket_listen;
122     int i_socket;
123     char *psz_unix_path;
124     vlc_thread_t thread;
125
126     /* status changes */
127     vlc_mutex_t       status_lock;
128     int               i_last_state;
129     playlist_t        *p_playlist;
130     input_thread_t    *p_input;
131     bool              b_input_buffering;
132
133 #ifdef _WIN32
134     HANDLE hConsoleIn;
135     bool b_quiet;
136 #endif
137 };
138
139 VLC_FORMAT(2, 3)
140 static void msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
141 {
142     va_list args;
143     char fmt_eol[strlen (psz_fmt) + 3];
144
145     snprintf (fmt_eol, sizeof (fmt_eol), "%s\r\n", psz_fmt);
146     va_start( args, psz_fmt );
147
148     if( p_intf->p_sys->i_socket == -1 )
149         utf8_vfprintf( stdout, fmt_eol, args );
150     else
151         net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, fmt_eol, args );
152     va_end( args );
153 }
154 #define msg_rc( ... ) msg_rc( p_intf, __VA_ARGS__ )
155
156 /*****************************************************************************
157  * Module descriptor
158  *****************************************************************************/
159 #define POS_TEXT N_("Show stream position")
160 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
161                         "stream from time to time." )
162
163 #define TTY_TEXT N_("Fake TTY")
164 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
165
166 #define UNIX_TEXT N_("UNIX socket command input")
167 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
168                          "stdin." )
169
170 #define HOST_TEXT N_("TCP command input")
171 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
172             "You can set the address and port the interface will bind to." )
173
174 #ifdef _WIN32
175 #define QUIET_TEXT N_("Do not open a DOS command box interface")
176 #define QUIET_LONGTEXT N_( \
177     "By default the rc interface plugin will start a DOS command box. " \
178     "Enabling the quiet mode will not bring this command box but can also " \
179     "be pretty annoying when you want to stop VLC and no video window is " \
180     "open." )
181 #include "intromsg.h"
182 #endif
183
184 vlc_module_begin ()
185     set_shortname( N_("RC"))
186     set_category( CAT_INTERFACE )
187     set_subcategory( SUBCAT_INTERFACE_MAIN )
188     set_description( N_("Remote control interface") )
189     add_bool( "rc-show-pos", false, POS_TEXT, POS_LONGTEXT, true )
190
191 #ifdef _WIN32
192     add_bool( "rc-quiet", false, QUIET_TEXT, QUIET_LONGTEXT, false )
193 #else
194 #if defined (HAVE_ISATTY)
195     add_bool( "rc-fake-tty", false, TTY_TEXT, TTY_LONGTEXT, true )
196 #endif
197     add_string( "rc-unix", NULL, UNIX_TEXT, UNIX_LONGTEXT, true )
198 #endif
199     add_string( "rc-host", NULL, HOST_TEXT, HOST_LONGTEXT, true )
200
201     set_capability( "interface", 20 )
202
203     set_callbacks( Activate, Deactivate )
204 #ifdef _WIN32
205     add_shortcut( "rc" )
206 #endif
207 vlc_module_end ()
208
209 /*****************************************************************************
210  * Activate: initialize and create stuff
211  *****************************************************************************/
212 static int Activate( vlc_object_t *p_this )
213 {
214     /* FIXME: This function is full of memory leaks and bugs in error paths. */
215     intf_thread_t *p_intf = (intf_thread_t*)p_this;
216     playlist_t *p_playlist = pl_Get( p_intf );
217     char *psz_host, *psz_unix_path = NULL;
218     int  *pi_socket = NULL;
219
220 #ifndef _WIN32
221 #if defined(HAVE_ISATTY)
222     /* Check that stdin is a TTY */
223     if( !var_InheritBool( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
224     {
225         msg_Warn( p_intf, "fd 0 is not a TTY" );
226         return VLC_EGENERIC;
227     }
228 #endif
229
230     psz_unix_path = var_InheritString( p_intf, "rc-unix" );
231     if( psz_unix_path )
232     {
233         int i_socket;
234
235 #ifndef AF_LOCAL
236         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
237         free( psz_unix_path );
238         return VLC_EGENERIC;
239 #else
240         struct sockaddr_un addr;
241
242         memset( &addr, 0, sizeof(struct sockaddr_un) );
243
244         msg_Dbg( p_intf, "trying UNIX socket" );
245
246         if( (i_socket = vlc_socket( PF_LOCAL, SOCK_STREAM, 0, false ) ) < 0 )
247         {
248             msg_Warn( p_intf, "can't open socket: %s", vlc_strerror_c(errno) );
249             free( psz_unix_path );
250             return VLC_EGENERIC;
251         }
252
253         addr.sun_family = AF_LOCAL;
254         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
255         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
256
257         if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))
258          && (errno == EADDRINUSE)
259          && connect (i_socket, (struct sockaddr *)&addr, sizeof (addr))
260          && (errno == ECONNREFUSED))
261         {
262             msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path);
263             unlink (psz_unix_path);
264
265             if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)))
266             {
267                 msg_Err (p_intf, "cannot bind UNIX socket at %s: %s",
268                          psz_unix_path, vlc_strerror_c(errno));
269                 free (psz_unix_path);
270                 net_Close (i_socket);
271                 return VLC_EGENERIC;
272             }
273         }
274
275         if( listen( i_socket, 1 ) )
276         {
277             msg_Warn (p_intf, "can't listen on socket: %s",
278                       vlc_strerror_c(errno));
279             free( psz_unix_path );
280             net_Close( i_socket );
281             return VLC_EGENERIC;
282         }
283
284         /* FIXME: we need a core function to merge listening sockets sets */
285         pi_socket = calloc( 2, sizeof( int ) );
286         if( pi_socket == NULL )
287         {
288             free( psz_unix_path );
289             net_Close( i_socket );
290             return VLC_ENOMEM;
291         }
292         pi_socket[0] = i_socket;
293         pi_socket[1] = -1;
294 #endif /* AF_LOCAL */
295     }
296 #endif /* !_WIN32 */
297
298     if( ( pi_socket == NULL ) &&
299         ( psz_host = var_InheritString( p_intf, "rc-host" ) ) != NULL )
300     {
301         vlc_url_t url;
302
303         vlc_UrlParse( &url, psz_host, 0 );
304
305         msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port );
306
307         pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
308         if( pi_socket == NULL )
309         {
310             msg_Warn( p_intf, "can't listen to %s port %i",
311                       url.psz_host, url.i_port );
312             vlc_UrlClean( &url );
313             free( psz_host );
314             return VLC_EGENERIC;
315         }
316
317         vlc_UrlClean( &url );
318         free( psz_host );
319     }
320
321     intf_sys_t *p_sys = malloc( sizeof( *p_sys ) );
322     if( unlikely(p_sys == NULL) )
323     {
324         net_ListenClose( pi_socket );
325         free( psz_unix_path );
326         return VLC_ENOMEM;
327     }
328
329     p_intf->p_sys = p_sys;
330     p_sys->pi_socket_listen = pi_socket;
331     p_sys->i_socket = -1;
332     p_sys->psz_unix_path = psz_unix_path;
333     vlc_mutex_init( &p_sys->status_lock );
334     p_sys->i_last_state = PLAYLIST_STOPPED;
335     p_sys->b_input_buffering = false;
336     p_sys->p_playlist = p_playlist;
337     p_sys->p_input = NULL;
338
339     /* Non-buffered stdout */
340     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
341
342 #ifdef _WIN32
343     p_sys->b_quiet = var_InheritBool( p_intf, "rc-quiet" );
344 # if !VLC_WINSTORE_APP
345     if( !p_sys->b_quiet )
346         intf_consoleIntroMsg( p_intf );
347 # endif
348 #endif
349
350     if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
351         abort();
352
353     msg_rc( "%s", _("Remote control interface initialized. Type `help' for help.") );
354
355     /* Listen to audio volume updates */
356     var_AddCallback( p_sys->p_playlist, "volume", VolumeChanged, p_intf );
357     return VLC_SUCCESS;
358 }
359
360 /*****************************************************************************
361  * Deactivate: uninitialize and cleanup
362  *****************************************************************************/
363 static void Deactivate( vlc_object_t *p_this )
364 {
365     intf_thread_t *p_intf = (intf_thread_t*)p_this;
366     intf_sys_t *p_sys = p_intf->p_sys;
367
368     vlc_cancel( p_sys->thread );
369     var_DelCallback( p_sys->p_playlist, "volume", VolumeChanged, p_intf );
370     vlc_join( p_sys->thread, NULL );
371
372     if( p_sys->p_input != NULL )
373     {
374         var_DelCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
375         vlc_object_release( p_sys->p_input );
376     }
377
378     net_ListenClose( p_sys->pi_socket_listen );
379     if( p_sys->i_socket != -1 )
380         net_Close( p_sys->i_socket );
381     if( p_sys->psz_unix_path != NULL )
382     {
383 #if defined(AF_LOCAL) && !defined(_WIN32)
384         unlink( p_sys->psz_unix_path );
385 #endif
386         free( p_sys->psz_unix_path );
387     }
388     vlc_mutex_destroy( &p_sys->status_lock );
389     free( p_sys );
390 }
391
392 /*****************************************************************************
393  * RegisterCallbacks: Register callbacks to dynamic variables
394  *****************************************************************************/
395 static void RegisterCallbacks( intf_thread_t *p_intf )
396 {
397     /* Register commands that will be cleaned up upon object destruction */
398 #define ADD( name, type, target )                                   \
399     var_Create( p_intf, name, VLC_VAR_ ## type | VLC_VAR_ISCOMMAND ); \
400     var_AddCallback( p_intf, name, target, NULL );
401     ADD( "quit", VOID, Quit )
402     ADD( "intf", STRING, Intf )
403
404     ADD( "add", STRING, Playlist )
405     ADD( "repeat", STRING, Playlist )
406     ADD( "loop", STRING, Playlist )
407     ADD( "random", STRING, Playlist )
408     ADD( "enqueue", STRING, Playlist )
409     ADD( "playlist", VOID, Playlist )
410     ADD( "sort", VOID, Playlist )
411     ADD( "play", VOID, Playlist )
412     ADD( "stop", VOID, Playlist )
413     ADD( "clear", VOID, Playlist )
414     ADD( "prev", VOID, Playlist )
415     ADD( "next", VOID, Playlist )
416     ADD( "goto", INTEGER, Playlist )
417     ADD( "status", INTEGER, Playlist )
418
419     /* DVD commands */
420     ADD( "pause", VOID, Input )
421     ADD( "seek", INTEGER, Input )
422     ADD( "title", STRING, Input )
423     ADD( "title_n", VOID, Input )
424     ADD( "title_p", VOID, Input )
425     ADD( "chapter", STRING, Input )
426     ADD( "chapter_n", VOID, Input )
427     ADD( "chapter_p", VOID, Input )
428
429     ADD( "fastforward", VOID, Input )
430     ADD( "rewind", VOID, Input )
431     ADD( "faster", VOID, Input )
432     ADD( "slower", VOID, Input )
433     ADD( "normal", VOID, Input )
434     ADD( "frame", VOID, Input )
435
436     ADD( "atrack", STRING, Input )
437     ADD( "vtrack", STRING, Input )
438     ADD( "strack", STRING, Input )
439
440     /* video commands */
441     ADD( "vratio", STRING, VideoConfig )
442     ADD( "vcrop", STRING, VideoConfig )
443     ADD( "vzoom", STRING, VideoConfig )
444     ADD( "snapshot", VOID, VideoConfig )
445
446     /* audio commands */
447     ADD( "volume", STRING, Volume )
448     ADD( "volup", STRING, VolumeMove )
449     ADD( "voldown", STRING, VolumeMove )
450     ADD( "adev", STRING, AudioDevice )
451     ADD( "achan", STRING, AudioChannel )
452
453     /* misc menu commands */
454     ADD( "stats", BOOL, Statistics )
455
456 #undef ADD
457 }
458
459 /*****************************************************************************
460  * Run: rc thread
461  *****************************************************************************
462  * This part of the interface is in a separate thread so that we can call
463  * exec() from within it without annoying the rest of the program.
464  *****************************************************************************/
465 static void *Run( void *data )
466 {
467     intf_thread_t *p_intf = data;
468     intf_sys_t *p_sys = p_intf->p_sys;
469
470     char p_buffer[ MAX_LINE_LENGTH + 1 ];
471     bool b_showpos = var_InheritBool( p_intf, "rc-show-pos" );
472
473     int  i_size = 0;
474     int  i_oldpos = 0;
475     int  i_newpos;
476     int  canc = vlc_savecancel( );
477
478     p_buffer[0] = 0;
479
480 #ifdef _WIN32
481     /* Get the file descriptor of the console input */
482     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
483     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
484     {
485         msg_Err( p_intf, "couldn't find user input handle" );
486         return NULL;
487     }
488 #endif
489
490     /* Register commands that will be cleaned up upon object destruction */
491     RegisterCallbacks( p_intf );
492
493     /* status callbacks */
494
495     for( ;; )
496     {
497         char *psz_cmd, *psz_arg;
498         bool b_complete;
499
500         vlc_restorecancel( canc );
501
502         if( p_sys->pi_socket_listen != NULL && p_sys->i_socket == -1 )
503         {
504             p_sys->i_socket =
505                 net_Accept( p_intf, p_sys->pi_socket_listen );
506             if( p_sys->i_socket == -1 ) continue;
507         }
508
509         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
510         canc = vlc_savecancel( );
511
512         /* Manage the input part */
513         if( p_sys->p_input == NULL )
514         {
515             p_sys->p_input = playlist_CurrentInput( p_sys->p_playlist );
516             /* New input has been registered */
517             if( p_sys->p_input )
518             {
519                 char *psz_uri = input_item_GetURI( input_GetItem( p_sys->p_input ) );
520                 msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
521                 free( psz_uri );
522
523                 var_AddCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
524             }
525         }
526 #warning This is not reliable...
527         else if( p_sys->p_input->b_dead )
528         {
529             var_DelCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
530             vlc_object_release( p_sys->p_input );
531             p_sys->p_input = NULL;
532
533             p_sys->i_last_state = PLAYLIST_STOPPED;
534             msg_rc( STATUS_CHANGE "( stop state: 0 )" );
535         }
536
537         if( p_sys->p_input != NULL )
538         {
539             playlist_t *p_playlist = p_sys->p_playlist;
540
541             PL_LOCK;
542             int status = playlist_Status( p_playlist );
543             PL_UNLOCK;
544
545             if( p_sys->i_last_state != status )
546             {
547                 if( status == PLAYLIST_STOPPED )
548                 {
549                     p_sys->i_last_state = PLAYLIST_STOPPED;
550                     msg_rc( STATUS_CHANGE "( stop state: 5 )" );
551                 }
552                 else if( status == PLAYLIST_RUNNING )
553                 {
554                     p_sys->i_last_state = PLAYLIST_RUNNING;
555                     msg_rc( STATUS_CHANGE "( play state: 3 )" );
556                 }
557                 else if( status == PLAYLIST_PAUSED )
558                 {
559                     p_sys->i_last_state = PLAYLIST_PAUSED;
560                     msg_rc( STATUS_CHANGE "( pause state: 4 )" );
561                 }
562             }
563         }
564
565         if( p_sys->p_input && b_showpos )
566         {
567             i_newpos = 100 * var_GetFloat( p_sys->p_input, "position" );
568             if( i_oldpos != i_newpos )
569             {
570                 i_oldpos = i_newpos;
571                 msg_rc( "pos: %d%%", i_newpos );
572             }
573         }
574
575         /* Is there something to do? */
576         if( !b_complete ) continue;
577
578         /* Skip heading spaces */
579         psz_cmd = p_buffer;
580         while( *psz_cmd == ' ' )
581         {
582             psz_cmd++;
583         }
584
585         /* Split psz_cmd at the first space and make sure that
586          * psz_arg is valid */
587         psz_arg = strchr( psz_cmd, ' ' );
588         if( psz_arg )
589         {
590             *psz_arg++ = 0;
591             while( *psz_arg == ' ' )
592             {
593                 psz_arg++;
594             }
595         }
596         else
597         {
598             psz_arg = (char*)"";
599         }
600
601         /* If the user typed a registered local command, try it */
602         if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
603         {
604             vlc_value_t val;
605             int i_ret;
606             val.psz_string = psz_arg;
607
608             if ((var_Type( p_intf, psz_cmd) & VLC_VAR_CLASS) == VLC_VAR_VOID)
609                 i_ret = var_TriggerCallback( p_intf, psz_cmd );
610             else
611                 i_ret = var_Set( p_intf, psz_cmd, val );
612             msg_rc( "%s: returned %i (%s)",
613                     psz_cmd, i_ret, vlc_error( i_ret ) );
614         }
615         /* Or maybe it's a global command */
616         else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
617         {
618             vlc_value_t val;
619             int i_ret;
620
621             val.psz_string = psz_arg;
622             /* FIXME: it's a global command, but we should pass the
623              * local object as an argument, not p_intf->p_libvlc. */
624             if ((var_Type( p_intf->p_libvlc, psz_cmd) & VLC_VAR_CLASS) == VLC_VAR_VOID)
625                 i_ret = var_TriggerCallback( p_intf, psz_cmd );
626             else
627                 i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
628             if( i_ret != 0 )
629             {
630                 msg_rc( "%s: returned %i (%s)",
631                          psz_cmd, i_ret, vlc_error( i_ret ) );
632             }
633         }
634         else if( !strcmp( psz_cmd, "logout" ) )
635         {
636             /* Close connection */
637             if( p_sys->i_socket != -1 )
638             {
639                 net_Close( p_sys->i_socket );
640                 p_sys->i_socket = -1;
641             }
642         }
643         else if( !strcmp( psz_cmd, "info" ) )
644         {
645             if( p_sys->p_input )
646             {
647                 int i, j;
648                 vlc_mutex_lock( &input_GetItem(p_sys->p_input)->lock );
649                 for ( i = 0; i < input_GetItem(p_sys->p_input)->i_categories; i++ )
650                 {
651                     info_category_t *p_category = input_GetItem(p_sys->p_input)
652                                                         ->pp_categories[i];
653
654                     msg_rc( "+----[ %s ]", p_category->psz_name );
655                     msg_rc( "| " );
656                     for ( j = 0; j < p_category->i_infos; j++ )
657                     {
658                         info_t *p_info = p_category->pp_infos[j];
659                         msg_rc( "| %s: %s", p_info->psz_name,
660                                 p_info->psz_value );
661                     }
662                     msg_rc( "| " );
663                 }
664                 msg_rc( "+----[ end of stream info ]" );
665                 vlc_mutex_unlock( &input_GetItem(p_sys->p_input)->lock );
666             }
667             else
668             {
669                 msg_rc( "no input" );
670             }
671         }
672         else if( !strcmp( psz_cmd, "is_playing" ) )
673         {
674             if( p_sys->p_input == NULL )
675             {
676                 msg_rc( "0" );
677             }
678             else
679             {
680                 msg_rc( "1" );
681             }
682         }
683         else if( !strcmp( psz_cmd, "get_time" ) )
684         {
685             if( p_sys->p_input == NULL )
686             {
687                 msg_rc("0");
688             }
689             else
690             {
691                 vlc_value_t time;
692                 var_Get( p_sys->p_input, "time", &time );
693                 msg_rc( "%"PRIu64, time.i_time / 1000000);
694             }
695         }
696         else if( !strcmp( psz_cmd, "get_length" ) )
697         {
698             if( p_sys->p_input == NULL )
699             {
700                 msg_rc("0");
701             }
702             else
703             {
704                 vlc_value_t time;
705                 var_Get( p_sys->p_input, "length", &time );
706                 msg_rc( "%"PRIu64, time.i_time / 1000000);
707             }
708         }
709         else if( !strcmp( psz_cmd, "get_title" ) )
710         {
711             if( p_sys->p_input == NULL )
712             {
713                 msg_rc("%s", "");
714             }
715             else
716             {
717                 msg_rc( "%s", input_GetItem(p_sys->p_input)->psz_name );
718             }
719         }
720         else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
721                  || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) )
722         {
723             Help( p_intf );
724         }
725         else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) )
726         {
727             var_SetInteger( p_intf->p_libvlc, "key-action",
728                             vlc_GetActionId( psz_arg ) );
729         }
730         else switch( psz_cmd[0] )
731         {
732         case 'f':
733         case 'F':
734         {
735             bool fs;
736
737             if( !strncasecmp( psz_arg, "on", 2 ) )
738                 var_SetBool( p_sys->p_playlist, "fullscreen", fs = true );
739             else if( !strncasecmp( psz_arg, "off", 3 ) )
740                 var_SetBool( p_sys->p_playlist, "fullscreen", fs = false );
741             else
742                 fs = var_ToggleBool( p_sys->p_playlist, "fullscreen" );
743
744             if( p_sys->p_input != NULL )
745             {
746                 vout_thread_t *p_vout = input_GetVout( p_sys->p_input );
747                 if( p_vout )
748                 {
749                     var_SetBool( p_vout, "fullscreen", fs );
750                     vlc_object_release( p_vout );
751                 }
752             }
753             break;
754         }
755         case 's':
756         case 'S':
757             ;
758             break;
759
760         case '\0':
761             /* Ignore empty lines */
762             break;
763
764         default:
765             msg_rc(_("Unknown command `%s'. Type `help' for help."), psz_cmd);
766             break;
767         }
768
769         /* Command processed */
770         i_size = 0; p_buffer[0] = 0;
771     }
772
773     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
774     msg_rc( STATUS_CHANGE "( quit )" );
775
776     vlc_restorecancel( canc );
777
778     return NULL;
779 }
780
781 static void Help( intf_thread_t *p_intf)
782 {
783     msg_rc("%s", _("+----[ Remote control commands ]"));
784     msg_rc(  "| ");
785     msg_rc("%s", _("| add XYZ  . . . . . . . . . . . . add XYZ to playlist"));
786     msg_rc("%s", _("| enqueue XYZ  . . . . . . . . . queue XYZ to playlist"));
787     msg_rc("%s", _("| playlist . . . . .  show items currently in playlist"));
788     msg_rc("%s", _("| play . . . . . . . . . . . . . . . . . . play stream"));
789     msg_rc("%s", _("| stop . . . . . . . . . . . . . . . . . . stop stream"));
790     msg_rc("%s", _("| next . . . . . . . . . . . . . .  next playlist item"));
791     msg_rc("%s", _("| prev . . . . . . . . . . . .  previous playlist item"));
792     msg_rc("%s", _("| goto . . . . . . . . . . . . . .  goto item at index"));
793     msg_rc("%s", _("| repeat [on|off] . . . .  toggle playlist item repeat"));
794     msg_rc("%s", _("| loop [on|off] . . . . . . . . . toggle playlist loop"));
795     msg_rc("%s", _("| random [on|off] . . . . . . .  toggle random jumping"));
796     msg_rc("%s", _("| clear . . . . . . . . . . . . . . clear the playlist"));
797     msg_rc("%s", _("| status . . . . . . . . . . . current playlist status"));
798     msg_rc("%s", _("| title [X]  . . . . . . set/get title in current item"));
799     msg_rc("%s", _("| title_n  . . . . . . . .  next title in current item"));
800     msg_rc("%s", _("| title_p  . . . . . .  previous title in current item"));
801     msg_rc("%s", _("| chapter [X]  . . . . set/get chapter in current item"));
802     msg_rc("%s", _("| chapter_n  . . . . . .  next chapter in current item"));
803     msg_rc("%s", _("| chapter_p  . . . .  previous chapter in current item"));
804     msg_rc(  "| ");
805     msg_rc("%s", _("| seek X . . . seek in seconds, for instance `seek 12'"));
806     msg_rc("%s", _("| pause  . . . . . . . . . . . . . . . .  toggle pause"));
807     msg_rc("%s", _("| fastforward  . . . . . . . .  .  set to maximum rate"));
808     msg_rc("%s", _("| rewind  . . . . . . . . . . . .  set to minimum rate"));
809     msg_rc("%s", _("| faster . . . . . . . . . .  faster playing of stream"));
810     msg_rc("%s", _("| slower . . . . . . . . . .  slower playing of stream"));
811     msg_rc("%s", _("| normal . . . . . . . . . .  normal playing of stream"));
812     msg_rc("%s", _("| frame. . . . . . . . . .  play frame by frame"));
813     msg_rc("%s", _("| f [on|off] . . . . . . . . . . . . toggle fullscreen"));
814     msg_rc("%s", _("| info . . . . .  information about the current stream"));
815     msg_rc("%s", _("| stats  . . . . . . . .  show statistical information"));
816     msg_rc("%s", _("| get_time . . seconds elapsed since stream's beginning"));
817     msg_rc("%s", _("| is_playing . . . .  1 if a stream plays, 0 otherwise"));
818     msg_rc("%s", _("| get_title . . . . .  the title of the current stream"));
819     msg_rc("%s", _("| get_length . . . .  the length of the current stream"));
820     msg_rc(  "| ");
821     msg_rc("%s", _("| volume [X] . . . . . . . . . .  set/get audio volume"));
822     msg_rc("%s", _("| volup [X]  . . . . . . .  raise audio volume X steps"));
823     msg_rc("%s", _("| voldown [X]  . . . . . .  lower audio volume X steps"));
824     msg_rc("%s", _("| adev [device]  . . . . . . . .  set/get audio device"));
825     msg_rc("%s", _("| achan [X]. . . . . . . . . .  set/get audio channels"));
826     msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track"));
827     msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track"));
828     msg_rc("%s", _("| vratio [X]  . . . . . . . set/get video aspect ratio"));
829     msg_rc("%s", _("| vcrop [X]  . . . . . . . . . . .  set/get video crop"));
830     msg_rc("%s", _("| vzoom [X]  . . . . . . . . . . .  set/get video zoom"));
831     msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot"));
832     msg_rc("%s", _("| strack [X] . . . . . . . . .  set/get subtitle track"));
833     msg_rc("%s", _("| key [hotkey name] . . . . . .  simulate hotkey press"));
834     msg_rc(  "| ");
835     msg_rc("%s", _("| help . . . . . . . . . . . . . . . this help message"));
836     msg_rc("%s", _("| logout . . . . . . .  exit (if in socket connection)"));
837     msg_rc("%s", _("| quit . . . . . . . . . . . . . . . . . . .  quit vlc"));
838     msg_rc(  "| ");
839     msg_rc("%s", _("+----[ end of help ]"));
840 }
841
842 /********************************************************************
843  * Status callback routines
844  ********************************************************************/
845 static int VolumeChanged( vlc_object_t *p_this, char const *psz_cmd,
846     vlc_value_t oldval, vlc_value_t newval, void *p_data )
847 {
848     (void) p_this;
849     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval);
850     intf_thread_t *p_intf = (intf_thread_t*)p_data;
851
852     vlc_mutex_lock( &p_intf->p_sys->status_lock );
853     msg_rc( STATUS_CHANGE "( audio volume: %ld )",
854             lroundf(newval.f_float * AOUT_VOLUME_DEFAULT) );
855     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
856     return VLC_SUCCESS;
857 }
858
859 static void StateChanged( intf_thread_t *p_intf, input_thread_t *p_input )
860 {
861     playlist_t *p_playlist = p_intf->p_sys->p_playlist;
862
863     PL_LOCK;
864     const int i_status = playlist_Status( p_playlist );
865     PL_UNLOCK;
866
867     /* */
868     const char *psz_cmd;
869     switch( i_status )
870     {
871     case PLAYLIST_STOPPED:
872         psz_cmd = "stop";
873         break;
874     case PLAYLIST_RUNNING:
875         psz_cmd = "play";
876         break;
877     case PLAYLIST_PAUSED:
878         psz_cmd = "pause";
879         break;
880     default:
881         psz_cmd = "";
882         break;
883     }
884
885     /* */
886     const int i_state = var_GetInteger( p_input, "state" );
887
888     vlc_mutex_lock( &p_intf->p_sys->status_lock );
889     msg_rc( STATUS_CHANGE "( %s state: %d ): %s", psz_cmd,
890             i_state, ppsz_input_state[i_state] );
891     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
892 }
893 static void RateChanged( intf_thread_t *p_intf,
894                          input_thread_t *p_input )
895 {
896     vlc_mutex_lock( &p_intf->p_sys->status_lock );
897     msg_rc( STATUS_CHANGE "( new rate: %.3f )",
898             var_GetFloat( p_input, "rate" ) );
899     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
900 }
901 static void PositionChanged( intf_thread_t *p_intf,
902                              input_thread_t *p_input )
903 {
904     vlc_mutex_lock( &p_intf->p_sys->status_lock );
905     if( p_intf->p_sys->b_input_buffering )
906         msg_rc( STATUS_CHANGE "( time: %"PRId64"s )",
907                 (var_GetTime( p_input, "time" )/1000000) );
908     p_intf->p_sys->b_input_buffering = false;
909     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
910 }
911 static void CacheChanged( intf_thread_t *p_intf )
912 {
913     vlc_mutex_lock( &p_intf->p_sys->status_lock );
914     p_intf->p_sys->b_input_buffering = true;
915     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
916 }
917
918 static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
919                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
920 {
921     VLC_UNUSED(psz_cmd);
922     VLC_UNUSED(oldval);
923     input_thread_t *p_input = (input_thread_t*)p_this;
924     intf_thread_t *p_intf = p_data;
925
926     switch( newval.i_int )
927     {
928     case INPUT_EVENT_STATE:
929     case INPUT_EVENT_DEAD:
930         StateChanged( p_intf, p_input );
931         break;
932     case INPUT_EVENT_RATE:
933         RateChanged( p_intf, p_input );
934         break;
935     case INPUT_EVENT_POSITION:
936         PositionChanged( p_intf, p_input );
937         break;
938     case INPUT_EVENT_CACHE:
939         CacheChanged( p_intf );
940         break;
941     default:
942         break;
943     }
944     return VLC_SUCCESS;
945 }
946
947 /********************************************************************
948  * Command routines
949  ********************************************************************/
950 static int Input( vlc_object_t *p_this, char const *psz_cmd,
951                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
952 {
953     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
954     intf_thread_t *p_intf = (intf_thread_t*)p_this;
955     input_thread_t *p_input =
956         playlist_CurrentInput( p_intf->p_sys->p_playlist );
957     int i_error = VLC_EGENERIC;
958
959     if( !p_input )
960         return VLC_ENOOBJ;
961
962     int state = var_GetInteger( p_input, "state" );
963     if( ( state == PAUSE_S ) &&
964         ( strcmp( psz_cmd, "pause" ) != 0 ) && (strcmp( psz_cmd,"frame") != 0 ) )
965     {
966         msg_rc( "%s", _("Press pause to continue.") );
967     }
968     else
969     /* Parse commands that only require an input */
970     if( !strcmp( psz_cmd, "pause" ) )
971     {
972         playlist_TogglePause( p_intf->p_sys->p_playlist );
973         i_error = VLC_SUCCESS;
974     }
975     else if( !strcmp( psz_cmd, "seek" ) )
976     {
977         if( strlen( newval.psz_string ) > 0 &&
978             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
979         {
980             float f = atof( newval.psz_string ) / 100.0;
981             var_SetFloat( p_input, "position", f );
982         }
983         else
984         {
985             mtime_t t = ((int64_t)atoi( newval.psz_string )) * CLOCK_FREQ;
986             var_SetTime( p_input, "time", t );
987         }
988         i_error = VLC_SUCCESS;
989     }
990     else if ( !strcmp( psz_cmd, "fastforward" ) )
991     {
992         if( var_GetBool( p_input, "can-rate" ) )
993         {
994             float f_rate = var_GetFloat( p_input, "rate" );
995             f_rate = (f_rate < 0) ? -f_rate : f_rate * 2;
996             var_SetFloat( p_input, "rate", f_rate );
997         }
998         else
999         {
1000             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_FORWARD_EXTRASHORT );
1001         }
1002         i_error = VLC_SUCCESS;
1003     }
1004     else if ( !strcmp( psz_cmd, "rewind" ) )
1005     {
1006         if( var_GetBool( p_input, "can-rewind" ) )
1007         {
1008             float f_rate = var_GetFloat( p_input, "rate" );
1009             f_rate = (f_rate > 0) ? -f_rate : f_rate * 2;
1010             var_SetFloat( p_input, "rate", f_rate );
1011         }
1012         else
1013         {
1014             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_BACKWARD_EXTRASHORT );
1015         }
1016         i_error = VLC_SUCCESS;
1017     }
1018     else if ( !strcmp( psz_cmd, "faster" ) )
1019     {
1020         var_TriggerCallback( p_intf->p_sys->p_playlist, "rate-faster" );
1021         i_error = VLC_SUCCESS;
1022     }
1023     else if ( !strcmp( psz_cmd, "slower" ) )
1024     {
1025         var_TriggerCallback( p_intf->p_sys->p_playlist, "rate-slower" );
1026         i_error = VLC_SUCCESS;
1027     }
1028     else if ( !strcmp( psz_cmd, "normal" ) )
1029     {
1030         var_SetFloat( p_intf->p_sys->p_playlist, "rate", 1. );
1031         i_error = VLC_SUCCESS;
1032     }
1033     else if ( !strcmp( psz_cmd, "frame" ) )
1034     {
1035         var_TriggerCallback( p_input, "frame-next" );
1036         i_error = VLC_SUCCESS;
1037     }
1038     else if( !strcmp( psz_cmd, "chapter" ) ||
1039              !strcmp( psz_cmd, "chapter_n" ) ||
1040              !strcmp( psz_cmd, "chapter_p" ) )
1041     {
1042         if( !strcmp( psz_cmd, "chapter" ) )
1043         {
1044             if ( *newval.psz_string )
1045             {
1046                 /* Set. */
1047                 var_SetInteger( p_input, "chapter", atoi( newval.psz_string ) );
1048             }
1049             else
1050             {
1051                 /* Get. */
1052                 int i_chap = var_GetInteger( p_input, "chapter" );
1053                 int i_chapter_count = var_CountChoices( p_input, "chapter" );
1054                 msg_rc( "Currently playing chapter %d/%d.", i_chap,
1055                         i_chapter_count );
1056             }
1057         }
1058         else if( !strcmp( psz_cmd, "chapter_n" ) )
1059             var_TriggerCallback( p_input, "next-chapter" );
1060         else if( !strcmp( psz_cmd, "chapter_p" ) )
1061             var_TriggerCallback( p_input, "prev-chapter" );
1062         i_error = VLC_SUCCESS;
1063     }
1064     else if( !strcmp( psz_cmd, "title" ) ||
1065              !strcmp( psz_cmd, "title_n" ) ||
1066              !strcmp( psz_cmd, "title_p" ) )
1067     {
1068         if( !strcmp( psz_cmd, "title" ) )
1069         {
1070             if ( *newval.psz_string )
1071                 /* Set. */
1072                 var_SetInteger( p_input, "title", atoi( newval.psz_string ) );
1073             else
1074             {
1075                 /* Get. */
1076                 int i_title = var_GetInteger( p_input, "title" );
1077                 int i_title_count = var_CountChoices( p_input, "title" );
1078                 msg_rc( "Currently playing title %d/%d.", i_title,
1079                         i_title_count );
1080             }
1081         }
1082         else if( !strcmp( psz_cmd, "title_n" ) )
1083             var_TriggerCallback( p_input, "next-title" );
1084         else if( !strcmp( psz_cmd, "title_p" ) )
1085             var_TriggerCallback( p_input, "prev-title" );
1086
1087         i_error = VLC_SUCCESS;
1088     }
1089     else if(    !strcmp( psz_cmd, "atrack" )
1090              || !strcmp( psz_cmd, "vtrack" )
1091              || !strcmp( psz_cmd, "strack" ) )
1092     {
1093         const char *psz_variable;
1094         vlc_value_t val_name;
1095
1096         if( !strcmp( psz_cmd, "atrack" ) )
1097         {
1098             psz_variable = "audio-es";
1099         }
1100         else if( !strcmp( psz_cmd, "vtrack" ) )
1101         {
1102             psz_variable = "video-es";
1103         }
1104         else
1105         {
1106             psz_variable = "spu-es";
1107         }
1108
1109         /* Get the descriptive name of the variable */
1110         var_Change( p_input, psz_variable, VLC_VAR_GETTEXT,
1111                      &val_name, NULL );
1112         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1113
1114         if( newval.psz_string && *newval.psz_string )
1115         {
1116             /* set */
1117             i_error = var_SetInteger( p_input, psz_variable,
1118                                       atoi( newval.psz_string ) );
1119         }
1120         else
1121         {
1122             /* get */
1123             vlc_value_t val, text;
1124             int i, i_value;
1125
1126             if ( var_Get( p_input, psz_variable, &val ) < 0 )
1127                 goto out;
1128             i_value = val.i_int;
1129
1130             if ( var_Change( p_input, psz_variable,
1131                              VLC_VAR_GETCHOICES, &val, &text ) < 0 )
1132                 goto out;
1133
1134             msg_rc( "+----[ %s ]", val_name.psz_string );
1135             for ( i = 0; i < val.p_list->i_count; i++ )
1136             {
1137                 if ( i_value == val.p_list->p_values[i].i_int )
1138                     msg_rc( "| %"PRId64" - %s *",
1139                             val.p_list->p_values[i].i_int,
1140                             text.p_list->p_values[i].psz_string );
1141                 else
1142                     msg_rc( "| %"PRId64" - %s",
1143                             val.p_list->p_values[i].i_int,
1144                             text.p_list->p_values[i].psz_string );
1145             }
1146             var_FreeList( &val, &text );
1147             msg_rc( "+----[ end of %s ]", val_name.psz_string );
1148         }
1149         free( val_name.psz_string );
1150     }
1151 out:
1152     vlc_object_release( p_input );
1153     return i_error;
1154 }
1155
1156 static void print_playlist( intf_thread_t *p_intf, playlist_item_t *p_item, int i_level )
1157 {
1158     int i;
1159     char psz_buffer[MSTRTIME_MAX_SIZE];
1160     for( i = 0; i< p_item->i_children; i++ )
1161     {
1162         if( p_item->pp_children[i]->p_input->i_duration != -1 )
1163         {
1164             secstotimestr( psz_buffer, p_item->pp_children[i]->p_input->i_duration / 1000000 );
1165             msg_rc( "|%*s- %s (%s)", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name, psz_buffer );
1166         }
1167         else
1168             msg_rc( "|%*s- %s", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name );
1169
1170         if( p_item->pp_children[i]->i_children >= 0 )
1171             print_playlist( p_intf, p_item->pp_children[i], i_level + 1 );
1172     }
1173 }
1174
1175 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
1176                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1177 {
1178     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1179     vlc_value_t val;
1180
1181     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1182     playlist_t *p_playlist = p_intf->p_sys->p_playlist;
1183     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1184
1185     if( p_input )
1186     {
1187         int state = var_GetInteger( p_input, "state" );
1188         vlc_object_release( p_input );
1189
1190         if( state == PAUSE_S )
1191         {
1192             msg_rc( "%s", _("Type 'pause' to continue.") );
1193             return VLC_EGENERIC;
1194         }
1195     }
1196
1197     /* Parse commands that require a playlist */
1198     if( !strcmp( psz_cmd, "prev" ) )
1199     {
1200         playlist_Prev( p_playlist );
1201     }
1202     else if( !strcmp( psz_cmd, "next" ) )
1203     {
1204         playlist_Next( p_playlist );
1205     }
1206     else if( !strcmp( psz_cmd, "play" ) )
1207     {
1208         msg_Warn( p_playlist, "play" );
1209         playlist_Play( p_playlist );
1210     }
1211     else if( !strcmp( psz_cmd, "repeat" ) )
1212     {
1213         bool b_update = true;
1214
1215         var_Get( p_playlist, "repeat", &val );
1216
1217         if( strlen( newval.psz_string ) > 0 )
1218         {
1219             if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
1220                  ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
1221             {
1222                 b_update = false;
1223             }
1224         }
1225
1226         if ( b_update )
1227         {
1228             val.b_bool = !val.b_bool;
1229             var_Set( p_playlist, "repeat", val );
1230         }
1231         msg_rc( "Setting repeat to %d", val.b_bool );
1232     }
1233     else if( !strcmp( psz_cmd, "loop" ) )
1234     {
1235         bool b_update = true;
1236
1237         var_Get( p_playlist, "loop", &val );
1238
1239         if( strlen( newval.psz_string ) > 0 )
1240         {
1241             if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
1242                  ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
1243             {
1244                 b_update = false;
1245             }
1246         }
1247
1248         if ( b_update )
1249         {
1250             val.b_bool = !val.b_bool;
1251             var_Set( p_playlist, "loop", val );
1252         }
1253         msg_rc( "Setting loop to %d", val.b_bool );
1254     }
1255     else if( !strcmp( psz_cmd, "random" ) )
1256     {
1257         bool b_update = true;
1258
1259         var_Get( p_playlist, "random", &val );
1260
1261         if( strlen( newval.psz_string ) > 0 )
1262         {
1263             if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
1264                  ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
1265             {
1266                 b_update = false;
1267             }
1268         }
1269
1270         if ( b_update )
1271         {
1272             val.b_bool = !val.b_bool;
1273             var_Set( p_playlist, "random", val );
1274         }
1275         msg_rc( "Setting random to %d", val.b_bool );
1276     }
1277     else if (!strcmp( psz_cmd, "goto" ) )
1278     {
1279         PL_LOCK;
1280         unsigned i_pos = atoi( newval.psz_string );
1281         unsigned i_size = p_playlist->items.i_size;
1282
1283         if( i_pos <= 0 )
1284             msg_rc( "%s", _("Error: `goto' needs an argument greater than zero.") );
1285         else if( i_pos <= i_size )
1286         {
1287             playlist_item_t *p_item, *p_parent;
1288             p_item = p_parent = p_playlist->items.p_elems[i_pos-1];
1289             while( p_parent->p_parent )
1290                 p_parent = p_parent->p_parent;
1291             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked,
1292                     p_parent, p_item );
1293         }
1294         else
1295             msg_rc( vlc_ngettext("Playlist has only %u element",
1296                                  "Playlist has only %u elements", i_size),
1297                      i_size );
1298         PL_UNLOCK;
1299     }
1300     else if( !strcmp( psz_cmd, "stop" ) )
1301     {
1302         playlist_Stop( p_playlist );
1303     }
1304     else if( !strcmp( psz_cmd, "clear" ) )
1305     {
1306         playlist_Stop( p_playlist );
1307         playlist_Clear( p_playlist, pl_Unlocked );
1308     }
1309     else if( !strcmp( psz_cmd, "add" ) &&
1310              newval.psz_string && *newval.psz_string )
1311     {
1312         input_item_t *p_item = parse_MRL( newval.psz_string );
1313
1314         if( p_item )
1315         {
1316             msg_rc( "Trying to add %s to playlist.", newval.psz_string );
1317             int i_ret =playlist_AddInput( p_playlist, p_item,
1318                      PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END, true,
1319                      pl_Unlocked );
1320             vlc_gc_decref( p_item );
1321             if( i_ret != VLC_SUCCESS )
1322             {
1323                 return VLC_EGENERIC;
1324             }
1325         }
1326     }
1327     else if( !strcmp( psz_cmd, "enqueue" ) &&
1328              newval.psz_string && *newval.psz_string )
1329     {
1330         input_item_t *p_item = parse_MRL( newval.psz_string );
1331
1332         if( p_item )
1333         {
1334             msg_rc( "trying to enqueue %s to playlist", newval.psz_string );
1335             if( playlist_AddInput( p_playlist, p_item,
1336                                PLAYLIST_APPEND, PLAYLIST_END, true,
1337                                pl_Unlocked ) != VLC_SUCCESS )
1338             {
1339                 return VLC_EGENERIC;
1340             }
1341         }
1342     }
1343     else if( !strcmp( psz_cmd, "playlist" ) )
1344     {
1345         msg_rc( "+----[ Playlist ]" );
1346         print_playlist( p_intf, p_playlist->p_root_category, 0 );
1347         msg_rc( "+----[ End of playlist ]" );
1348     }
1349
1350     else if( !strcmp( psz_cmd, "sort" ))
1351     {
1352         PL_LOCK;
1353         playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_onelevel,
1354                                     SORT_ARTIST, ORDER_NORMAL );
1355         PL_UNLOCK;
1356     }
1357     else if( !strcmp( psz_cmd, "status" ) )
1358     {
1359         input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1360         if( p_input )
1361         {
1362             /* Replay the current state of the system. */
1363             char *psz_uri =
1364                     input_item_GetURI( input_GetItem( p_input ) );
1365             vlc_object_release( p_input );
1366             if( likely(psz_uri != NULL) )
1367             {
1368                 msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
1369                 free( psz_uri );
1370             }
1371         }
1372
1373         float volume = playlist_VolumeGet( p_playlist );
1374         if( volume >= 0.f )
1375             msg_rc( STATUS_CHANGE "( audio volume: %ld )",
1376                     lroundf(volume * AOUT_VOLUME_DEFAULT) );
1377
1378         int status;
1379         PL_LOCK;
1380         status = playlist_Status(p_playlist);
1381         PL_UNLOCK;
1382         switch( status )
1383         {
1384             case PLAYLIST_STOPPED:
1385                 msg_rc( STATUS_CHANGE "( stop state: 5 )" );
1386                 break;
1387             case PLAYLIST_RUNNING:
1388                 msg_rc( STATUS_CHANGE "( play state: 3 )" );
1389                 break;
1390             case PLAYLIST_PAUSED:
1391                 msg_rc( STATUS_CHANGE "( pause state: 4 )" );
1392                 break;
1393             default:
1394                 msg_rc( STATUS_CHANGE "( unknown state: -1 )" );
1395                 break;
1396         }
1397     }
1398
1399     /*
1400      * sanity check
1401      */
1402     else
1403     {
1404         msg_rc( "unknown command!" );
1405     }
1406
1407     return VLC_SUCCESS;
1408 }
1409
1410 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
1411                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1412 {
1413     VLC_UNUSED(p_data); VLC_UNUSED(psz_cmd);
1414     VLC_UNUSED(oldval); VLC_UNUSED(newval);
1415
1416     libvlc_Quit( p_this->p_libvlc );
1417     return VLC_SUCCESS;
1418 }
1419
1420 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
1421                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1422 {
1423     intf_thread_t *intf = (intf_thread_t *)p_this;
1424
1425     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1426     return intf_Create(pl_Get(intf), newval.psz_string );
1427 }
1428
1429 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
1430                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
1431 {
1432     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1433     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1434     playlist_t *p_playlist = p_intf->p_sys->p_playlist;
1435     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
1436     int i_error = VLC_EGENERIC;
1437
1438     if( !p_input )
1439         return VLC_ENOOBJ;
1440
1441     if( p_input )
1442     {
1443         int state = var_GetInteger( p_input, "state" );
1444         vlc_object_release( p_input );
1445         if( state == PAUSE_S )
1446         {
1447             msg_rc( "%s", _("Type 'pause' to continue.") );
1448             return VLC_EGENERIC;
1449         }
1450     }
1451
1452     if ( *newval.psz_string )
1453     {
1454         /* Set. */
1455         int i_volume = atoi( newval.psz_string );
1456         if( !playlist_VolumeSet( p_playlist,
1457                              i_volume / (float)AOUT_VOLUME_DEFAULT ) )
1458             i_error = VLC_SUCCESS;
1459         playlist_MuteSet( p_playlist, i_volume == 0 );
1460         msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1461     }
1462     else
1463     {
1464         /* Get. */
1465         msg_rc( STATUS_CHANGE "( audio volume: %ld )",
1466                lroundf( playlist_VolumeGet( p_playlist ) * AOUT_VOLUME_DEFAULT ) );
1467         i_error = VLC_SUCCESS;
1468     }
1469
1470     return i_error;
1471 }
1472
1473 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
1474                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1475 {
1476     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1477     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1478     float volume;
1479     input_thread_t *p_input =
1480         playlist_CurrentInput( p_intf->p_sys->p_playlist );
1481     int i_nb_steps = atoi(newval.psz_string);
1482     int i_error = VLC_SUCCESS;
1483
1484     if( !p_input )
1485         return VLC_ENOOBJ;
1486
1487     int state = var_GetInteger( p_input, "state" );
1488     vlc_object_release( p_input );
1489     if( state == PAUSE_S )
1490     {
1491         msg_rc( "%s", _("Type 'pause' to continue.") );
1492         return VLC_EGENERIC;
1493     }
1494
1495     if( !strcmp(psz_cmd, "voldown") )
1496         i_nb_steps *= -1;
1497     if( playlist_VolumeUp( p_intf->p_sys->p_playlist, i_nb_steps, &volume ) < 0 )
1498         i_error = VLC_EGENERIC;
1499
1500     if ( !i_error )
1501         msg_rc( STATUS_CHANGE "( audio volume: %ld )",
1502                 lroundf( volume * AOUT_VOLUME_DEFAULT ) );
1503     return i_error;
1504 }
1505
1506
1507 static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
1508                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1509 {
1510     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1511     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1512     input_thread_t *p_input =
1513         playlist_CurrentInput( p_intf->p_sys->p_playlist );
1514     vout_thread_t * p_vout;
1515     const char * psz_variable = NULL;
1516     int i_error = VLC_SUCCESS;
1517
1518     if( !p_input )
1519         return VLC_ENOOBJ;
1520
1521     p_vout = input_GetVout( p_input );
1522     vlc_object_release( p_input );
1523     if( !p_vout )
1524         return VLC_ENOOBJ;
1525
1526     if( !strcmp( psz_cmd, "vcrop" ) )
1527     {
1528         psz_variable = "crop";
1529     }
1530     else if( !strcmp( psz_cmd, "vratio" ) )
1531     {
1532         psz_variable = "aspect-ratio";
1533     }
1534     else if( !strcmp( psz_cmd, "vzoom" ) )
1535     {
1536         psz_variable = "zoom";
1537     }
1538     else if( !strcmp( psz_cmd, "snapshot" ) )
1539     {
1540         psz_variable = "video-snapshot";
1541     }
1542     else
1543         /* This case can't happen */
1544         vlc_assert_unreachable();
1545
1546     if( newval.psz_string && *newval.psz_string )
1547     {
1548         /* set */
1549         if( !strcmp( psz_variable, "zoom" ) )
1550         {
1551             vlc_value_t val;
1552             val.f_float = atof( newval.psz_string );
1553             i_error = var_Set( p_vout, psz_variable, val );
1554         }
1555         else
1556         {
1557             i_error = var_Set( p_vout, psz_variable, newval );
1558         }
1559     }
1560     else if( !strcmp( psz_cmd, "snapshot" ) )
1561     {
1562         var_TriggerCallback( p_vout, psz_variable );
1563     }
1564     else
1565     {
1566         /* get */
1567         vlc_value_t val_name;
1568         vlc_value_t val, text;
1569         int i;
1570         float f_value = 0.;
1571         char *psz_value = NULL;
1572
1573         if ( var_Get( p_vout, psz_variable, &val ) < 0 )
1574         {
1575             vlc_object_release( p_vout );
1576             return VLC_EGENERIC;
1577         }
1578         if( !strcmp( psz_variable, "zoom" ) )
1579         {
1580             f_value = val.f_float;
1581         }
1582         else
1583         {
1584             psz_value = val.psz_string;
1585         }
1586
1587         if ( var_Change( p_vout, psz_variable,
1588                          VLC_VAR_GETCHOICES, &val, &text ) < 0 )
1589         {
1590             vlc_object_release( p_vout );
1591             free( psz_value );
1592             return VLC_EGENERIC;
1593         }
1594
1595         /* Get the descriptive name of the variable */
1596         var_Change( p_vout, psz_variable, VLC_VAR_GETTEXT,
1597                     &val_name, NULL );
1598         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1599
1600         msg_rc( "+----[ %s ]", val_name.psz_string );
1601         if( !strcmp( psz_variable, "zoom" ) )
1602         {
1603             for ( i = 0; i < val.p_list->i_count; i++ )
1604             {
1605                 if ( f_value == val.p_list->p_values[i].f_float )
1606                     msg_rc( "| %f - %s *", val.p_list->p_values[i].f_float,
1607                             text.p_list->p_values[i].psz_string );
1608                 else
1609                     msg_rc( "| %f - %s", val.p_list->p_values[i].f_float,
1610                             text.p_list->p_values[i].psz_string );
1611             }
1612         }
1613         else
1614         {
1615             for ( i = 0; i < val.p_list->i_count; i++ )
1616             {
1617                 if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
1618                     msg_rc( "| %s - %s *", val.p_list->p_values[i].psz_string,
1619                             text.p_list->p_values[i].psz_string );
1620                 else
1621                     msg_rc( "| %s - %s", val.p_list->p_values[i].psz_string,
1622                             text.p_list->p_values[i].psz_string );
1623             }
1624             free( psz_value );
1625         }
1626         var_FreeList( &val, &text );
1627         msg_rc( "+----[ end of %s ]", val_name.psz_string );
1628
1629         free( val_name.psz_string );
1630     }
1631     vlc_object_release( p_vout );
1632     return i_error;
1633 }
1634
1635 static int AudioDevice( vlc_object_t *obj, char const *cmd,
1636                         vlc_value_t old, vlc_value_t cur, void *dummy )
1637 {
1638     intf_thread_t *p_intf = (intf_thread_t *)obj;
1639     audio_output_t *p_aout = playlist_GetAout( pl_Get(p_intf) );
1640     if( p_aout == NULL )
1641         return VLC_ENOOBJ;
1642
1643     if( !*cur.psz_string )
1644     {
1645         char **ids, **names;
1646         int n = aout_DevicesList( p_aout, &ids, &names );
1647         if( n < 0 )
1648             goto out;
1649
1650         char *dev = aout_DeviceGet( p_aout );
1651         const char *devstr = (dev != NULL) ? dev : "";
1652
1653         msg_rc( "+----[ %s ]", cmd );
1654         for ( int i = 0; i < n; i++ )
1655         {
1656             const char *fmt = "| %s - %s";
1657
1658             if( !strcmp(devstr, ids[i]) )
1659                 fmt = "| %s - %s *";
1660             msg_rc( fmt, ids[i], names[i] );
1661             free( names[i] );
1662             free( ids[i] );
1663         }
1664         msg_rc( "+----[ end of %s ]", cmd );
1665
1666         free( dev );
1667         free( names );
1668         free( ids );
1669     }
1670     else
1671         aout_DeviceSet( p_aout, cur.psz_string );
1672 out:
1673     vlc_object_release( p_aout );
1674     (void) old; (void) dummy;
1675     return VLC_SUCCESS;
1676 }
1677
1678 static int AudioChannel( vlc_object_t *obj, char const *cmd,
1679                          vlc_value_t old, vlc_value_t cur, void *dummy )
1680 {
1681     intf_thread_t *p_intf = (intf_thread_t*)obj;
1682     vlc_object_t *p_aout = (vlc_object_t *)playlist_GetAout( pl_Get(p_intf) );
1683     if ( p_aout == NULL )
1684          return VLC_ENOOBJ;
1685
1686     int ret = VLC_SUCCESS;
1687
1688     if ( !*cur.psz_string )
1689     {
1690         /* Retrieve all registered ***. */
1691         vlc_value_t val, text;
1692         if ( var_Change( p_aout, "stereo-mode",
1693                          VLC_VAR_GETCHOICES, &val, &text ) < 0 )
1694         {
1695             ret = VLC_ENOVAR;
1696             goto out;
1697         }
1698
1699         int i_value = var_GetInteger( p_aout, "stereo-mode" );
1700
1701         msg_rc( "+----[ %s ]", cmd );
1702         for ( int i = 0; i < val.p_list->i_count; i++ )
1703         {
1704             if ( i_value == val.p_list->p_values[i].i_int )
1705                 msg_rc( "| %"PRId64" - %s *", val.p_list->p_values[i].i_int,
1706                         text.p_list->p_values[i].psz_string );
1707             else
1708                 msg_rc( "| %"PRId64" - %s", val.p_list->p_values[i].i_int,
1709                         text.p_list->p_values[i].psz_string );
1710         }
1711         var_FreeList( &val, &text );
1712         msg_rc( "+----[ end of %s ]", cmd );
1713     }
1714     else
1715         ret = var_SetInteger( p_aout, "stereo-mode", atoi( cur.psz_string ) );
1716 out:
1717     vlc_object_release( p_aout );
1718     (void) old; (void) dummy;
1719     return ret;
1720 }
1721
1722 static int Statistics ( vlc_object_t *p_this, char const *psz_cmd,
1723     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1724 {
1725     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1726     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1727     input_thread_t *p_input =
1728         playlist_CurrentInput( p_intf->p_sys->p_playlist );
1729
1730     if( !p_input )
1731         return VLC_ENOOBJ;
1732
1733     updateStatistics( p_intf, input_GetItem(p_input) );
1734     vlc_object_release( p_input );
1735     return VLC_SUCCESS;
1736 }
1737
1738 static int updateStatistics( intf_thread_t *p_intf, input_item_t *p_item )
1739 {
1740     if( !p_item ) return VLC_EGENERIC;
1741
1742     vlc_mutex_lock( &p_item->lock );
1743     vlc_mutex_lock( &p_item->p_stats->lock );
1744     msg_rc( "+----[ begin of statistical info ]" );
1745
1746     /* Input */
1747     msg_rc("%s", _("+-[Incoming]"));
1748     msg_rc(_("| input bytes read : %8.0f KiB"),
1749             (float)(p_item->p_stats->i_read_bytes)/1024 );
1750     msg_rc(_("| input bitrate    :   %6.0f kb/s"),
1751             (float)(p_item->p_stats->f_input_bitrate)*8000 );
1752     msg_rc(_("| demux bytes read : %8.0f KiB"),
1753             (float)(p_item->p_stats->i_demux_read_bytes)/1024 );
1754     msg_rc(_("| demux bitrate    :   %6.0f kb/s"),
1755             (float)(p_item->p_stats->f_demux_bitrate)*8000 );
1756     msg_rc(_("| demux corrupted  :    %5"PRIi64),
1757             p_item->p_stats->i_demux_corrupted );
1758     msg_rc(_("| discontinuities  :    %5"PRIi64),
1759             p_item->p_stats->i_demux_discontinuity );
1760     msg_rc("|");
1761     /* Video */
1762     msg_rc("%s", _("+-[Video Decoding]"));
1763     msg_rc(_("| video decoded    :    %5"PRIi64),
1764             p_item->p_stats->i_decoded_video );
1765     msg_rc(_("| frames displayed :    %5"PRIi64),
1766             p_item->p_stats->i_displayed_pictures );
1767     msg_rc(_("| frames lost      :    %5"PRIi64),
1768             p_item->p_stats->i_lost_pictures );
1769     msg_rc("|");
1770     /* Audio*/
1771     msg_rc("%s", _("+-[Audio Decoding]"));
1772     msg_rc(_("| audio decoded    :    %5"PRIi64),
1773             p_item->p_stats->i_decoded_audio );
1774     msg_rc(_("| buffers played   :    %5"PRIi64),
1775             p_item->p_stats->i_played_abuffers );
1776     msg_rc(_("| buffers lost     :    %5"PRIi64),
1777             p_item->p_stats->i_lost_abuffers );
1778     msg_rc("|");
1779     /* Sout */
1780     msg_rc("%s", _("+-[Streaming]"));
1781     msg_rc(_("| packets sent     :    %5"PRIi64),
1782            p_item->p_stats->i_sent_packets );
1783     msg_rc(_("| bytes sent       : %8.0f KiB"),
1784             (float)(p_item->p_stats->i_sent_bytes)/1024 );
1785     msg_rc(_("| sending bitrate  :   %6.0f kb/s"),
1786             (float)(p_item->p_stats->f_send_bitrate*8)*1000 );
1787     msg_rc("|");
1788     msg_rc( "+----[ end of statistical info ]" );
1789     vlc_mutex_unlock( &p_item->p_stats->lock );
1790     vlc_mutex_unlock( &p_item->lock );
1791
1792     return VLC_SUCCESS;
1793 }
1794
1795 #ifdef _WIN32
1796 static bool ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1797 {
1798     INPUT_RECORD input_record;
1799     DWORD i_dw;
1800
1801     /* On Win32, select() only works on socket descriptors */
1802     while( WaitForSingleObjectEx( p_intf->p_sys->hConsoleIn,
1803                                 INTF_IDLE_SLEEP/1000, TRUE ) == WAIT_OBJECT_0 )
1804     {
1805         while( *pi_size < MAX_LINE_LENGTH &&
1806                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
1807                                  1, &i_dw ) )
1808         {
1809             if( input_record.EventType != KEY_EVENT ||
1810                 !input_record.Event.KeyEvent.bKeyDown ||
1811                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
1812                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
1813                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
1814                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
1815             {
1816                 /* nothing interesting */
1817                 continue;
1818             }
1819
1820             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
1821
1822             /* Echo out the command */
1823             putc( p_buffer[ *pi_size ], stdout );
1824
1825             /* Handle special keys */
1826             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1827             {
1828                 putc( '\n', stdout );
1829                 break;
1830             }
1831             switch( p_buffer[ *pi_size ] )
1832             {
1833             case '\b':
1834                 if( *pi_size )
1835                 {
1836                     *pi_size -= 2;
1837                     putc( ' ', stdout );
1838                     putc( '\b', stdout );
1839                 }
1840                 break;
1841             case '\r':
1842                 (*pi_size) --;
1843                 break;
1844             }
1845
1846             (*pi_size)++;
1847         }
1848
1849         if( *pi_size == MAX_LINE_LENGTH ||
1850             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1851         {
1852             p_buffer[ *pi_size ] = 0;
1853             return true;
1854         }
1855     }
1856
1857     vlc_testcancel ();
1858
1859     return false;
1860 }
1861 #endif
1862
1863 bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1864 {
1865     int i_read = 0;
1866
1867 #ifdef _WIN32
1868     if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
1869         return ReadWin32( p_intf, p_buffer, pi_size );
1870     else if( p_intf->p_sys->i_socket == -1 )
1871     {
1872         msleep( INTF_IDLE_SLEEP );
1873         return false;
1874     }
1875 #endif
1876
1877     while( *pi_size < MAX_LINE_LENGTH &&
1878            (i_read = net_Read( p_intf, p_intf->p_sys->i_socket == -1 ?
1879                        0 /*STDIN_FILENO*/ : p_intf->p_sys->i_socket, NULL,
1880                   (uint8_t *)p_buffer + *pi_size, 1, false ) ) > 0 )
1881     {
1882         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1883             break;
1884
1885         (*pi_size)++;
1886     }
1887
1888     /* Connection closed */
1889     if( i_read <= 0 )
1890     {
1891         if( p_intf->p_sys->i_socket != -1 )
1892         {
1893             net_Close( p_intf->p_sys->i_socket );
1894             p_intf->p_sys->i_socket = -1;
1895         }
1896         else
1897         {
1898             /* Standard input closed: exit */
1899             vlc_value_t empty;
1900             Quit( VLC_OBJECT(p_intf), NULL, empty, empty, NULL );
1901         }
1902
1903         p_buffer[ *pi_size ] = 0;
1904         return true;
1905     }
1906
1907     if( *pi_size == MAX_LINE_LENGTH ||
1908         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1909     {
1910         p_buffer[ *pi_size ] = 0;
1911         return true;
1912     }
1913
1914     return false;
1915 }
1916
1917 /*****************************************************************************
1918  * parse_MRL: build a input item from a full mrl
1919  *****************************************************************************
1920  * MRL format: "simplified-mrl [:option-name[=option-value]]"
1921  * We don't check for '"' or '\'', we just assume that a ':' that follows a
1922  * space is a new option. Should be good enough for our purpose.
1923  *****************************************************************************/
1924 static input_item_t *parse_MRL( const char *mrl )
1925 {
1926 #define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; }
1927 #define SKIPTRAILINGSPACE( p, d ) \
1928     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
1929
1930     input_item_t *p_item = NULL;
1931     char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig, *psz_mrl;
1932     char **ppsz_options = NULL;
1933     int i, i_options = 0;
1934
1935     if( !mrl ) return 0;
1936
1937     psz_mrl = psz_orig = strdup( mrl );
1938     if( !psz_mrl )
1939         return NULL;
1940     while( *psz_mrl )
1941     {
1942         SKIPSPACE( psz_mrl );
1943         psz_item = psz_mrl;
1944
1945         for( ; *psz_mrl; psz_mrl++ )
1946         {
1947             if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' )
1948             {
1949                 /* We have a complete item */
1950                 break;
1951             }
1952             if( (*psz_mrl == ' ' || *psz_mrl == '\t') &&
1953                 (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
1954             {
1955                 /* We have a complete item */
1956                 break;
1957             }
1958         }
1959
1960         if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; }
1961         SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) );
1962
1963         /* Remove '"' and '\'' if necessary */
1964         if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' )
1965         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
1966         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
1967         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
1968
1969         if( !psz_item_mrl )
1970         {
1971             if( strstr( psz_item, "://" ) != NULL )
1972                 psz_item_mrl = strdup( psz_item );
1973             else
1974                 psz_item_mrl = vlc_path2uri( psz_item, NULL );
1975             if( psz_item_mrl == NULL )
1976             {
1977                 free( psz_orig );
1978                 return NULL;
1979             }
1980         }
1981         else if( *psz_item )
1982         {
1983             i_options++;
1984             ppsz_options = xrealloc( ppsz_options, i_options * sizeof(char *) );
1985             ppsz_options[i_options - 1] = &psz_item[1];
1986         }
1987
1988         if( *psz_mrl ) SKIPSPACE( psz_mrl );
1989     }
1990
1991     /* Now create a playlist item */
1992     if( psz_item_mrl )
1993     {
1994         p_item = input_item_New( psz_item_mrl, NULL );
1995         for( i = 0; i < i_options; i++ )
1996         {
1997             input_item_AddOption( p_item, ppsz_options[i], VLC_INPUT_OPTION_TRUSTED );
1998         }
1999         free( psz_item_mrl );
2000     }
2001
2002     if( i_options ) free( ppsz_options );
2003     free( psz_orig );
2004
2005     return p_item;
2006 }