]> git.sesse.net Git - vlc/blob - modules/control/rc/rc.c
Major change of the channels management. p_format->i_channels disappeares
[vlc] / 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.10 2002/11/14 22:38:47 massiot 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/vout.h>
38
39 #ifdef HAVE_UNISTD_H
40 #    include <unistd.h>
41 #endif
42
43 #ifdef HAVE_SYS_TIME_H
44 #    include <sys/time.h>
45 #endif
46 #include <sys/types.h>
47
48 #if defined( WIN32 )
49 #include <winsock2.h>                                            /* select() */
50 #endif
51
52 #define MAX_LINE_LENGTH 256
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57 static int  Activate     ( vlc_object_t * );
58 static void Run          ( intf_thread_t *p_intf );
59
60 static int  Playlist     ( vlc_object_t *, char *, char * );
61 static int  Quit         ( vlc_object_t *, char *, char * );
62 static int  Intf         ( vlc_object_t *, char *, char * );
63 static int  Volume       ( vlc_object_t *, char *, char * );
64 static int  VolumeMove   ( vlc_object_t *, char *, char * );
65 static int  AudioConfig  ( vlc_object_t *, char *, char * );
66
67 /*****************************************************************************
68  * Module descriptor
69  *****************************************************************************/
70 #define POS_TEXT N_("show stream position")
71 #define POS_LONGTEXT N_("Show the current position in seconds within the stream from time to time.")
72
73 #define TTY_TEXT N_("fake TTY")
74 #define TTY_LONGTEXT N_("Force the rc plugin to use stdin as if it was a TTY.")
75
76 vlc_module_begin();
77     add_category_hint( N_("Remote control"), NULL );
78     add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT );
79 #ifdef HAVE_ISATTY
80     add_bool( "fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT );
81 #endif
82     set_description( _("remote control interface module") );
83     set_capability( "interface", 20 );
84     set_callbacks( Activate, NULL );
85 vlc_module_end();
86
87 /*****************************************************************************
88  * Activate: initialize and create stuff
89  *****************************************************************************/
90 static int Activate( vlc_object_t *p_this )
91 {
92     intf_thread_t *p_intf = (intf_thread_t*)p_this;
93
94 #ifdef HAVE_ISATTY
95     /* Check that stdin is a TTY */
96     if( !config_GetInt( p_intf, "fake-tty" ) && !isatty( 0 ) )
97     {
98         msg_Warn( p_intf, "fd 0 is not a TTY" );
99         return VLC_EGENERIC;
100     }
101 #endif
102
103     /* Non-buffered stdout */
104     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
105
106     p_intf->pf_run = Run;
107
108     CONSOLE_INTRO_MSG;
109
110     printf( "remote control interface initialized, `h' for help\n" );
111     return VLC_SUCCESS;
112 }
113
114 /*****************************************************************************
115  * Run: rc thread
116  *****************************************************************************
117  * This part of the interface is in a separate thread so that we can call
118  * exec() from within it without annoying the rest of the program.
119  *****************************************************************************/
120 static void Run( intf_thread_t *p_intf )
121 {
122     input_thread_t * p_input;
123     playlist_t *     p_playlist;
124
125     char       p_buffer[ MAX_LINE_LENGTH + 1 ];
126     vlc_bool_t b_showpos = config_GetInt( p_intf, "rc-show-pos" );
127     input_info_category_t * p_category;
128     input_info_t * p_info;
129
130     int        i_dummy;
131     off_t      i_oldpos = 0;
132     off_t      i_newpos;
133
134     double     f_ratio = 1.0;
135
136     p_input = NULL;
137     p_playlist = NULL;
138
139     /* Register commands that will be cleaned up upon object destruction */
140     var_Create( p_intf, "quit", VLC_VAR_COMMAND );
141     var_Set( p_intf, "quit", (vlc_value_t)(void*)Quit );
142     var_Create( p_intf, "intf", VLC_VAR_COMMAND );
143     var_Set( p_intf, "intf", (vlc_value_t)(void*)Intf );
144
145     var_Create( p_intf, "play", VLC_VAR_COMMAND );
146     var_Set( p_intf, "play", (vlc_value_t)(void*)Playlist );
147     var_Create( p_intf, "stop", VLC_VAR_COMMAND );
148     var_Set( p_intf, "stop", (vlc_value_t)(void*)Playlist );
149     var_Create( p_intf, "pause", VLC_VAR_COMMAND );
150     var_Set( p_intf, "pause", (vlc_value_t)(void*)Playlist );
151     var_Create( p_intf, "prev", VLC_VAR_COMMAND );
152     var_Set( p_intf, "prev", (vlc_value_t)(void*)Playlist );
153     var_Create( p_intf, "next", VLC_VAR_COMMAND );
154     var_Set( p_intf, "next", (vlc_value_t)(void*)Playlist );
155
156     var_Create( p_intf, "volume", VLC_VAR_COMMAND );
157     var_Set( p_intf, "volume", (vlc_value_t)(void*)Volume );
158     var_Create( p_intf, "volup", VLC_VAR_COMMAND );
159     var_Set( p_intf, "volup", (vlc_value_t)(void*)VolumeMove );
160     var_Create( p_intf, "voldown", VLC_VAR_COMMAND );
161     var_Set( p_intf, "voldown", (vlc_value_t)(void*)VolumeMove );
162     var_Create( p_intf, "adev", VLC_VAR_COMMAND );
163     var_Set( p_intf, "adev", (vlc_value_t)(void*)AudioConfig );
164     var_Create( p_intf, "achan", VLC_VAR_COMMAND );
165     var_Set( p_intf, "achan", (vlc_value_t)(void*)AudioConfig );
166
167     while( !p_intf->b_die )
168     {
169         fd_set         fds;
170         struct timeval tv;
171         vlc_bool_t     b_complete = VLC_FALSE;
172
173         /* Check stdin */
174         tv.tv_sec = 0;
175         tv.tv_usec = 50000;
176         FD_ZERO( &fds );
177         FD_SET( STDIN_FILENO, &fds );
178
179         i_dummy = select( 32, &fds, NULL, NULL, &tv );
180         if( i_dummy > 0 )
181         {
182             int i_size = 0;
183
184             while( !p_intf->b_die
185                     && i_size < MAX_LINE_LENGTH
186                     && read( STDIN_FILENO, p_buffer + i_size, 1 ) > 0
187                     && p_buffer[ i_size ] != '\r'
188                     && p_buffer[ i_size ] != '\n' )
189             {
190                 i_size++;
191             }
192
193             if( i_size == MAX_LINE_LENGTH
194                  || p_buffer[ i_size ] == '\r'
195                  || p_buffer[ i_size ] == '\n' )
196             {
197                 p_buffer[ i_size ] = 0;
198                 b_complete = VLC_TRUE;
199             }
200         }
201
202         /* Manage the input part */
203         if( p_input == NULL )
204         {
205             if( p_playlist )
206             {
207                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
208                                                        FIND_CHILD );
209             }
210             else
211             {
212                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
213                                                    FIND_ANYWHERE );
214                 if( p_input )
215                 {
216                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
217                                                            FIND_PARENT );
218                 }
219             }
220         }
221         else if( p_input->b_dead )
222         {
223             vlc_object_release( p_input );
224             p_input = NULL;
225         }
226
227         if( p_input && b_showpos )
228         {
229             /* Get position */
230             vlc_mutex_lock( &p_input->stream.stream_lock );
231             if( !p_input->b_die && p_input->stream.i_mux_rate )
232             {
233 #define A p_input->stream.p_selected_area
234                 f_ratio = 1.0 / ( 50 * p_input->stream.i_mux_rate );
235                 i_newpos = A->i_tell * f_ratio;
236
237                 if( i_oldpos != i_newpos )
238                 {
239                     i_oldpos = i_newpos;
240                     printf( "pos: %li s / %li s\n", (long int)i_newpos,
241                             (long int)(f_ratio * A->i_size) );
242                 }
243 #undef S
244             }
245             vlc_mutex_unlock( &p_input->stream.stream_lock );
246         }
247
248         /* Is there something to do? */
249         if( b_complete )
250         {
251             char *psz_cmd, *psz_arg;
252
253             /* Skip heading spaces */
254             psz_cmd = p_buffer;
255             while( *psz_cmd == ' ' )
256             {
257                 psz_cmd++;
258             }
259
260             /* Split psz_cmd at the first space and make sure that
261              * psz_arg is valid */
262             psz_arg = strchr( psz_cmd, ' ' );
263             if( psz_arg )
264             {
265                 *psz_arg++ = 0;
266                 while( *psz_arg == ' ' )
267                 {
268                     psz_arg++;
269                 }
270             }
271             else
272             {
273                 psz_arg = "";
274             }
275
276             /* If the user typed a registered local command, try it */
277             if( var_Type( p_intf, psz_cmd ) == VLC_VAR_COMMAND )
278             {
279                 vlc_value_t val;
280                 int i_ret;
281
282                 val.psz_string = psz_arg;
283                 i_ret = var_Get( p_intf, psz_cmd, &val );
284                 printf( "%s: returned %i (%s)\n",
285                         psz_cmd, i_ret, vlc_error( i_ret ) );
286             }
287             /* Or maybe it's a global command */
288             else if( var_Type( p_intf->p_libvlc, psz_cmd ) == VLC_VAR_COMMAND )
289             {
290                 vlc_value_t val;
291                 int i_ret;
292
293                 val.psz_string = psz_arg;
294                 /* FIXME: it's a global command, but we should pass the
295                  * local object as an argument, not p_intf->p_libvlc. */
296                 i_ret = var_Get( p_intf->p_libvlc, psz_cmd, &val );
297                 printf( "%s: returned %i (%s)\n",
298                         psz_cmd, i_ret, vlc_error( i_ret ) );
299             }
300             else if( !strcmp( psz_cmd, "info" ) )
301             {
302                 if ( p_input )
303                 {
304                     vlc_mutex_lock( &p_input->stream.stream_lock );
305                     p_category = p_input->stream.p_info;
306                     while ( p_category )
307                     {
308                         printf( "+----[ %s ]\n", p_category->psz_name );
309                         printf( "| \n" );
310                         p_info = p_category->p_info;
311                         while ( p_info )
312                         {
313                             printf( "| %s: %s\n", p_info->psz_name,
314                                     p_info->psz_value );
315                             p_info = p_info->p_next;
316                         }
317                         p_category = p_category->p_next;
318                         printf( "| \n" );
319                     }
320                     printf( "+----[ end of stream info ]\n" );
321                     vlc_mutex_unlock( &p_input->stream.stream_lock );
322                 }
323                 else
324                 {
325                     printf( "no input\n" );
326                 }
327             }
328             else switch( psz_cmd[0] )
329             {
330             case 'a':
331             case 'A':
332                 if( psz_cmd[1] == ' ' && p_playlist )
333                 {
334                     playlist_Add( p_playlist, psz_cmd + 2,
335                                   PLAYLIST_APPEND | PLAYLIST_GO, PLAYLIST_END );
336                 }
337                 break;
338
339             case 'f':
340             case 'F':
341                 if( p_input )
342                 {
343                     vout_thread_t *p_vout;
344                     p_vout = vlc_object_find( p_input,
345                                               VLC_OBJECT_VOUT, FIND_CHILD );
346
347                     if( p_vout )
348                     {
349                         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
350                         vlc_object_release( p_vout );
351                     }
352                 }
353                 break;
354
355             case 's':
356             case 'S':
357                 ;
358                 break;
359
360             case 'r':
361             case 'R':
362                 if( p_input )
363                 {
364                     for( i_dummy = 1;
365                          i_dummy < MAX_LINE_LENGTH && psz_cmd[ i_dummy ] >= '0'
366                                                    && psz_cmd[ i_dummy ] <= '9';
367                          i_dummy++ )
368                     {
369                         ;
370                     }
371
372                     psz_cmd[ i_dummy ] = 0;
373                     input_Seek( p_input, (off_t)atoi( psz_cmd + 1 ),
374                                 INPUT_SEEK_SECONDS | INPUT_SEEK_SET );
375                     /* rcreseek(f_cpos); */
376                 }
377                 break;
378
379             case '?':
380             case 'h':
381             case 'H':
382                 printf("+----[ remote control commands ]\n");
383                 printf("| \n");
384                 printf("| a XYZ  . . . . . . . . . . . add XYZ to playlist\n");
385                 printf("| play . . . . . . . . . . . . . . . . play stream\n");
386                 printf("| stop . . . . . . . . . . . . . . . . stop stream\n");
387                 printf("| next . . . . . . . . . . . .  next playlist item\n");
388                 printf("| prev . . . . . . . . . .  previous playlist item\n");
389                 printf("| \n");
390                 printf("| r X  . . . seek in seconds, for instance `r 3.5'\n");
391                 printf("| pause  . . . . . . . . . . . . . .  toggle pause\n");
392                 printf("| f  . . . . . . . . . . . . . . toggle fullscreen\n");
393                 printf("| info . . .  information about the current stream\n");
394                 printf("| \n");
395                 printf("| volume [X] . . . . . . . .  set/get audio volume\n");
396                 printf("| volup [X]  . . . . .  raise audio volume X steps\n");
397                 printf("| voldown [X]  . . . .  lower audio volume X steps\n");
398                 printf("| adev [X] . . . . . . . . .  set/get audio device\n");
399                 printf("| achan [X]. . . . . . . .  set/get audio channels\n");
400                 printf("| \n");
401                 printf("| help . . . . . . . . . . . . . this help message\n");
402                 printf("| quit . . . . . . . . . . . . . . . . .  quit vlc\n");
403                 printf("| \n");
404                 printf("+----[ end of help ]\n");
405                 break;
406             case '\0':
407                 /* Ignore empty lines */
408                 break;
409             default:
410                 printf( "unknown command `%s', type `help' for help\n", psz_cmd );
411                 break;
412             }
413         }
414     }
415
416     if( p_input )
417     {
418         vlc_object_release( p_input );
419         p_input = NULL;
420     }
421
422     if( p_playlist )
423     {
424         vlc_object_release( p_playlist );
425         p_playlist = NULL;
426     }
427 }
428
429 static int Playlist( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
430 {
431     input_thread_t * p_input;
432     playlist_t *     p_playlist;
433
434     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
435
436     if( !p_input )
437     {
438         return VLC_ENOOBJ;
439     }
440
441     /* Parse commands that only require an input */
442     if( !strcmp( psz_cmd, "pause" ) )
443     {
444         input_SetStatus( p_input, INPUT_STATUS_PAUSE );
445         vlc_object_release( p_input );
446         return VLC_SUCCESS;
447     }
448
449     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
450                                            FIND_PARENT );
451     vlc_object_release( p_input );
452
453     if( !p_playlist )
454     {
455         return VLC_ENOOBJ;
456     }
457
458     /* Parse commands that require a playlist */
459     if( !strcmp( psz_cmd, "prev" ) )
460     {
461         playlist_Prev( p_playlist );
462     }
463     else if( !strcmp( psz_cmd, "next" ) )
464     {
465         playlist_Next( p_playlist );
466     }
467     else if( !strcmp( psz_cmd, "play" ) )
468     {
469         playlist_Play( p_playlist );
470     }
471     else if( !strcmp( psz_cmd, "stop" ) )
472     {
473         playlist_Stop( p_playlist );
474     }
475
476     return VLC_SUCCESS;
477 }
478
479 static int Quit( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
480 {
481     p_this->p_vlc->b_die = VLC_TRUE;
482     return VLC_SUCCESS;
483 }
484
485 static int Intf( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
486 {
487     intf_thread_t *p_newintf;
488     char *psz_oldmodule = config_GetPsz( p_this->p_vlc, "intf" );
489
490     config_PutPsz( p_this->p_vlc, "intf", psz_arg );
491     p_newintf = intf_Create( p_this->p_vlc );
492     config_PutPsz( p_this->p_vlc, "intf", psz_oldmodule );
493
494     if( psz_oldmodule )
495     {
496         free( psz_oldmodule );
497     }
498
499     if( p_newintf )
500     {
501         p_newintf->b_block = VLC_FALSE;
502         if( intf_RunThread( p_newintf ) )
503         {
504             vlc_object_detach( p_newintf );
505             intf_Destroy( p_newintf );
506         }
507     }
508
509     return VLC_SUCCESS;
510 }
511
512 static int Signal( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
513 {
514     raise( atoi(psz_arg) );
515     return VLC_SUCCESS;
516 }
517
518 static int Volume( vlc_object_t *p_this, char *psz_cmd, char *psz_arg )
519 {
520     aout_instance_t * p_aout;
521     int i_error;
522     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
523     if ( p_aout == NULL ) return VLC_ENOOBJ;
524
525     if ( *psz_arg )
526     {
527         /* Set. */
528         audio_volume_t i_volume = atoi( psz_arg );
529         if ( i_volume > AOUT_VOLUME_MAX )
530         {
531             printf( "Volume must be in the range %d-%d\n", AOUT_VOLUME_MIN,
532                     AOUT_VOLUME_MAX );
533             i_error = VLC_EBADVAR;
534         }
535         else i_error = aout_VolumeSet( p_aout, i_volume );
536     }
537     else
538     {
539         /* Get. */
540         audio_volume_t i_volume;
541         if ( aout_VolumeGet( p_aout, &i_volume ) < 0 )
542         {
543             i_error = VLC_EGENERIC;
544         }
545         else
546         {
547             printf( "Volume is %d\n", i_volume );
548             i_error = VLC_SUCCESS;
549         }
550     }
551     vlc_object_release( (vlc_object_t *)p_aout );
552
553     return i_error;
554 }
555
556 static int VolumeMove( vlc_object_t * p_this, char * psz_cmd, char * psz_arg )
557 {
558     aout_instance_t * p_aout;
559     audio_volume_t i_volume;
560     int i_nb_steps = atoi(psz_arg);
561     int i_error = VLC_SUCCESS;
562
563     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/AOUT_VOLUME_STEP) )
564     {
565         i_nb_steps = 1;
566     }
567
568     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
569     if ( p_aout == NULL ) return VLC_ENOOBJ;
570
571     if ( !strcmp(psz_cmd, "volup") )
572     {
573         if ( aout_VolumeUp( p_aout, i_nb_steps, &i_volume ) < 0 )
574             i_error = VLC_EGENERIC;
575     }
576     else
577     {
578         if ( aout_VolumeDown( p_aout, i_nb_steps, &i_volume ) < 0 )
579             i_error = VLC_EGENERIC;
580     }
581     vlc_object_release( (vlc_object_t *)p_aout );
582
583     if ( !i_error ) printf( "Volume is %d\n", i_volume );
584     return i_error;
585 }
586
587 static int AudioConfig( vlc_object_t * p_this, char * psz_cmd, char * psz_arg )
588 {
589     aout_instance_t * p_aout;
590     const char * psz_variable;
591     const char * psz_name;
592     int i_error;
593
594     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
595     if ( p_aout == NULL ) return VLC_ENOOBJ;
596
597     if ( !strcmp( psz_cmd, "adev" ) )
598     {
599         psz_variable = "audio-device";
600         psz_name = "audio devices";
601     }
602     else
603     {
604         psz_variable = "audio-channels";
605         psz_name = "audio channels";
606     }
607
608     if ( !*psz_arg )
609     {
610         /* Retrieve all registered ***. */
611         vlc_value_t val;
612         int i, i_vals;
613         vlc_value_t * p_vals;
614         char * psz_value;
615
616         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
617         {
618             vlc_object_release( (vlc_object_t *)p_aout );
619             return VLC_EGENERIC;
620         }
621         psz_value = val.psz_string;
622
623         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
624                          VLC_VAR_GETLIST, &val ) < 0 )
625         {
626             free( psz_value );
627             vlc_object_release( (vlc_object_t *)p_aout );
628             return VLC_EGENERIC;
629         }
630
631         printf( "+----[ %s ]\n", psz_name );
632         i_vals = ((vlc_value_t *)val.p_address)[0].i_int;
633         p_vals = &((vlc_value_t *)val.p_address)[1]; /* Starts at index 1 */
634         for ( i = 0; i < i_vals; i++ )
635         {
636             if ( !strcmp( psz_value, p_vals[i].psz_string ) )
637                 printf( "| %s *\n", p_vals[i].psz_string );
638             else
639                 printf( "| %s\n", p_vals[i].psz_string );
640         }
641         var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_FREELIST,
642                     &val );
643         printf( "+----[ end of %s ]\n", psz_name );
644
645         free( psz_value );
646         i_error = VLC_SUCCESS;
647     }
648     else
649     {
650         vlc_value_t val;
651         val.psz_string = psz_arg;
652
653         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
654     }
655     vlc_object_release( (vlc_object_t *)p_aout );
656
657     return i_error;
658 }
659