]> git.sesse.net Git - vlc/blob - src/interface/interface.c
. SPU decoder now uses Meuuh's GetChunk() code.
[vlc] / src / interface / interface.c
1 /*****************************************************************************
2  * interface.c: interface access for other threads
3  * This library provides basic functions for threads to interact with user
4  * interface, such as command line.
5  *****************************************************************************
6  * Copyright (C) 1998, 1999, 2000 VideoLAN
7  *
8  * Authors:
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                      /* free(), strtol() */
32 #include <stdio.h>                                                   /* FILE */
33 #include <string.h>                                            /* strerror() */
34
35 #include "config.h"
36 #include "common.h"
37 #include "threads.h"
38 #include "mtime.h"
39 #include "plugins.h"
40 #include "playlist.h"
41
42 #include "stream_control.h"
43 #include "input_ext-intf.h"
44
45 #include "audio_output.h"
46
47 #include "intf_msg.h"
48 #include "interface.h"
49 #include "intf_cmd.h"
50 #include "intf_console.h"
51 #include "keystrokes.h"
52
53 #include "video.h"
54 #include "video_output.h"
55
56 #include "main.h"
57
58 /*****************************************************************************
59  * intf_channel_t: channel description
60  *****************************************************************************
61  * A 'channel' is a descriptor of an input method. It is used to switch easily
62  * from source to source without having to specify the whole input thread
63  * configuration. The channels array, stored in the interface thread object, is
64  * loaded in intf_Create, and unloaded in intf_Destroy.
65  *****************************************************************************/
66 typedef struct intf_channel_s
67 {
68     /* Channel description */
69     int         i_channel;            /* channel number, -1 for end of array */
70     char *      psz_description;              /* channel description (owned) */
71
72     /* Input configuration */
73     int         i_input_method;                   /* input method descriptor */
74     char *      psz_input_source;                   /* source string (owned) */
75     int         i_input_port;                                        /* port */
76     int         i_input_vlan_id;                                  /* vlan id */
77 } intf_channel_t;
78
79 /*****************************************************************************
80  * Local prototypes
81  *****************************************************************************/
82 static int      LoadChannels    ( intf_thread_t *p_intf, char *psz_filename );
83 static void     UnloadChannels  ( intf_thread_t *p_intf );
84 static int      ParseChannel    ( intf_channel_t *p_channel, char *psz_str );
85
86 /*****************************************************************************
87  * intf_Create: prepare interface before main loop
88  *****************************************************************************
89  * This function opens output devices and creates specific interfaces. It sends
90  * its own error messages.
91  *****************************************************************************/
92 intf_thread_t* intf_Create( void )
93 {
94     intf_thread_t * p_intf;
95     typedef void    ( intf_getplugin_t ) ( intf_thread_t * p_intf );
96     int             i_index;
97     int             i_best_index = 0, i_best_score = 0;
98
99     /* Allocate structure */
100     p_intf = malloc( sizeof( intf_thread_t ) );
101     if( !p_intf )
102     {
103         intf_ErrMsg("error: %s", strerror( ENOMEM ) );
104         return( NULL );
105     }
106
107     /* Get a suitable interface plugin */
108     for( i_index = 0 ; i_index < p_main->p_bank->i_plugin_count ; i_index++ )
109     {
110         /* If there's a plugin in p_info ... */
111         if( p_main->p_bank->p_info[ i_index ] != NULL )
112         {
113             /* ... and if this plugin provides the functions we want ... */
114             if( p_main->p_bank->p_info[ i_index ]->intf_GetPlugin != NULL )
115             {
116                 /* ... and if this plugin has a good score ... */
117                 if( p_main->p_bank->p_info[ i_index ]->i_score > i_best_score )
118                 {
119                     /* ... then take it */
120                     i_best_score = p_main->p_bank->p_info[ i_index ]->i_score;
121                     i_best_index = i_index;
122                 }
123             }
124         }
125     }
126
127     if( i_best_score == 0 )
128     {
129         free( p_intf );
130         intf_ErrMsg( "error: no suitable plugin to create interface" );
131         return( NULL );
132     }
133
134     /* Get the plugin functions */
135     ( (intf_getplugin_t *)
136       p_main->p_bank->p_info[ i_best_index ]->intf_GetPlugin )( p_intf );
137
138     /* Initialize structure */
139     p_intf->b_die =     0;
140     p_intf->p_vout =    NULL;
141     p_intf->p_input =   NULL;
142     p_intf->p_keys =    NULL;
143
144     /* Warning level initialisation */
145     p_intf->i_warning_level = main_GetIntVariable( INTF_WARNING_VAR, INTF_WARNING_DEFAULT );
146     
147     /* Load channels - the pointer will be set to NULL on failure. The
148      * return value is ignored since the program can work without
149      * channels */
150     LoadChannels( p_intf, main_GetPszVariable( INTF_CHANNELS_VAR, INTF_CHANNELS_DEFAULT ));
151
152     /* Start interfaces */
153     p_intf->p_console = intf_ConsoleCreate();
154     if( p_intf->p_console == NULL )
155     {
156         intf_ErrMsg("error: can't create control console");
157         free( p_intf );
158         return( NULL );
159     }
160     if( p_intf->p_sys_create( p_intf ) )
161     {
162         intf_ErrMsg("error: can't create interface");
163         intf_ConsoleDestroy( p_intf->p_console );
164         free( p_intf );
165         return( NULL );
166     }
167
168     intf_Msg("Interface initialized");
169     return( p_intf );
170 }
171
172 /*****************************************************************************
173  * intf_Run
174  *****************************************************************************
175  * Initialization script and main interface loop.
176  *****************************************************************************/
177 void intf_Run( intf_thread_t *p_intf )
178 {
179     char * psz_server = main_GetPszVariable( INPUT_SERVER_VAR, NULL );
180     input_config_t *    p_input_config;
181
182     /* Flush messages before spawning input */
183     intf_FlushMsg();
184
185     /* If a server was specified */
186     if( psz_server )
187     {
188         if( (p_input_config =
189               (input_config_t *)malloc( sizeof(input_config_t) )) == NULL )
190         {
191             intf_ErrMsg("Out of memory");
192         }
193         else
194         {
195             p_input_config->i_method = INPUT_METHOD_UCAST;
196             p_input_config->p_source = psz_server;
197             p_input_config->p_default_aout = p_main->p_aout;
198             p_input_config->p_default_vout = p_intf->p_vout;
199
200             p_intf->p_input = input_CreateThread( p_input_config, NULL );
201         }
202     }
203     /* Or if a file was specified */
204     else if( p_main->p_playlist->p_list != NULL )
205     {
206         if( (p_input_config =
207               (input_config_t *)malloc( sizeof(input_config_t) )) == NULL )
208         {
209             intf_ErrMsg("Out of memory");
210         }
211         else
212         {
213             p_input_config->i_method = INPUT_METHOD_FILE;
214             p_input_config->p_source = p_main->p_playlist->p_list[0]; /* FIXME ??? */
215             p_input_config->p_default_aout = p_main->p_aout;
216             p_input_config->p_default_vout = p_intf->p_vout;
217
218             p_intf->p_input = input_CreateThread( p_input_config, NULL );
219         }
220     }
221     /* Execute the initialization script - if a positive number is returned,
222      * the script could be executed but failed */
223     else if( intf_ExecScript( main_GetPszVariable( INTF_INIT_SCRIPT_VAR, INTF_INIT_SCRIPT_DEFAULT ) ) > 0 )
224     {
225         intf_ErrMsg("warning: error(s) during startup script");
226     }
227
228     /* Main loop */
229     while(!p_intf->b_die)
230     {
231         /* Flush waiting messages */
232         intf_FlushMsg();
233
234         /* Manage specific interface */
235         p_intf->p_sys_manage( p_intf );
236
237         /* Check attached threads status */
238         if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_error )
239         {
240             /* FIXME: add aout error detection ?? */
241             p_intf->b_die = 1;
242         }
243         if( (p_intf->p_input != NULL) && p_intf->p_input->b_error )
244         {
245             input_DestroyThread( p_intf->p_input, NULL );
246             p_intf->p_input = NULL;
247             intf_DbgMsg("Input thread destroyed");
248         }
249
250         /* Sleep to avoid using all CPU - since some interfaces needs to access
251          * keyboard events, a 100ms delay is a good compromise */
252         msleep( INTF_IDLE_SLEEP );
253     }
254 }
255
256 /*****************************************************************************
257  * intf_Destroy: clean interface after main loop
258  *****************************************************************************
259  * This function destroys specific interfaces and close output devices.
260  *****************************************************************************/
261 void intf_Destroy( intf_thread_t *p_intf )
262 {
263     p_intf_key  p_cur;
264     p_intf_key  p_next;
265     /* Destroy interfaces */
266     p_intf->p_sys_destroy( p_intf );
267     intf_ConsoleDestroy( p_intf->p_console );
268
269     /* Unload channels */
270     UnloadChannels( p_intf );
271
272     /* Destroy keymap */
273     p_cur = p_intf->p_keys;
274     while( p_cur != NULL)
275     {
276         p_next = p_cur->next;
277         free(p_cur);
278         p_cur = p_next;
279     }
280          
281         
282         /* Free structure */
283     free( p_intf );
284 }
285
286 /*****************************************************************************
287  * intf_SelectChannel: change channel
288  *****************************************************************************
289  * Kill existing input, if any, and try to open a new one, using an input
290  * configuration table.
291  *****************************************************************************/
292 int intf_SelectChannel( intf_thread_t * p_intf, int i_channel )
293 {
294     /* FIXME */
295 #if 0
296     intf_channel_t *    p_channel;                                /* channel */
297
298     /* Look for channel in array */
299     if( p_intf->p_channel != NULL )
300     {
301         for( p_channel = p_intf->p_channel; p_channel->i_channel != -1; p_channel++ )
302         {
303             if( p_channel->i_channel == i_channel )
304             {
305             /*
306              * Change channel
307              */
308
309             /* Kill existing input, if any */
310             if( p_intf->p_input != NULL )
311             {
312                 input_DestroyThread( p_intf->p_input, NULL );
313             }
314
315             intf_Msg("Channel %d: %s", i_channel, p_channel->psz_description );
316
317             /* Open a new input */
318             p_intf->p_input = input_CreateThread( p_channel->i_input_method, p_channel->psz_input_source,
319                                   p_channel->i_input_port, p_channel->i_input_vlan_id,
320                                   p_intf->p_vout, p_main->p_aout, NULL );
321             return( p_intf->p_input == NULL );
322             }
323         }
324     }
325
326     /* Channel does not exist */
327     intf_Msg("Channel %d does not exist", i_channel );
328 #endif
329     return( 1 );
330 }
331
332 /*****************************************************************************
333  * intf_AssignKey: assign standartkeys                                       *
334  *****************************************************************************
335  * This function fills in the associative array that links the key pressed   *
336  * and the key we use internally. Support one extra parameter.               *
337  ****************************************************************************/
338 void intf_AssignKey( intf_thread_t *p_intf, int r_key, int f_key, int param)
339 {
340     p_intf_key  p_cur =  p_intf->p_keys;
341     if( p_cur == NULL )
342     {
343         p_cur = (p_intf_key )(malloc ( sizeof( intf_key ) ) );
344         p_cur->received_key = r_key;
345         p_cur->forwarded.key = f_key;
346         p_cur->forwarded.param = param; 
347         p_cur->next = NULL;
348         p_intf->p_keys = p_cur;
349     } 
350     else 
351     {
352         while( p_cur->next != NULL && p_cur ->received_key != r_key)
353         {
354             p_cur = p_cur->next;
355         }
356         if( p_cur->next == NULL )
357         {   
358             p_cur->next  = ( p_intf_key )( malloc( sizeof( intf_key ) ) );
359             p_cur = p_cur->next;
360             p_cur->next = NULL;
361             p_cur->forwarded.param = param; 
362             p_cur->received_key = r_key;
363         }
364         p_cur->forwarded.key = f_key;
365     }        
366 }
367
368 /* Basic getKey function... */
369 keyparm intf_GetKey( intf_thread_t *p_intf, int r_key)
370 {   
371     keyparm reply;
372     
373     p_intf_key current = p_intf->p_keys;
374     while(current != NULL && current->received_key != r_key)
375     {    
376         current = current->next;
377     }
378     if(current == NULL)
379     {   /* didn't find any key in the array */ 
380         reply.key = INTF_KEY_UNKNOWN;
381         reply.param = 0;
382     }
383     else
384     {
385         reply.key = current->forwarded.key;
386         reply.param = current->forwarded.param;
387     }
388     return reply;
389 }
390
391 /*****************************************************************************
392 * intf_AssignNormalKeys: used for normal interfaces.
393 *****************************************************************************
394 * This function assign the basic key to the normal keys.
395 *****************************************************************************/
396
397 void intf_AssignNormalKeys( intf_thread_t *p_intf)
398 {
399     p_intf->p_intf_get_key = intf_GetKey;
400
401     intf_AssignKey( p_intf , 'Q', INTF_KEY_QUIT, 0);
402     intf_AssignKey( p_intf , 'q', INTF_KEY_QUIT, 0);
403     intf_AssignKey( p_intf ,  27, INTF_KEY_QUIT, 0);
404     intf_AssignKey( p_intf ,   3, INTF_KEY_QUIT, 0);
405     intf_AssignKey( p_intf , '0', INTF_KEY_SET_CHANNEL, 0);
406     intf_AssignKey( p_intf , '1', INTF_KEY_SET_CHANNEL, 1);
407     intf_AssignKey( p_intf , '2', INTF_KEY_SET_CHANNEL, 2);
408     intf_AssignKey( p_intf , '3', INTF_KEY_SET_CHANNEL, 3);
409     intf_AssignKey( p_intf , '4', INTF_KEY_SET_CHANNEL, 4);
410     intf_AssignKey( p_intf , '5', INTF_KEY_SET_CHANNEL, 5);
411     intf_AssignKey( p_intf , '6', INTF_KEY_SET_CHANNEL, 6);
412     intf_AssignKey( p_intf , '7', INTF_KEY_SET_CHANNEL, 7);
413     intf_AssignKey( p_intf , '8', INTF_KEY_SET_CHANNEL, 8);
414     intf_AssignKey( p_intf , '9', INTF_KEY_SET_CHANNEL, 9);
415     intf_AssignKey( p_intf , '0', INTF_KEY_SET_CHANNEL, 0);
416     intf_AssignKey( p_intf , '+', INTF_KEY_INC_VOLUME, 0);
417     intf_AssignKey( p_intf , '-', INTF_KEY_DEC_VOLUME, 0);
418     intf_AssignKey( p_intf , 'm', INTF_KEY_TOGGLE_VOLUME, 0);
419     intf_AssignKey( p_intf , 'M', INTF_KEY_TOGGLE_VOLUME, 0);
420     intf_AssignKey( p_intf , 'g', INTF_KEY_DEC_GAMMA, 0);
421     intf_AssignKey( p_intf , 'G', INTF_KEY_INC_GAMMA, 0);
422     intf_AssignKey( p_intf , 'c', INTF_KEY_TOGGLE_GRAYSCALE, 0);
423     intf_AssignKey( p_intf , ' ', INTF_KEY_TOGGLE_INTERFACE, 0);
424     intf_AssignKey( p_intf , 'i', INTF_KEY_TOGGLE_INFO, 0);
425     intf_AssignKey( p_intf , 's', INTF_KEY_TOGGLE_SCALING, 0);
426 }   
427
428 /*****************************************************************************
429  * intf_ProcessKey: process standard keys
430  *****************************************************************************
431  * This function will process standard keys and return non 0 if the key was
432  * unknown.
433  *****************************************************************************/
434 int intf_ProcessKey( intf_thread_t *p_intf, int g_key )
435 {
436     static int i_volbackup;
437     keyparm k_reply;
438     
439     k_reply = intf_GetKey( p_intf, g_key); 
440     
441     switch( k_reply.key )
442     {
443     case INTF_KEY_QUIT:                                                  /* quit order */
444         p_intf->b_die = 1;
445         break;
446     case INTF_KEY_SET_CHANNEL:
447         /* Change channel - return code is ignored since SelectChannel displays
448          * its own error messages */
449         intf_SelectChannel( p_intf, k_reply.param );
450         break;
451     case INTF_KEY_INC_VOLUME:                                                    /* volume + */
452         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol < VOLUME_MAX) )
453             p_main->p_aout->vol += VOLUME_STEP;
454         break;
455     case INTF_KEY_DEC_VOLUME:                                                    /* volume - */
456         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol > VOLUME_STEP) )
457             p_main->p_aout->vol -= VOLUME_STEP;
458         break;
459     case INTF_KEY_TOGGLE_VOLUME:                                                 /* toggle mute */
460         if( (p_main->p_aout != NULL) && (p_main->p_aout->vol))
461         {
462             i_volbackup = p_main->p_aout->vol;
463             p_main->p_aout->vol = 0;
464         }
465         else if( (p_main->p_aout != NULL) && (!p_main->p_aout->vol))
466             p_main->p_aout->vol = i_volbackup;
467         break;
468     case INTF_KEY_DEC_GAMMA:                                                     /* gamma - */
469         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma > -INTF_GAMMA_LIMIT) )
470         {
471             vlc_mutex_lock( &p_intf->p_vout->change_lock );
472             p_intf->p_vout->f_gamma   -= INTF_GAMMA_STEP;
473             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
474             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
475         }
476         break;
477     case INTF_KEY_INC_GAMMA:                                                     /* gamma + */
478         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma < INTF_GAMMA_LIMIT) )
479         {
480             vlc_mutex_lock( &p_intf->p_vout->change_lock );
481             p_intf->p_vout->f_gamma   += INTF_GAMMA_STEP;
482             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
483             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
484         }
485         break;
486     case INTF_KEY_TOGGLE_GRAYSCALE:                                            /* toggle grayscale */
487         if( p_intf->p_vout != NULL )
488         {
489             vlc_mutex_lock( &p_intf->p_vout->change_lock );
490             p_intf->p_vout->b_grayscale = !p_intf->p_vout->b_grayscale;
491             p_intf->p_vout->i_changes  |= VOUT_GRAYSCALE_CHANGE;
492             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
493         }
494         break;
495     case INTF_KEY_TOGGLE_INTERFACE:                                            /* toggle interface */
496         if( p_intf->p_vout != NULL )
497         {
498             vlc_mutex_lock( &p_intf->p_vout->change_lock );
499             p_intf->p_vout->b_interface     = !p_intf->p_vout->b_interface;
500             p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
501             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
502         }
503         break;
504     case INTF_KEY_TOGGLE_INFO:                                                 /* toggle info */
505         if( p_intf->p_vout != NULL )
506         {
507             vlc_mutex_lock( &p_intf->p_vout->change_lock );
508             p_intf->p_vout->b_info     = !p_intf->p_vout->b_info;
509             p_intf->p_vout->i_changes |= VOUT_INFO_CHANGE;
510             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
511         }
512         break;
513     case INTF_KEY_TOGGLE_SCALING:                                              /* toggle scaling */
514         if( p_intf->p_vout != NULL )
515         {
516             vlc_mutex_lock( &p_intf->p_vout->change_lock );
517             p_intf->p_vout->b_scale    = !p_intf->p_vout->b_scale;
518             p_intf->p_vout->i_changes |= VOUT_SCALE_CHANGE;
519             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
520         }
521         break;
522    default:                                                   /* unknown key */
523         return( 1 );
524     }
525
526     return( 0 );
527 }
528
529 /* following functions are local */
530
531 /*****************************************************************************
532  * LoadChannels: load channels description from a file
533  *****************************************************************************
534  * This structe describes all interface-specific data of the main (interface)
535  * thread.
536  * Each line of the file is a semicolon separated list of the following
537  * fields :
538  *      integer         channel number
539  *      string          channel description
540  *      integer         input method (see input.h)
541  *      string          input source
542  *      integer         input port
543  *      integer         input vlan id
544  * The last field must end with a semicolon.
545  * Comments and empty lines are not explicitely allowed, but lines with parsing
546  * errors are ignored without warning.
547  *****************************************************************************/
548 static int LoadChannels( intf_thread_t *p_intf, char *psz_filename )
549 {
550     FILE *              p_file;                                      /* file */
551     intf_channel_t *    p_channel;                        /* current channel */
552     char                psz_line[INTF_MAX_CMD_SIZE];          /* line buffer */
553     int                 i_index;                   /* channel or field index */
554
555     /* Set default value */
556     p_intf->p_channel = NULL;
557
558     /* FIXME: channels are disabled */
559     //return( 0 );
560
561     /* Open file */
562     p_file = fopen( psz_filename, "r" );
563     if( p_file == NULL )
564     {
565         intf_ErrMsg("error: can't open %s (%s)", psz_filename, strerror(errno));
566         return( 1 );
567     }
568
569     /* First pass: count number of lines */
570     for( i_index = 0; fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL; i_index++ )
571     {
572         ;
573     }
574
575     if( i_index != 0 )
576     {
577         /* Allocate array and rewind - some of the lines may be invalid, and the
578          * array will probably be larger than the actual number of channels, but
579          * it has no consequence. */
580         p_intf->p_channel = malloc( sizeof( intf_channel_t ) * i_index );
581         if( p_intf->p_channel == NULL )
582         {
583             intf_ErrMsg("error: %s", strerror(ENOMEM));
584             fclose( p_file );
585             return( 1 );
586         }
587         p_channel = p_intf->p_channel;
588         rewind( p_file );
589
590         /* Second pass: read channels descriptions */
591         while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
592         {
593             if( !ParseChannel( p_channel, psz_line ) )
594             {
595                 intf_DbgMsg( "channel [%d] %s : method %d (%s:%d vlan id %d)",
596                          p_channel->i_channel, p_channel->psz_description,
597                          p_channel->i_input_method,
598                          p_channel->psz_input_source,
599                          p_channel->i_input_port, p_channel->i_input_vlan_id );
600                 p_channel++;
601             }
602         }
603
604         /* Add marker at the end of the array */
605         p_channel->i_channel = -1;
606     }
607
608     /* Close file */
609     fclose( p_file );
610     return( 0 );
611 }
612
613 /*****************************************************************************
614  * UnloadChannels: unload channels description
615  *****************************************************************************
616  * This function free all resources allocated by LoadChannels, if any.
617  *****************************************************************************/
618 static void UnloadChannels( intf_thread_t *p_intf )
619 {
620     int i_channel;                                          /* channel index */
621
622     if( p_intf->p_channel != NULL )
623     {
624         /* Free allocated strings */
625         for( i_channel = 0;
626              p_intf->p_channel[ i_channel ].i_channel != -1;
627              i_channel++ )
628         {
629             if( p_intf->p_channel[ i_channel ].psz_description != NULL )
630             {
631                 free( p_intf->p_channel[ i_channel ].psz_description );
632             }
633             if( p_intf->p_channel[ i_channel ].psz_input_source != NULL )
634             {
635                 free( p_intf->p_channel[ i_channel ].psz_input_source );
636             }
637         }
638
639         /* Free array */
640         free( p_intf->p_channel );
641         p_intf->p_channel = NULL;
642     }
643 }
644
645
646 /*****************************************************************************
647  * ParseChannel: parse a channel description line
648  *****************************************************************************
649  * See LoadChannels. This function return non 0 on parsing error.
650  *****************************************************************************/
651 static int ParseChannel( intf_channel_t *p_channel, char *psz_str )
652 {
653     char *      psz_index;                              /* current character */
654     char *      psz_end;                           /* end pointer for strtol */
655     int         i_field;                        /* field number, -1 on error */
656     int         i_field_length;             /* field length, for text fields */
657
658     /* Set some default fields */
659     p_channel->i_channel =              0;
660     p_channel->psz_description =        NULL;
661     p_channel->i_input_method =         0;
662     p_channel->psz_input_source =       NULL;
663     p_channel->i_input_port =           0;
664     p_channel->i_input_vlan_id =        0;
665
666     /* Parse string */
667     i_field = 0;
668     for( psz_index = psz_str; (i_field != -1) && (*psz_index != '\0'); psz_index++ )
669     {
670         if( *psz_index == ';' )
671         {
672             /* Mark end of field */
673             *psz_index = '\0';
674
675             /* Parse field */
676             switch( i_field++ )
677             {
678             case 0:                                        /* channel number */
679                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
680                 if( (*psz_str == '\0') || (*psz_end != '\0') )
681                 {
682                     i_field = -1;
683                 }
684                 break;
685             case 1:                                   /* channel description */
686                 i_field_length = strlen( psz_str );
687                 if( i_field_length != 0 )
688                 {
689                     p_channel->psz_description = malloc( i_field_length + 1 );
690                     if( p_channel->psz_description == NULL )
691                     {
692                         intf_ErrMsg("error: %s", strerror( ENOMEM ));
693                         i_field = -1;
694                     }
695                     else
696                     {
697                         strcpy( p_channel->psz_description, psz_str );
698                     }
699                 }
700                 break;
701             case 2:                                          /* input method */
702                 p_channel->i_input_method = strtol( psz_str, &psz_end, 0);
703                 if( (*psz_str == '\0') || (*psz_end != '\0') )
704                 {
705                     i_field = -1;
706                 }
707                 break;
708             case 3:                                          /* input source */
709                 i_field_length = strlen( psz_str );
710                 if( i_field_length != 0 )
711                 {
712                     p_channel->psz_input_source = malloc( i_field_length + 1 );
713                     if( p_channel->psz_input_source == NULL )
714                     {
715                         intf_ErrMsg("error: %s", strerror( ENOMEM ));
716                         i_field = -1;
717                     }
718                     else
719                     {
720                         strcpy( p_channel->psz_input_source, psz_str );
721                     }
722                 }
723                 break;
724             case 4:                                            /* input port */
725                 p_channel->i_input_port = strtol( psz_str, &psz_end, 0);
726                 if( (*psz_str == '\0') || (*psz_end != '\0') )
727                 {
728                     i_field = -1;
729                 }
730                 break;
731             case 5:                                          /* input vlan id */
732                 p_channel->i_input_vlan_id = strtol( psz_str, &psz_end, 0);
733                 if( (*psz_str == '\0') || (*psz_end != '\0') )
734                 {
735                     i_field = -1;
736                 }
737                 break;
738                 /* ... following fields are ignored */
739             }
740
741             /* Set new beginning of field */
742             psz_str = psz_index + 1;
743         }
744     }
745
746     /* At least the first three fields must be parsed sucessfully for function
747      * success. Other parsing errors are returned using i_field = -1. */
748     if( i_field < 3 )
749     {
750         /* Function fails. Free allocated strings */
751         if( p_channel->psz_description != NULL )
752         {
753             free( p_channel->psz_description );
754         }
755         if( p_channel->psz_input_source != NULL )
756         {
757             free( p_channel->psz_input_source );
758         }
759         return( 1 );
760     }
761
762     /* Return success */
763     return( 0 );
764 }