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