]> git.sesse.net Git - vlc/blob - src/interface/intf_ctrl.c
6ac632a02b70795fa3e57c0c1f86353e8adc1a87
[vlc] / src / interface / intf_ctrl.c
1 /*******************************************************************************
2  * intf_ctrl.c: interface commands access to control functions
3  * (c)1999 VideoLAN
4  *******************************************************************************
5  * Library of functions common to all interfaces, allowing access to various
6  * structures and settings. Interfaces should only use those functions
7  * to read or write informations from other threads.
8  * A control function must be declared in the `local prototypes' section (it's
9  * type is fixed), and copied into the control_command array. Functions should
10  * be listed in alphabetical order, so when `help' is called they are also
11  * displayed in this order.
12  * A control function can use any function of the program, but should respect
13  * two points: first, it should not block, since if it does so, the whole
14  * interface thread will hang and in particular miscelannous interface events
15  * won't be handled. Secondly, it should send it's output messages exclusively
16  * with intf_IntfMsg() function, except particularly critical messages which
17  * can use over intf_*Msg() functions.
18  * Control functions should return 0 (INTF_NO_ERROR) on success, or one of the
19  * error codes defined in command.h. Custom error codes are allowed, but should
20  * be positive.
21  * More informations about parameters stand in `list of commands' section.
22  *******************************************************************************/
23
24 /*******************************************************************************
25  * Preamble
26  *******************************************************************************/
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <netinet/in.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/soundcard.h>
35 #include <sys/stat.h>
36 #include <sys/uio.h>
37 #include <X11/Xlib.h>
38 #include <X11/extensions/XShm.h>
39
40 #include "config.h"
41 #include "common.h"
42 #include "mtime.h"
43 #include "vlc_thread.h"
44 #include "debug.h"
45
46 #include "intf_msg.h"
47
48 #include "input.h"
49 #include "input_ctrl.h"
50 #include "input_vlan.h"
51 #include "input_psi.h"
52 #include "input_netlist.h"
53 #include "decoder_fifo.h"
54
55 #include "audio_output.h"
56 #include "audio_decoder.h"
57
58 #include "video.h"
59 #include "video_output.h"
60 #include "video_graphics.h"
61 #include "video_decoder.h"
62
63 #include "xconsole.h"
64 #include "interface.h"
65 #include "intf_cmd.h"
66 #include "control.h"
67 #include "intf_ctrl.h"
68
69 #include "pgm_data.h"
70
71 /*
72  * Local prototypes
73  */
74 static int Demo                 ( int i_argc, intf_arg_t *p_argv );
75 static int DisplayImage         ( int i_argc, intf_arg_t *p_argv );
76 static int Exec                 ( int i_argc, intf_arg_t *p_argv );
77 static int Help                 ( int i_argc, intf_arg_t *p_argv );
78 static int PlayAudio            ( int i_argc, intf_arg_t *p_argv );
79 static int PlayVideo            ( int i_argc, intf_arg_t *p_argv );
80 static int Quit                 ( int i_argc, intf_arg_t *p_argv );
81 static int SelectPID            ( int i_argc, intf_arg_t *p_argv );
82 static int SpawnInput           ( int i_argc, intf_arg_t *p_argv );
83 #ifdef DEBUG
84 static int Test                 ( int i_argc, intf_arg_t *p_argv );
85 #endif
86 static int Vlan                 ( int i_argc, intf_arg_t *p_argv );
87 static int Psi                  ( int i_argc, intf_arg_t *p_argv );
88
89 /*
90  * List of commands.
91  * This list is used by intf_ExecCommand function to find functions to
92  * execute and prepare its arguments. It is terminated by an element  which name 
93  * is a null pointer. intf_command_t is defined in command.h.
94  *
95  * Here is a description of a command description elements:
96  *  name is the name of the function, as it should be typed on command line,
97  *  function is a pointer to the control function,
98  *  format is an argument descriptor (see below),
99  *  summary is a text string displayed in regard of the command name when `help'
100  *      is called without parameters, and whith usage on syntax error,
101  *  usage is a short syntax indicator displayed with summary when the command 
102  *      causes a syntax error,
103  *  help is a complete help about the command, displayed when `help' is called with
104  *      the command name as parameter.
105  *
106  * Format string is a list of ' ' separated strings, which have following 
107  * meanings:
108  *  s       string argument
109  *  i       integer argument
110  *  f       float argument
111  *  ?       optionnal argument
112  *  *       argument can be repeated
113  *  name=   named argument
114  * Example: "channel=i? s*? i " means that any number of string arguments, 
115  * followed by a single mandatory integer argument are waited. A named argument,
116  * which name is `channel' and must have an integer value can be optionnaly
117  * specified at beginning. The last space is mandatory if there is at least one
118  * element, since it acts as an item terminator.
119  * Named arguments MUST be at the beginning of the format string, and in
120  * alphabetic order, but their order on command line has no importance. 
121  * The format string can't have more than INTF_MAX_ARGS elements.
122  */
123 const intf_command_t control_command[] =
124 {
125   { "demo", Demo,                                                      /* demo */
126     /* format: */   "", 
127     /* summary: */  "program demonstration", 
128     /* usage: */    "demo", 
129     /* help: */     "Start program capabilities demonstration." },
130   { "display", DisplayImage,                                        /* display */
131     /* format: */   "s ",
132     /* summary: */  "load and display an image",
133     /* usage: */    "display <file>",
134     /* help: */     "Load and display an image. Image format is automatically " \
135     "identified from file name extension." },
136   { "exec", Exec,                                                      /* exec */
137     /* format: */   "s ",
138     /* summary: */  "execute a script file",
139     /* usage: */    "exec <file>",
140     /* help: */     "Load an execute a script." },      
141   { "exit", Quit,                                         /* exit (quit alias) */
142     /* format: */   "",
143     /* summary: */  "quit program",
144     /* usage: */    "exit",
145     /* help: */     "see `quit'." },
146   { "help", Help,                                                      /* help */
147     /* format: */   "s? ", 
148     /* summary: */  "list all functions or print help about a specific function", 
149     /* usage: */    "help [command]", 
150     /* help: */     "If called without argument, list all available " \
151     " functions.\nIf a command name is provided as argument, displays a short " \
152     "inline help about the command.\n" },
153   { "play-audio", PlayAudio,                                     /* play-audio */
154     /* format: */   "stereo=i? rate=i? s ", 
155     /* summary: */  "play an audio file",
156     /* usage: */    "play-audio [stereo=1/0] [rate=r] <file>",
157     /* help: */     "Load and play an audio file." },
158   { "play-video", PlayVideo,                                      /* play-video */
159     /* format: */   "s ", 
160     /* summary: */  "play a video (.vlp) file",
161     /* usage: */    "play-video <file>",
162     /* help: */     "Load and play a video file." },
163   { "quit", Quit,                                                      /* quit */
164     /* format: */   "",
165     /* summary: */  "quit program",
166     /* usage: */    "quit",
167     /* help: */     "Terminates the program execution... There is not much to" \
168     " say about it !" },
169   { "select-pid", SelectPID,                                     /* select-pid */
170     /* format: */   "i i ",
171     /* summary: */  "spawn a decoder thread for a specified PID",
172     /* summary: */  "select-pid <input> <pid>",
173     /* help: */     "Spawn a decoder thread for <pid>. The stream will be" \
174     " received by <input>." },
175   { "spawn-input", SpawnInput,                                  /* spawn-input */
176     /* format: */   "method=i? filename=s? hostname=s? ip=s? port=i? vlan=i?",
177     /* summary: */  "spawn an input thread",
178     /* summary: */  "spawn-input [method=<method>]\n" \
179     "[filename=<file>|hostname=<hostname>|ip=<ip>]\n" \
180     "[port=<port>] [vlan=<vlan>]",
181     /* help: */     "Spawn an input thread. Method is 10, 20, 21, 22, 32, "\
182     "hostname is the fully-qualified domain name, ip is a dotted-decimal address." },
183 #ifdef DEBUG
184   { "test", Test,                                                      /* test */
185     /* format: */   "i? ",
186     /* summary: */  "crazy developper's test",
187     /* usage: */    "depends on the last coder :-)",
188     /* help: */     "`test' works only in DEBUG mode, and is provide for " \
189     "developpers as an easy way to test part of their code. If you don't know " \
190     "what it should do, just try !" },
191 #endif
192   { "vlan", Vlan,
193     /* format: */   "intf=s? s i? ",
194     /* summary: */  "vlan operations",
195     /* usage: */    "vlan synchro\n" \
196     "vlan [intf=<interface>] request\n" \
197     "vlan [intf=<interface>] join <vlan>\n" \
198     "vlan [intf=<interface>] leave"
199     /* help: */     "Perform various operations on vlans. 'synchro' resynchronize " \
200     "with the server. 'request' ask which is the current vlan (for the default " \
201     "interface or for a given one). 'join' and 'leave' try to change vlan." },
202   { "psi", Psi,
203     /* format: */   "i ",
204     /* summary: */  "Dump PSI tables",
205     /* usage: */    "psi <input thread index>",
206     /* help: */     "Display the PSI tables on the console. Warning: this is debug" \
207     "command, it can leads to pb since locks are not taken yet" },
208   { 0, 0, 0, 0, 0 }                                        /* array terminator */
209 };
210
211 /* following functions are local */
212
213 /*******************************************************************************
214  * Demo: demo
215  *******************************************************************************
216  * This function is provided to display a demo of program possibilities. 
217  *******************************************************************************/
218 static int Demo( int i_argc, intf_arg_t *p_argv )
219 {
220     intf_IntfMsg( COPYRIGHT_MESSAGE );
221
222     return( INTF_NO_ERROR );
223 }
224
225 /*******************************************************************************
226  * Exec: execute a script
227  *******************************************************************************
228  * This function load and execute a script.
229  *******************************************************************************/
230 static int Exec( int i_argc, intf_arg_t *p_argv )
231 {
232     int i_err;                                                   /* error code */
233     
234     i_err = intf_ExecScript( p_argv[1].psz_str );
235     return( i_err ? INTF_OTHER_ERROR : INTF_NO_ERROR );     
236 }
237
238 /*******************************************************************************
239  * DisplayImage: load and display an image                               (ok ?)
240  *******************************************************************************
241  * Try to load an image identified by it's filename and displays it as a still
242  * image using interface video heap.
243  *******************************************************************************/
244 static int DisplayImage( int i_argc, intf_arg_t *p_argv )
245 {
246     /* ?? */
247     return( INTF_NO_ERROR );
248 }
249
250 /*******************************************************************************
251  * Help: list all available commands                                     (ok ?)
252  *******************************************************************************
253  * This function print a list of available commands
254  *******************************************************************************/
255 static int Help( int i_argc, intf_arg_t *p_argv )
256 {
257     int     i_index;                                          /* command index */
258
259     /* If called with an argument: look for the command and display it's help */
260     if( i_argc == 2 )
261     {
262         for( i_index = 0; control_command[i_index].psz_name 
263                  && strcmp( control_command[i_index].psz_name, p_argv[1].psz_str );
264              i_index++ )
265         {
266             ;
267         }
268         /* Command has been found in list */
269         if( control_command[i_index].psz_name )
270         {
271             intf_IntfMsg( control_command[i_index].psz_usage );
272             intf_IntfMsg( control_command[i_index].psz_help );
273         }
274         /* Command is unknown */
275         else
276         {
277             intf_IntfMsg("help: don't know command `%s'", p_argv[1].psz_str);
278             return( INTF_OTHER_ERROR );
279         }
280     }
281     /* If called without argument: print all commands help field */
282     else
283     {
284         for( i_index = 0; control_command[i_index].psz_name; i_index++ )
285         {
286             intf_IntfMsg( "%s: %s",  control_command[i_index].psz_name, 
287                           control_command[i_index].psz_summary );
288         }
289     }
290
291     return( INTF_NO_ERROR );
292 }
293
294 /******************************************************************************
295  * PlayAudio: play an audio file                                         (ok ?)
296  ******************************************************************************
297  * Play a raw audio file from a file, at a given rate.
298  ******************************************************************************/
299 static int PlayAudio( int i_argc, intf_arg_t *p_argv )
300 {
301     char *psz_file;                       /* name of the audio raw file (s16) */
302     int i_fd;       /* file descriptor of the audio file that is to be loaded */
303     aout_fifo_t fifo;          /* fifo stores the informations about the file */
304     struct stat stat_buffer;       /* needed to find out the size of psz_file */
305     int i_arg;                                              /* argument index */
306
307     if ( !p_program_data->cfg.b_audio )                  /* audio is disabled */
308     {
309         intf_IntfMsg("play-audio error: audio is disabled");
310         return( INTF_NO_ERROR );
311     }
312
313     /* Set default configuration */
314     fifo.b_stereo = AOUT_DEFAULT_STEREO;
315     fifo.l_rate = AOUT_DEFAULT_RATE;
316
317     /* The stereo and rate parameters are essential ! */
318     /* Parse parameters - see command list above */
319     for ( i_arg = 1; i_arg < i_argc; i_arg++ )
320     {
321         switch( p_argv[i_arg].i_index )
322         {
323         case 0:                                                     /* stereo */
324             fifo.b_stereo = p_argv[i_arg].i_num;
325             break;
326         case 1:                                                       /* rate */
327             fifo.l_rate = p_argv[i_arg].i_num;
328             break;
329         case 2:                                                   /* filename */
330             psz_file = p_argv[i_arg].psz_str;
331             break;
332         }
333     }
334
335     /* Setting up the type of the fifo */
336     switch ( fifo.b_stereo )
337     {
338         case 0:
339             fifo.i_type = AOUT_INTF_MONO_FIFO;
340             break;
341
342         case 1:
343             fifo.i_type = AOUT_INTF_STEREO_FIFO;
344             break;
345
346         default:
347             intf_IntfMsg("play-audio error: stereo must be 0 or 1");
348             return( INTF_OTHER_ERROR );
349     }
350
351     /* Open file */
352     i_fd =  open( psz_file, O_RDONLY );
353     if ( i_fd < 0 )                                                  /* error */
354     {
355         intf_IntfMsg("play-audio error: can't open `%s'", psz_file);
356         return( INTF_OTHER_ERROR );
357     }
358
359     /* Get file size to calculate number of audio units */
360     fstat( i_fd, &stat_buffer );
361     fifo.l_units = ( long )( stat_buffer.st_size / (sizeof(s16) << fifo.b_stereo) );
362
363     /* Allocate memory, read file and close it */
364     if ( (fifo.buffer = malloc(sizeof(s16)*(fifo.l_units << fifo.b_stereo))) == NULL ) /* !! */
365     {
366         intf_IntfMsg("play-audio error: not enough memory to read `%s'", psz_file );
367         close( i_fd );                                          /* close file */
368         return( INTF_OTHER_ERROR );
369     }
370     if ( read(i_fd, fifo.buffer, sizeof(s16)*(fifo.l_units << fifo.b_stereo))
371         != sizeof(s16)*(fifo.l_units << fifo.b_stereo) )
372     {
373         intf_IntfMsg("play-audio error: can't read %s", psz_file);
374         free( fifo.buffer );
375         close( i_fd );
376         return( INTF_OTHER_ERROR );
377     }
378     close( i_fd );
379
380     /* Now we can work out how many output units we can compute with the fifo */
381     fifo.l_units = (long)(((s64)fifo.l_units*(s64)p_program_data->aout_thread.dsp.l_rate)/(s64)fifo.l_rate);
382
383     /* Create the fifo */
384     if ( aout_CreateFifo(&p_program_data->aout_thread, &fifo) == NULL )
385     {
386         intf_IntfMsg("play-audio error: can't create audio fifo");
387         free( fifo.buffer );
388         return( INTF_OTHER_ERROR );
389     }
390
391     return( INTF_NO_ERROR );
392 }
393
394 /*******************************************************************************
395  * PlayVideo: play a video sequence from a file
396  *******************************************************************************
397  * ??
398  *******************************************************************************/
399 static int PlayVideo( int i_argc, intf_arg_t *p_argv )
400 {
401     /* ?? */
402     return( INTF_NO_ERROR );    
403 }
404
405 /*******************************************************************************
406  * Quit: quit program                                                    (ok ?)
407  *******************************************************************************
408  * This function set `die' flag of interface, asking the program to terminate.
409  *******************************************************************************/
410 static int Quit( int i_argc, intf_arg_t *p_argv )
411 {
412     p_program_data->intf_thread.b_die = 1;
413     return( INTF_NO_ERROR );
414 }
415
416
417 /******************************************************************************
418  *
419  ******************************************************************************
420  *
421  ******************************************************************************/
422 static int SelectPID( int i_argc, intf_arg_t *p_argv )
423 {
424     int i_input, i_pid;
425     int i_arg;
426   
427     /* Parse parameters - see command list above */
428     for ( i_arg = 1; i_arg < i_argc; i_arg++ )
429     {
430       switch( p_argv[i_arg].i_index )
431       {
432       case 0:
433           i_input = p_argv[i_arg].i_num;
434           break;
435       case 1:
436          i_pid = p_argv[i_arg].i_num;
437         break;
438       }
439     }
440
441
442     /* Find to which input this command is destinated */
443     if(i_input < INPUT_MAX_THREADS )
444     {
445         if( p_program_data->intf_thread.pp_input[i_input] )
446         {
447             intf_IntfMsg( "Adding PID %d to input %d\n", i_pid, i_input );
448             input_AddPgrmElem( p_program_data->intf_thread.pp_input[i_input],
449                                i_pid );
450             return( INTF_NO_ERROR );
451         }
452     }      
453
454     /* No such input was created */
455     intf_IntfMsg("No such input thread is currently running: %d\n", i_input);
456     return(  INTF_OTHER_ERROR );
457 }
458
459
460 /******************************************************************************
461  * SpawnInput: spawn an input thread                                     (ok ?)
462  ******************************************************************************
463  * Spawn an input thread with the correct p_cfg parameters.
464  ******************************************************************************/
465 static int SpawnInput( int i_argc, intf_arg_t *p_argv )
466 {
467     input_cfg_t         cfg;
468     int                 i_arg;
469     
470     /* Erase p_cfg. */
471     bzero( &cfg, sizeof( cfg ) );
472
473     /* Parse parameters - see command list above */
474     for ( i_arg = 1; i_arg < i_argc; i_arg++ )
475     {
476         switch( p_argv[i_arg].i_index )
477         {
478         case 0:                                                     /* method */
479             cfg.i_method = p_argv[i_arg].i_num;
480             break;
481         case 1:                                                   /* filename */
482             cfg.psz_filename = p_argv[i_arg].psz_str;
483             break;
484         case 2:                                                   /* hostname */
485             cfg.psz_hostname = p_argv[i_arg].psz_str;
486             break;
487         case 3:                                                         /* ip */
488             cfg.psz_ip = p_argv[i_arg].psz_str;
489             break;
490         case 4:                                                       /* port */
491             cfg.i_port = p_argv[i_arg].i_num;
492             break;
493         case 5:                                                       /* VLAN */
494             cfg.i_vlan = p_argv[i_arg].i_num;
495             break;
496         }
497     }
498
499     /* Setting i_properties to indicate which parameters are set. */
500     if( cfg.i_method )
501     {
502         cfg.i_properties |= INPUT_CFG_METHOD;
503     }
504     if( cfg.psz_filename )
505     {
506         cfg.i_properties |= INPUT_CFG_FILENAME;
507     }
508     if( cfg.psz_hostname )
509     {
510         cfg.i_properties |= INPUT_CFG_HOSTNAME;
511     }
512     if( cfg.psz_ip )
513     {
514         cfg.i_properties |= INPUT_CFG_IP;
515     }
516     if( cfg.i_port )
517     {
518         cfg.i_properties |= INPUT_CFG_PORT;
519     }
520     if( cfg.i_vlan )
521     {
522         cfg.i_properties |= INPUT_CFG_VLAN;
523     }
524
525     /* Default settings for the decoder threads */
526     cfg.p_aout = p_program_data->intf_thread.p_aout;
527
528     /* Create the input thread */
529     if( intf_CreateInputThread( &p_program_data->intf_thread, &cfg ) == -1)
530     {
531         return( INTF_OTHER_ERROR );
532     }
533
534     return( INTF_NO_ERROR );
535 }
536
537 /*******************************************************************************
538  * Test: test function
539  *******************************************************************************
540  * This function is provided to test new functions in the program. Fell free
541  * to modify ! 
542  * This function is only defined in DEBUG mode.
543  *******************************************************************************/
544 #ifdef DEBUG
545 static int Test( int i_argc, intf_arg_t *p_argv )
546 {
547     int i_thread;    
548
549     if( i_argc == 1 )
550     {
551         i_thread = intf_CreateVoutThread( &p_program_data->intf_thread, NULL, -1, -1);
552         intf_IntfMsg("return value: %d", i_thread );        
553     }
554     else
555     {
556         i_thread = p_argv[1].i_num;        
557         intf_DestroyVoutThread( &p_program_data->intf_thread, i_thread );        
558     }    
559
560     return( INTF_NO_ERROR );
561 }
562 #endif
563
564 /*******************************************************************************
565  * Vlan: vlan operations
566  *******************************************************************************
567  * This function performs various vlan operations.
568  *******************************************************************************/
569 static int Vlan( int i_argc, intf_arg_t *p_argv  )
570 {
571     int i_command;                                  /* command argument number */
572
573     /* Do not try anything if vlans are desactivated */
574     if( !p_program_data->cfg.b_vlans )
575     {
576         intf_IntfMsg("vlans are desactivated");
577         return( INTF_OTHER_ERROR );
578     }
579     
580     /* Look for command in list of arguments - this argument is mandatory and
581      * imposed by the calling function */
582     for( i_command = 1; p_argv[i_command].i_index == 1; i_command++ )
583     {
584         ;        
585     }
586     
587     /* Command is 'synchro' */
588     if( !strcmp(p_argv[i_command].psz_str, "synchro") )
589     {
590         input_VlanSynchronize();
591     }
592     /* Command is 'request' */
593     else if( !strcmp(p_argv[i_command].psz_str, "request") )
594     {
595         /* ?? */
596     }
597     /* Command is 'join' */
598     else if( !strcmp(p_argv[i_command].psz_str, "join") )
599     {
600         /* ?? */
601     }    
602     /* Command is 'leave' */
603     else if( !strcmp(p_argv[i_command].psz_str, "leave") )
604     {
605         /* ?? */
606     }
607     /* Command is unknown */
608     else
609     {
610         intf_IntfMsg("vlan error: unknown command %s", p_argv[i_command].psz_str );
611         return( INTF_USAGE_ERROR );        
612     }
613          
614     return( INTF_NO_ERROR );
615 }
616
617
618 /*******************************************************************************
619  * Psi
620  *******************************************************************************
621  * This function is provided to display PSI tables. 
622  *******************************************************************************/
623 static int Psi( int i_argc, intf_arg_t *p_argv )
624 {
625     int i_index = p_argv[1].i_num;
626   
627     if(i_index < INPUT_MAX_THREADS )
628     {
629       if(p_program_data->intf_thread.pp_input[i_index])
630       {
631         /* Read the Psi table for that thread */
632         intf_IntfMsg("Reading PSI table for input %d\n", i_index);
633         input_PsiRead(p_program_data->intf_thread.pp_input[i_index]);
634         return( INTF_NO_ERROR );
635       }
636     }      
637
638     /* No such input was created */
639     intf_IntfMsg("No such input thread is currently running: %d\n", i_index);
640     
641     return(  INTF_OTHER_ERROR );
642 }