fba364e0f437894bd5a978c0487825922b74e1b1
[vlc.git] / modules / control / rc / rc.c
1 /*****************************************************************************
2  * rc.c : remote control stdin/stdout plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: rc.c,v 1.14 2002/12/06 16:34:06 sam Exp $
6  *
7  * Authors: Peter Surda <shurdeek@panorama.sth.ac.at>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <signal.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/intf.h>
37 #include <vlc/aout.h>
38 #include <vlc/vout.h>
39
40 #ifdef HAVE_UNISTD_H
41 #    include <unistd.h>
42 #endif
43
44 #ifdef HAVE_SYS_TIME_H
45 #    include <sys/time.h>
46 #endif
47 #include <sys/types.h>
48
49 #if defined( WIN32 )
50 #include <winsock2.h>                                            /* select() */
51 #endif
52
53 #include "error.h"
54
55 #define MAX_LINE_LENGTH 256
56
57 /*****************************************************************************
58  * Local prototypes
59  *****************************************************************************/
60 static int  Activate     ( vlc_object_t * );
61 static void Run          ( intf_thread_t *p_intf );
62
63 static int  Playlist     ( vlc_object_t *, char *, char * );
64 static int  Quit         ( vlc_object_t *, char *, char * );
65 static int  Intf         ( vlc_object_t *, char *, char * );
66 static int  Volume       ( vlc_object_t *, char *, char * );
67 static int  VolumeMove   ( vlc_object_t *, char *, char * );
68 static int  AudioConfig  ( vlc_object_t *, char *, char * );
69
70 /*****************************************************************************
71  * Module descriptor
72  *****************************************************************************/
73 #define POS_TEXT N_("show stream position")
74 #define POS_LONGTEXT N_("Show the current position in seconds within the stream from time to time.")
75
76 #define TTY_TEXT N_("fake TTY")
77 #define TTY_LONGTEXT N_("Force the rc plugin to use stdin as if it was a TTY.")
78
79 vlc_module_begin();
80     add_category_hint( N_("Remote control"), NULL );
81     add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT );
82 #ifdef HAVE_ISATTY
83     add_bool( "fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT );
84 #endif
85     set_description( _("remote control interface module") );
86     set_capability( "interface", 20 );
87     set_callbacks( Activate, NULL );
88 vlc_module_end();
89
90 /*****************************************************************************
91  * Activate: initialize and create stuff
92  *****************************************************************************/
93 static int Activate( vlc_object_t *p_this )
94 {
95     intf_thread_t *p_intf = (intf_thread_t*)p_this;
96
97 #ifdef HAVE_ISATTY
98     /* Check that stdin is a TTY */
99     if( !config_GetInt( p_intf, "fake-tty" ) && !isatty( 0 ) )
100     {
101         msg_Warn( p_intf, "fd 0 is not a TTY" );
102         return VLC_EGENERIC;
103     }
104 #endif
105
106     /* Non-buffered stdout */
107     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
108
109     p_intf->pf_run = Run;
110
111     CONSOLE_INTRO_MSG;
112
113     printf( "remote control interface initialized, `h' for help\n" );
114     return VLC_SUCCESS;
115 }
116
117 /*****************************************************************************
118  * Run: rc thread
119  *****************************************************************************
120  * This part of the interface is in a separate thread so that we can call
121  * exec() from within it without annoying the rest of the program.
122  *****************************************************************************/
123 static void Run( intf_thread_t *p_intf )
124 {
125     input_thread_t * p_input;
126     playlist_t *     p_playlist;
127
128     char       p_buffer[ MAX_LINE_LENGTH + 1 ];
129     vlc_bool_t b_showpos = config_GetInt( p_intf, "rc-show-pos" );
130     input_info_category_t * p_category;
131     input_info_t * p_info;
132
133     int        i_dummy;
134     off_t      i_oldpos = 0;
135     off_t      i_newpos;
136
137     double     f_ratio = 1.0;
138
139     p_input = NULL;
140     p_playlist = NULL;
141
142     /* Register commands that will be cleaned up upon object destruction */
143     var_Create( p_intf, "quit", VLC_VAR_COMMAND );
144     var_Set( p_intf, "quit", (vlc_value_t)(void*)Quit );
145     var_Create( p_intf, "intf", VLC_VAR_COMMAND );
146     var_Set( p_intf, "intf", (vlc_value_t)(void*)Intf );
147
148     var_Create( p_intf, "play", VLC_VAR_COMMAND );
149     var_Set( p_intf, "play", (vlc_value_t)(void*)Playlist );
150     var_Create( p_intf, "stop", VLC_VAR_COMMAND );
151     var_Set( p_intf, "stop", (vlc_value_t)(void*)Playlist );
152     var_Create( p_intf, "pause", VLC_VAR_COMMAND );
153     var_Set( p_intf, "pause", (vlc_value_t)(void*)Playlist );
154     var_Create( p_intf, "seek", VLC_VAR_COMMAND );
155     var_Set( p_intf, "seek", (vlc_value_t)(void*)Playlist );
156     var_Create( p_intf, "prev", VLC_VAR_COMMAND );
157     var_Set( p_intf, "prev", (vlc_value_t)(void*)Playlist );
158     var_Create( p_intf, "next", VLC_VAR_COMMAND );
159     var_Set( p_intf, "next", (vlc_value_t)(void*)Playlist );
160
161     var_Create( p_intf, "title", VLC_VAR_COMMAND );
162     var_Set( p_intf, "title", (vlc_value_t)(void*)Playlist );
163     var_Create( p_intf, "title_n", VLC_VAR_COMMAND );
164     var_Set( p_intf, "title_n", (vlc_value_t)(void*)Playlist );
165     var_Create( p_intf, "title_p", VLC_VAR_COMMAND );
166     var_Set( p_intf, "title_p", (vlc_value_t)(void*)Playlist );
167     var_Create( p_intf, "chapter", VLC_VAR_COMMAND );
168     var_Set( p_intf, "chapter", (vlc_value_t)(void*)Playlist );
169     var_Create( p_intf, "chapter_n", VLC_VAR_COMMAND );
170     var_Set( p_intf, "chapter_n", (vlc_value_t)(void*)Playlist );
171     var_Create( p_intf, "chapter_p", VLC_VAR_COMMAND );
172     var_Set( p_intf, "chapter_p", (vlc_value_t)(void*)Playlist );
173
174     var_Create( p_intf, "volume", VLC_VAR_COMMAND );
175     var_Set( p_intf, "volume", (vlc_value_t)(void*)Volume );
176     var_Create( p_intf, "volup", VLC_VAR_COMMAND );
177     var_Set( p_intf, "volup", (vlc_value_t)(void*)VolumeMove );
178     var_Create( p_intf, "voldown", VLC_VAR_COMMAND );
179     var_Set( p_intf, "voldown", (vlc_value_t)(void*)VolumeMove );
180     var_Create( p_intf, "adev", VLC_VAR_COMMAND );
181     var_Set( p_intf, "adev", (vlc_value_t)(void*)AudioConfig );
182     var_Create( p_intf, "achan", VLC_VAR_COMMAND );
183     var_Set( p_intf, "achan", (vlc_value_t)(void*)AudioConfig );
184
185     while( !p_intf->b_die )
186     {
187         fd_set         fds;
188         struct timeval tv;
189         vlc_bool_t     b_complete = VLC_FALSE;
190
191         /* Check stdin */
192         tv.tv_sec = 0;
193         tv.tv_usec = 50000;
194         FD_ZERO( &fds );
195         FD_SET( STDIN_FILENO, &fds );
196
197         i_dummy = select( 32, &fds, NULL, NULL, &tv );
198         if( i_dummy > 0 )
199         {
200             int i_size = 0;
201
202             while( !p_intf->b_die
203                     && i_size < MAX_LINE_LENGTH
204                     && read( STDIN_FILENO, p_buffer + i_size, 1 ) > 0
205                     && p_buffer[ i_size ] != '\r'
206                     && p_buffer[ i_size ] != '\n' )
207             {
208                 i_size++;
209             }
210
211             if( i_size == MAX_LINE_LENGTH
212                  || p_buffer[ i_size ] == '\r'
213                  || p_buffer[ i_size ] == '\n' )
214             {
215                 p_buffer[ i_size ] = 0;
216                 b_complete = VLC_TRUE;
217             }
218         }
219
220         /* Manage the input part */
221         if( p_input == NULL )
222         {
223             if( p_playlist )
224             {
225                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
226                                                        FIND_CHILD );
227             }
228             else
229             {
230                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
231                                                    FIND_ANYWHERE );
232                 if( p_input )
233                 {
234                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
235                                                            FIND_PARENT );
236                 }
237             }
238         }
239         else if( p_input->b_dead )
240         {
241             vlc_object_release( p_input );
242             p_input = NULL;
243         }
244
245         if( p_input && b_showpos )
246         {
247             /* Get position */
248             vlc_mutex_lock( &p_input->stream.stream_lock );
249             if( !p_input->b_die && p_input->stream.i_mux_rate )
250             {
251 #define A p_input->stream.p_selected_area
252                 f_ratio = 1.0 / ( 50 * p_input->stream.i_mux_rate );
253                 i_newpos = A->i_tell * f_ratio;
254
255                 if( i_oldpos != i_newpos )
256                 {
257                     i_oldpos = i_newpos;
258                     printf( "pos: %li s / %li s\n", (long int)i_newpos,
259                             (long int)(f_ratio * A->i_size) );
260                 }
261 #undef S
262             }
263             vlc_mutex_unlock( &p_input->stream.stream_lock );
264         }
265
266         /* Is there something to do? */
267         if( b_complete )
268         {
269             char *psz_cmd, *psz_arg;
270
271             /* Skip heading spaces */
272             psz_cmd = p_buffer;
273             while( *psz_cmd == ' ' )
274             {
275                 psz_cmd++;
276             }
277
278             /* Split psz_cmd at the first space and make sure that
279              * psz_arg is valid */
280             psz_arg = strchr( psz_cmd, ' ' );
281             if( psz_arg )
282             {
283                 *psz_arg++ = 0;
284                 while( *psz_arg == ' ' )
285                 {
286                     psz_arg++;
287                 }
288             }
289             else
290             {
291                 psz_arg = "";
292             }
293
294             /* If the user typed a registered local command, try it */
295             if( var_Type( p_intf, psz_cmd ) == VLC_VAR_COMMAND )
296             {
297                 vlc_value_t val;
298                 int i_ret;
299
300                 val.psz_string = psz_arg;
301                 i_ret = var_Get( p_intf, psz_cmd, &val );
302                 printf( "%s: returned %i (%s)\n",
303                         psz_cmd, i_ret, vlc_error( i_ret ) );
304             }
305             /* Or maybe it's a global command */
306             else if( var_Type( p_intf->p_libvlc, psz_cmd ) == VLC_VAR_COMMAND )
307             {
308                 vlc_value_t val;
309                 int i_ret;
310
311                 val.psz_string = psz_arg;
312                 /* FIXME: it's a global command, but we should pass the
313                  * local object as an argument, not p_intf->p_libvlc. */
314                 i_ret = var_Get( p_intf->p_libvlc, psz_cmd, &val );
315                 printf( "%s: returned %i (%s)\n",
316                         psz_cmd, i_ret, vlc_error( i_ret ) );
317             }
318             else if( !strcmp( psz_cmd, "info" ) )
319             {
320                 if ( p_input )
321                 {
322                     vlc_mutex_lock( &p_input->stream.stream_lock );
323                     p_category = p_input->stream.p_info;
324                     while ( p_category )
325                     {
326                         printf( "+----[ %s ]\n", p_category->psz_name );
327                         printf( "| \n" );
328                         p_info = p_category->p_info;
329                         while ( p_info )
330                         {
331                             printf( "| %s: %s\n", p_info->psz_name,
332                                     p_info->psz_value );
333                             p_info = p_info->p_next;
334                         }
335                         p_category = p_category->p_next;
336                         printf( "| \n" );
337                     }
338                     printf( "+----[ end of stream info ]\n" );
339                     vlc_mutex_unlock( &p_input->stream.stream_lock );
340                 }
341                 else
342                 {
343                     printf( "no input\n" );
344                 }
345             }
346             else switch( psz_cmd[0] )
347             {
348             case 'a':
349             case 'A':
350                 if( psz_cmd[1] == ' ' && p_playlist )
351                 {
352                     playlist_Add( p_playlist, psz_cmd + 2,
353                                   PLAYLIST_APPEND | PLAYLIST_GO, PLAYLIST_END );
354                 }
355                 break;
356
357             case 'f':
358             case 'F':
359                 if( p_input )
360                 {
361                     vout_thread_t *p_vout;
362                     p_vout = vlc_object_find( p_input,
363                                               VLC_OBJECT_VOUT, FIND_CHILD );
364
365                     if( p_vout )
366                     {
367                         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
368                         vlc_object_release( p_vout );
369                     }
370                 }
371                 break;
372
373             case 's':
374             case 'S':
375                 ;
376                 break;
377
378             case '?':
379             case 'h':
380             case 'H':
381                 printf("+----[ remote control commands ]\n");
382                 printf("| \n");
383                 printf("| a XYZ  . . . . . . . . . . . add XYZ to playlist\n");
384                 printf("| play . . . . . . . . . . . . . . . . play stream\n");
385                 printf("| stop . . . . . . . . . . . . . . . . stop stream\n");
386                 printf("| next . . . . . . . . . . . .  next playlist item\n");
387                 printf("| prev . . . . . . . . . .  previous playlist item\n");
388                 printf("| title [X]  . . . . set/get title in current item\n");
389                 printf("| title_n  . . . . . .  next title in current item\n");
390                 printf("| title_p  . . . .  previous title in current item\n");
391                 printf("| chapter [X]  . . set/get chapter in current item\n");
392                 printf("| chapter_n  . . . .  next chapter in current item\n");
393                 printf("| chapter_p  . .  previous chapter in current item\n");
394                 printf("| \n");
395                 printf("| seek X . seek in seconds, for instance `seek 12'\n");
396                 printf("| pause  . . . . . . . . . . . . . .  toggle pause\n");
397                 printf("| f  . . . . . . . . . . . . . . toggle fullscreen\n");
398                 printf("| info . . .  information about the current stream\n");
399                 printf("| \n");
400                 printf("| volume [X] . . . . . . . .  set/get audio volume\n");
401                 printf("| volup [X]  . . . . .  raise audio volume X steps\n");
402                 printf("| voldown [X]  . . . .  lower audio volume X steps\n");
403                 printf("| adev [X] . . . . . . . . .  set/get audio device\n");
404                 printf("| achan [X]. . . . . . . .  set/get audio channels\n");
405                 printf("| \n");
406                 printf("| help . . . . . . . . . . . . . this help message\n");
407                 printf("| quit . . . . . . . . . . . . . . . . .  quit vlc\n");
408                 printf("| \n");
409                 printf("+----[ end of help ]\n");
410                 break;
411             case '\0':
412                 /* Ignore empty lines */
413                 break;
414             default:
415                 printf( "unknown command `%s', type `help' for help\n", psz_cmd );
416                 break;
417             }
418         }
419     }
420
421     if( p_input )
422     {
423         vlc_object_release( p_input );
424         p_input = NULL;
425     }
426
427     if( p_playlist )
428     {
429         vlc_object_release( p_playlist );
430         p_playlist = NULL;
431     }
432 }
433
434 static int Playlist( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
435 {
436     input_thread_t * p_input;
437     playlist_t *     p_playlist;
438
439     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
440
441     if( !p_input )
442     {
443         return VLC_ENOOBJ;
444     }
445
446     /* Parse commands that only require an input */
447     if( !strcmp( psz_cmd, "pause" ) )
448     {
449         input_SetStatus( p_input, INPUT_STATUS_PAUSE );
450         vlc_object_release( p_input );
451         return VLC_SUCCESS;
452     }
453     else if( !strcmp( psz_cmd, "seek" ) )
454     {
455         input_Seek( p_input, atoi( psz_arg ),
456                     INPUT_SEEK_SECONDS | INPUT_SEEK_SET );
457     }
458     else if( !strcmp( psz_cmd, "chapter" ) ||
459              !strcmp( psz_cmd, "chapter_n" ) ||
460              !strcmp( psz_cmd, "chapter_p" ) )
461     {
462         unsigned int i_chapter = 0;
463
464         if( !strcmp( psz_cmd, "chapter" ) )
465         {
466             if ( *psz_arg )
467             {
468                 /* Set. */
469                 i_chapter = atoi( psz_arg );
470             }
471             else
472             {
473                 /* Get. */
474                 vlc_mutex_lock( &p_input->stream.stream_lock );
475                 printf( "Currently playing chapter %d\n",
476                         p_input->stream.p_selected_area->i_part );
477                 vlc_mutex_unlock( &p_input->stream.stream_lock );
478
479                 vlc_object_release( p_input );
480                 return VLC_SUCCESS;
481             }
482         }
483         else if( !strcmp( psz_cmd, "chapter_n" ) )
484         {
485             vlc_mutex_lock( &p_input->stream.stream_lock );
486             i_chapter = p_input->stream.p_selected_area->i_part + 1;
487             vlc_mutex_unlock( &p_input->stream.stream_lock );
488         }
489         else if( !strcmp( psz_cmd, "chapter_p" ) )
490         {
491             vlc_mutex_lock( &p_input->stream.stream_lock );
492             i_chapter = p_input->stream.p_selected_area->i_part - 1;
493             vlc_mutex_unlock( &p_input->stream.stream_lock );
494         }
495
496         vlc_mutex_lock( &p_input->stream.stream_lock );
497         if( ( i_chapter > 0 ) && ( i_chapter <=
498             p_input->stream.p_selected_area->i_part_nb ) )
499         {
500           p_input->stream.p_selected_area->i_part = i_chapter;
501           vlc_mutex_unlock( &p_input->stream.stream_lock );
502           input_ChangeArea( p_input,
503                             (input_area_t*)p_input->stream.p_selected_area );
504           input_SetStatus( p_input, INPUT_STATUS_PLAY );
505           vlc_mutex_lock( &p_input->stream.stream_lock );
506         }
507         vlc_mutex_unlock( &p_input->stream.stream_lock );
508
509         vlc_object_release( p_input );
510         return VLC_SUCCESS;
511     }
512     else if( !strcmp( psz_cmd, "title" ) ||
513              !strcmp( psz_cmd, "title_n" ) ||
514              !strcmp( psz_cmd, "title_p" ) )
515     {
516         unsigned int i_title = 0;
517
518         if( !strcmp( psz_cmd, "title" ) )
519         {
520             if ( *psz_arg )
521             {
522                 /* Set. */
523                 i_title = atoi( psz_arg );
524             }
525             else
526             {
527                 /* Get. */
528                 vlc_mutex_lock( &p_input->stream.stream_lock );
529                 printf( "Currently playing title %d\n",
530                         p_input->stream.p_selected_area->i_id );
531                 vlc_mutex_unlock( &p_input->stream.stream_lock );
532
533                 vlc_object_release( p_input );
534                 return VLC_SUCCESS;
535             }
536         }
537         else if( !strcmp( psz_cmd, "title_n" ) )
538         {
539             vlc_mutex_lock( &p_input->stream.stream_lock );
540             i_title = p_input->stream.p_selected_area->i_id + 1;
541             vlc_mutex_unlock( &p_input->stream.stream_lock );
542         }
543         else if( !strcmp( psz_cmd, "title_p" ) )
544         {
545             vlc_mutex_lock( &p_input->stream.stream_lock );
546             i_title = p_input->stream.p_selected_area->i_id - 1;
547             vlc_mutex_unlock( &p_input->stream.stream_lock );
548         }
549
550         vlc_mutex_lock( &p_input->stream.stream_lock );
551         if( ( i_title > 0 ) && ( i_title <=
552             p_input->stream.p_selected_area->i_part_nb ) )
553         {
554           p_input->stream.p_selected_area->i_part = i_title;
555           vlc_mutex_unlock( &p_input->stream.stream_lock );
556           input_ChangeArea( p_input,
557                             (input_area_t*)p_input->stream.pp_areas[i_title] );
558           input_SetStatus( p_input, INPUT_STATUS_PLAY );
559           vlc_mutex_lock( &p_input->stream.stream_lock );
560         }
561         vlc_mutex_unlock( &p_input->stream.stream_lock );
562
563         vlc_object_release( p_input );
564         return VLC_SUCCESS;
565     }
566
567
568     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
569                                            FIND_PARENT );
570     vlc_object_release( p_input );
571
572     if( !p_playlist )
573     {
574         return VLC_ENOOBJ;
575     }
576
577     /* Parse commands that require a playlist */
578     if( !strcmp( psz_cmd, "prev" ) )
579     {
580         playlist_Prev( p_playlist );
581     }
582     else if( !strcmp( psz_cmd, "next" ) )
583     {
584         playlist_Next( p_playlist );
585     }
586     else if( !strcmp( psz_cmd, "play" ) )
587     {
588         playlist_Play( p_playlist );
589     }
590     else if( !strcmp( psz_cmd, "stop" ) )
591     {
592         playlist_Stop( p_playlist );
593     }
594
595     vlc_object_release( p_playlist );
596     return VLC_SUCCESS;
597 }
598
599 static int Quit( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
600 {
601     p_this->p_vlc->b_die = VLC_TRUE;
602     return VLC_SUCCESS;
603 }
604
605 static int Intf( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
606 {
607     intf_thread_t *p_newintf;
608     char *psz_oldmodule = config_GetPsz( p_this->p_vlc, "intf" );
609
610     config_PutPsz( p_this->p_vlc, "intf", psz_arg );
611     p_newintf = intf_Create( p_this->p_vlc );
612     config_PutPsz( p_this->p_vlc, "intf", psz_oldmodule );
613
614     if( psz_oldmodule )
615     {
616         free( psz_oldmodule );
617     }
618
619     if( p_newintf )
620     {
621         p_newintf->b_block = VLC_FALSE;
622         if( intf_RunThread( p_newintf ) )
623         {
624             vlc_object_detach( p_newintf );
625             intf_Destroy( p_newintf );
626         }
627     }
628
629     return VLC_SUCCESS;
630 }
631
632 static int Signal( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
633 {
634     raise( atoi(psz_arg) );
635     return VLC_SUCCESS;
636 }
637
638 static int Volume( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
639 {
640     aout_instance_t * p_aout;
641     int i_error;
642     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
643     if ( p_aout == NULL ) return VLC_ENOOBJ;
644
645     if ( *psz_arg )
646     {
647         /* Set. */
648         audio_volume_t i_volume = atoi( psz_arg );
649         if ( i_volume > AOUT_VOLUME_MAX )
650         {
651             printf( "Volume must be in the range %d-%d\n", AOUT_VOLUME_MIN,
652                     AOUT_VOLUME_MAX );
653             i_error = VLC_EBADVAR;
654         }
655         else i_error = aout_VolumeSet( p_aout, i_volume );
656     }
657     else
658     {
659         /* Get. */
660         audio_volume_t i_volume;
661         if ( aout_VolumeGet( p_aout, &i_volume ) < 0 )
662         {
663             i_error = VLC_EGENERIC;
664         }
665         else
666         {
667             printf( "Volume is %d\n", i_volume );
668             i_error = VLC_SUCCESS;
669         }
670     }
671     vlc_object_release( (vlc_object_t *)p_aout );
672
673     return i_error;
674 }
675
676 static int VolumeMove( vlc_object_t * p_this, char * psz_cmd, char * psz_arg )
677 {
678     aout_instance_t * p_aout;
679     audio_volume_t i_volume;
680     int i_nb_steps = atoi(psz_arg);
681     int i_error = VLC_SUCCESS;
682
683     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/AOUT_VOLUME_STEP) )
684     {
685         i_nb_steps = 1;
686     }
687
688     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
689     if ( p_aout == NULL ) return VLC_ENOOBJ;
690
691     if ( !strcmp(psz_cmd, "volup") )
692     {
693         if ( aout_VolumeUp( p_aout, i_nb_steps, &i_volume ) < 0 )
694             i_error = VLC_EGENERIC;
695     }
696     else
697     {
698         if ( aout_VolumeDown( p_aout, i_nb_steps, &i_volume ) < 0 )
699             i_error = VLC_EGENERIC;
700     }
701     vlc_object_release( (vlc_object_t *)p_aout );
702
703     if ( !i_error ) printf( "Volume is %d\n", i_volume );
704     return i_error;
705 }
706
707 static int AudioConfig( vlc_object_t * p_this, char * psz_cmd, char * psz_arg )
708 {
709     aout_instance_t * p_aout;
710     const char * psz_variable;
711     const char * psz_name;
712     int i_error;
713
714     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
715     if ( p_aout == NULL ) return VLC_ENOOBJ;
716
717     if ( !strcmp( psz_cmd, "adev" ) )
718     {
719         psz_variable = "audio-device";
720         psz_name = "audio devices";
721     }
722     else
723     {
724         psz_variable = "audio-channels";
725         psz_name = "audio channels";
726     }
727
728     if ( !*psz_arg )
729     {
730         /* Retrieve all registered ***. */
731         vlc_value_t val;
732         int i, i_vals;
733         vlc_value_t * p_vals;
734         char * psz_value;
735
736         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
737         {
738             vlc_object_release( (vlc_object_t *)p_aout );
739             return VLC_EGENERIC;
740         }
741         psz_value = val.psz_string;
742
743         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
744                          VLC_VAR_GETLIST, &val ) < 0 )
745         {
746             free( psz_value );
747             vlc_object_release( (vlc_object_t *)p_aout );
748             return VLC_EGENERIC;
749         }
750
751         printf( "+----[ %s ]\n", psz_name );
752         i_vals = ((vlc_value_t *)val.p_address)[0].i_int;
753         p_vals = &((vlc_value_t *)val.p_address)[1]; /* Starts at index 1 */
754         for ( i = 0; i < i_vals; i++ )
755         {
756             if ( !strcmp( psz_value, p_vals[i].psz_string ) )
757                 printf( "| %s *\n", p_vals[i].psz_string );
758             else
759                 printf( "| %s\n", p_vals[i].psz_string );
760         }
761         var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_FREELIST,
762                     &val );
763         printf( "+----[ end of %s ]\n", psz_name );
764
765         free( psz_value );
766         i_error = VLC_SUCCESS;
767     }
768     else
769     {
770         vlc_value_t val;
771         val.psz_string = psz_arg;
772
773         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
774     }
775     vlc_object_release( (vlc_object_t *)p_aout );
776
777     return i_error;
778 }
779