]> git.sesse.net Git - vlc/blob - src/input/input_programs.c
* input_programs.c: a little memleak.
[vlc] / src / input / input_programs.c
1 /*****************************************************************************
2  * input_programs.c: es_descriptor_t, pgrm_descriptor_t management
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VideoLAN
5  * $Id: input_programs.c,v 1.132 2004/02/22 16:40:25 fenrir Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
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>
28 #include <string.h>                                    /* memcpy(), memset() */
29
30 #include <vlc/vlc.h>
31
32 #include "stream_control.h"
33 #include "input_ext-intf.h"
34 #include "input_ext-dec.h"
35 #include "input_ext-plugins.h"
36
37 /*
38  * NOTICE : all of these functions expect you to have taken the lock on
39  * p_input->stream.lock
40  */
41
42 /* Navigation callbacks */
43 static int ProgramCallback( vlc_object_t *, char const *,
44                             vlc_value_t, vlc_value_t, void * );
45 static int TitleCallback( vlc_object_t *, char const *,
46                           vlc_value_t, vlc_value_t, void * );
47 static int ChapterCallback( vlc_object_t *, char const *,
48                             vlc_value_t, vlc_value_t, void * );
49 static int NavigationCallback( vlc_object_t *, char const *,
50                                vlc_value_t, vlc_value_t, void * );
51 static int ESCallback( vlc_object_t *, char const *,
52                        vlc_value_t, vlc_value_t, void * );
53
54 /*****************************************************************************
55  * input_InitStream: init the stream descriptor of the given input
56  *****************************************************************************/
57 int input_InitStream( input_thread_t * p_input, size_t i_data_len )
58 {
59     vlc_value_t text,val;
60
61     p_input->stream.i_stream_id = 0;
62
63     /* initialized to 0 since we don't give the signal to the interface
64      * before the end of input initialization */
65     p_input->stream.b_changed = 0;
66     p_input->stream.pp_es = NULL;
67     p_input->stream.pp_selected_es = NULL;
68     p_input->stream.p_removed_es = NULL;
69     p_input->stream.p_newly_selected_es = NULL;
70     p_input->stream.i_pgrm_number = 0;
71     p_input->stream.pp_programs = NULL;
72     p_input->stream.p_selected_program = NULL;
73     p_input->stream.p_new_program = NULL;
74
75     if( i_data_len )
76     {
77         if ( (p_input->stream.p_demux_data = malloc( i_data_len )) == NULL )
78         {
79             msg_Err( p_input, "out of memory" );
80             return 1;
81         }
82         memset( p_input->stream.p_demux_data, 0, i_data_len );
83     }
84     else
85     {
86         p_input->stream.p_demux_data = NULL;
87     }
88
89     var_Create( p_input, "intf-change", VLC_VAR_BOOL );
90     val.b_bool = VLC_TRUE;
91     var_Set( p_input, "intf-change", val );
92
93     /* Create a few object variables used for navigation in the interfaces */
94     var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
95     text.psz_string = _("Program");
96     var_Change( p_input, "program", VLC_VAR_SETTEXT, &text, NULL );
97
98     var_Create( p_input, "title", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
99     text.psz_string = _("Title");
100     var_Change( p_input, "title", VLC_VAR_SETTEXT, &text, NULL );
101
102     var_Create( p_input, "chapter", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
103     text.psz_string = _("Chapter");
104     var_Change( p_input, "chapter", VLC_VAR_SETTEXT, &text, NULL );
105
106     var_Create( p_input, "navigation", VLC_VAR_VARIABLE | VLC_VAR_HASCHOICE );
107     text.psz_string = _("Navigation");
108     var_Change( p_input, "navigation", VLC_VAR_SETTEXT, &text, NULL );
109
110     var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
111     text.psz_string = _("Video Track");
112     var_Change( p_input, "video-es", VLC_VAR_SETTEXT, &text, NULL );
113     var_Create( p_input, "audio-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
114     text.psz_string = _("Audio Track");
115     var_Change( p_input, "audio-es", VLC_VAR_SETTEXT, &text, NULL );
116     var_Create( p_input, "spu-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
117     text.psz_string = _("Subtitles Track");
118     var_Change( p_input, "spu-es", VLC_VAR_SETTEXT, &text, NULL );
119
120     var_AddCallback( p_input, "program", ProgramCallback, NULL );
121     var_AddCallback( p_input, "title", TitleCallback, NULL );
122     var_AddCallback( p_input, "chapter", ChapterCallback, NULL );
123     var_AddCallback( p_input, "video-es", ESCallback, NULL );
124     var_AddCallback( p_input, "audio-es", ESCallback, NULL );
125     var_AddCallback( p_input, "spu-es", ESCallback, NULL );
126
127     return VLC_SUCCESS;
128 }
129
130 /*****************************************************************************
131  * input_EndStream: free all stream descriptors
132  *****************************************************************************/
133 void input_EndStream( input_thread_t * p_input )
134 {
135     /* Free all programs and associated ES, and associated decoders. */
136     while( p_input->stream.i_pgrm_number )
137     {
138         input_DelProgram( p_input, p_input->stream.pp_programs[0] );
139     }
140
141     /* Free standalone ES */
142     while( p_input->stream.i_es_number )
143     {
144         input_DelES( p_input, p_input->stream.pp_es[0] );
145     }
146
147     /* Free all areas */
148     while( p_input->stream.i_area_nb )
149     {
150         input_DelArea( p_input, p_input->stream.pp_areas[0] );
151     }
152
153     /* Free selected ES */
154     if( p_input->stream.pp_selected_es != NULL )
155     {
156         free( p_input->stream.pp_selected_es );
157     }
158
159     if( p_input->stream.p_demux_data != NULL )
160     {
161         free( p_input->stream.p_demux_data );
162     }
163
164     /* Free navigation variables */
165     var_Destroy( p_input, "program" );
166     var_Destroy( p_input, "title" );
167     var_Destroy( p_input, "chapter" );
168     var_Destroy( p_input, "video-es" );
169     var_Destroy( p_input, "audio-es" );
170     var_Destroy( p_input, "spu-es" );
171     var_Destroy( p_input, "intf-change" );
172 }
173
174 /*****************************************************************************
175  * input_FindProgram: returns a pointer to a program described by its ID
176  *****************************************************************************/
177 pgrm_descriptor_t * input_FindProgram( input_thread_t * p_input,
178                                        uint16_t i_pgrm_id )
179 {
180     unsigned int i;
181
182     for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
183     {
184         if( p_input->stream.pp_programs[i]->i_number == i_pgrm_id )
185         {
186             return p_input->stream.pp_programs[i];
187         }
188     }
189
190     return NULL;
191 }
192
193 /*****************************************************************************
194  * input_AddProgram: add and init a program descriptor
195  *****************************************************************************
196  * This program descriptor will be referenced in the given stream descriptor
197  *****************************************************************************/
198 pgrm_descriptor_t * input_AddProgram( input_thread_t * p_input,
199                                       uint16_t i_pgrm_id, size_t i_data_len )
200 {
201     /* Where to add the pgrm */
202     pgrm_descriptor_t * p_pgrm = malloc( sizeof(pgrm_descriptor_t) );
203     vlc_value_t val;
204
205     if( p_pgrm == NULL )
206     {
207         msg_Err( p_input, "out of memory" );
208         return NULL;
209     }
210
211     /* Init this entry */
212     p_pgrm->i_number = i_pgrm_id;
213     p_pgrm->b_is_ok = 0;
214     p_pgrm->i_version = 0;
215
216     p_pgrm->i_es_number = 0;
217     p_pgrm->pp_es = NULL;
218
219     input_ClockInit( p_pgrm );
220
221     p_pgrm->i_synchro_state = SYNCHRO_START;
222
223     if( i_data_len )
224     {
225         p_pgrm->p_demux_data = malloc( i_data_len );
226         if( p_pgrm->p_demux_data == NULL )
227         {
228             msg_Err( p_input, "out of memory" );
229             return NULL;
230         }
231         memset( p_pgrm->p_demux_data, 0, i_data_len );
232     }
233     else
234     {
235         p_pgrm->p_demux_data = NULL;
236     }
237
238     /* Add an entry to the list of program associated with the stream */
239     INSERT_ELEM( p_input->stream.pp_programs,
240                  p_input->stream.i_pgrm_number,
241                  p_input->stream.i_pgrm_number,
242                  p_pgrm );
243
244     val.i_int = i_pgrm_id;
245     var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
246
247     return p_pgrm;
248 }
249
250 /*****************************************************************************
251  * input_DelProgram: destroy a program descriptor
252  *****************************************************************************
253  * All ES descriptions referenced in the descriptor will be deleted.
254  *****************************************************************************/
255 void input_DelProgram( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm )
256 {
257     unsigned int i_pgrm_index;
258     vlc_value_t val;
259
260     /* Find the program in the programs table */
261     for( i_pgrm_index = 0; i_pgrm_index < p_input->stream.i_pgrm_number;
262          i_pgrm_index++ )
263     {
264         if( p_input->stream.pp_programs[i_pgrm_index] == p_pgrm )
265             break;
266     }
267
268     /* If the program wasn't found, do nothing */
269     if( i_pgrm_index == p_input->stream.i_pgrm_number )
270     {
271         msg_Err( p_input, "program does not belong to this input" );
272         return;
273     }
274
275     val.i_int = p_input->stream.pp_programs[i_pgrm_index]->i_number;
276     var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
277
278     /* Free the structures that describe the es that belongs to that program */
279     while( p_pgrm->i_es_number )
280     {
281         input_DelES( p_input, p_pgrm->pp_es[0] );
282     }
283
284     /* Free the demux data */
285     if( p_pgrm->p_demux_data != NULL )
286     {
287         free( p_pgrm->p_demux_data );
288     }
289
290     /* Remove this program from the stream's list of programs */
291     REMOVE_ELEM( p_input->stream.pp_programs,
292                  p_input->stream.i_pgrm_number,
293                  i_pgrm_index );
294
295     if( p_pgrm == p_input->stream.p_selected_program )
296         p_input->stream.p_selected_program = NULL;
297
298     /* Free the description of this program */
299     free( p_pgrm );
300 }
301
302 /*****************************************************************************
303  * input_AddArea: add and init an area descriptor
304  *****************************************************************************
305  * This area descriptor will be referenced in the given stream descriptor
306  *****************************************************************************/
307 input_area_t * input_AddArea( input_thread_t * p_input,
308                               uint16_t i_area_id, uint16_t i_part_nb )
309 {
310     /* Where to add the pgrm */
311     input_area_t * p_area = malloc( sizeof(input_area_t) );
312     vlc_value_t val;
313     int i;
314
315     if( p_area == NULL )
316     {
317         msg_Err( p_input, "out of memory" );
318         return NULL;
319     }
320
321     /* Init this entry */
322     p_area->i_id = i_area_id;
323     p_area->i_part_nb = i_part_nb;
324     p_area->i_part= 0;
325     p_area->i_start = 0;
326     p_area->i_size = 0;
327     p_area->i_tell = 0;
328     p_area->i_seek = NO_SEEK;
329
330     /* Add an entry to the list of program associated with the stream */
331     INSERT_ELEM( p_input->stream.pp_areas,
332                  p_input->stream.i_area_nb,
333                  p_input->stream.i_area_nb,
334                  p_area );
335
336     /* Don't add empty areas */
337     if( i_part_nb == 0 )
338         return NULL;
339
340     /* Take care of the navigation variables */
341     val.i_int = i_area_id;
342     var_Change( p_input, "title", VLC_VAR_ADDCHOICE, &val, NULL );
343
344     val.psz_string = malloc( sizeof("title ") + 5 );
345     if( val.psz_string )
346     {
347         vlc_value_t val2, text, text2;
348
349         sprintf( val.psz_string, "title %2i", i_area_id );
350         var_Destroy( p_input, val.psz_string );
351         var_Create( p_input, val.psz_string, VLC_VAR_INTEGER |
352                     VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
353         var_AddCallback( p_input, val.psz_string, NavigationCallback,
354                          (void *)(int)i_area_id );
355
356         text.psz_string = malloc( strlen( _("Title %i") ) + 20 );
357         if( text.psz_string )
358             sprintf( text.psz_string, _("Title %i"), i_area_id );
359
360         var_Change( p_input, "navigation", VLC_VAR_ADDCHOICE, &val, &text );
361
362         if( text.psz_string ) free( text.psz_string );
363
364         text2.psz_string = malloc( strlen( _("Chapter %i") ) + 20 );
365
366         for( i = 1; i <= i_part_nb; i++ )
367         {
368             val2.i_int = i;
369
370             if( text2.psz_string )
371                 sprintf( text2.psz_string, _("Chapter %i"), i );
372
373             var_Change( p_input, val.psz_string,
374                         VLC_VAR_ADDCHOICE, &val2, &text2 );
375         }
376
377         if( text2.psz_string ) free( text2.psz_string );
378         free( val.psz_string );
379     }
380
381     if( p_input->stream.i_area_nb == 2 )
382     {
383         vlc_value_t text;
384
385         /* Add another bunch of navigation object variables */
386         var_Create( p_input, "next-title", VLC_VAR_VOID );
387         text.psz_string = _("Next title");
388         var_Change( p_input, "next-title", VLC_VAR_SETTEXT, &text, NULL );
389         var_Create( p_input, "prev-title", VLC_VAR_VOID );
390         text.psz_string = _("Previous title");
391         var_Change( p_input, "prev-title", VLC_VAR_SETTEXT, &text, NULL );
392         var_AddCallback( p_input, "next-title", TitleCallback, NULL );
393         var_AddCallback( p_input, "prev-title", TitleCallback, NULL );
394
395         var_Create( p_input, "next-chapter", VLC_VAR_VOID );
396         text.psz_string = _("Next chapter");
397         var_Change( p_input, "next-chapter", VLC_VAR_SETTEXT, &text, NULL );
398         var_Create( p_input, "prev-chapter", VLC_VAR_VOID );
399         text.psz_string = _("Previous chapter");
400         var_Change( p_input, "prev-chapter", VLC_VAR_SETTEXT, &text, NULL );
401         var_AddCallback( p_input, "next-chapter", ChapterCallback, NULL );
402         var_AddCallback( p_input, "prev-chapter", ChapterCallback, NULL );
403     }
404
405     return p_area;
406 }
407
408 /*****************************************************************************
409  * input_SetProgram: changes the current program
410  *****************************************************************************/
411 int input_SetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_new_prg )
412 {
413     unsigned int i_es_index;
414     int i_required_audio_es;
415     int i_required_spu_es;
416     int i_audio_es = 0;
417     int i_spu_es = 0;
418     vlc_value_t val;
419
420     if ( p_input->stream.p_selected_program )
421     {
422         for ( i_es_index = 1 ; /* 0 should be the PMT */
423                 i_es_index < p_input->stream.p_selected_program->
424                 i_es_number ;
425                 i_es_index ++ )
426         {
427 #define p_es p_input->stream.p_selected_program->pp_es[i_es_index]
428             if ( p_es->p_dec ) /* if the ES was selected */
429             {
430                 input_UnselectES( p_input , p_es );
431             }
432 #undef p_es
433         }
434     }
435
436     /* Get the number of the required audio stream */
437     var_Get( p_input, "audio", &val );
438     if( val.b_bool )
439     {
440         /* Default is the first one */
441         var_Get( p_input, "audio-channel", &val );
442         i_required_audio_es = val.i_int;
443         if( i_required_audio_es < 0 )
444         {
445             i_required_audio_es = 1;
446         }
447     }
448     else
449     {
450         i_required_audio_es = 0;
451     }
452
453     /* Same thing for subtitles */
454     var_Get( p_input, "video", &val );
455     if( val.b_bool )
456     {
457         /* for spu, default is none */
458         var_Get( p_input, "spu-channel", &val );
459         i_required_spu_es = val.i_int;
460         if( i_required_spu_es < 0 )
461         {
462             i_required_spu_es = 0;
463         }
464     }
465     else
466     {
467         i_required_spu_es = 0;
468     }
469
470     for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ )
471     {
472         switch( p_new_prg->pp_es[i_es_index]->i_cat )
473         {
474             case VIDEO_ES:
475                 msg_Dbg( p_input, "selecting video ES %x",
476                          p_new_prg->pp_es[i_es_index]->i_id );
477                 input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
478                 break;
479             case AUDIO_ES:
480                 i_audio_es += 1;
481                 if( i_audio_es <= i_required_audio_es )
482                 {
483                     msg_Dbg( p_input, "selecting audio ES %x",
484                              p_new_prg->pp_es[i_es_index]->i_id );
485                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index]);
486                 }
487                 break;
488             /* Not sure this one is fully specification-compliant */
489             case SPU_ES :
490                 i_spu_es += 1;
491                 if( i_spu_es <= i_required_spu_es )
492                 {
493                     msg_Dbg( p_input, "selecting spu ES %x",
494                              p_new_prg->pp_es[i_es_index]->i_id );
495                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
496                 }
497             break;
498             default :
499                 msg_Dbg( p_input, "ES %x has unknown type",
500                          p_new_prg->pp_es[i_es_index]->i_id );
501                 break;
502         }
503
504     }
505
506     p_input->stream.p_selected_program = p_new_prg;
507
508     /* Update the navigation variables without triggering a callback */
509     val.i_int = p_new_prg->i_number;
510     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
511
512     return( 0 );
513 }
514
515 /*****************************************************************************
516  * input_DelArea: destroy a area descriptor
517  *****************************************************************************
518  * All ES descriptions referenced in the descriptor will be deleted.
519  *****************************************************************************/
520 void input_DelArea( input_thread_t * p_input, input_area_t * p_area )
521 {
522     unsigned int i_area_index;
523     vlc_value_t val;
524
525     /* Find the area in the areas table */
526     for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb;
527          i_area_index++ )
528     {
529         if( p_input->stream.pp_areas[i_area_index] == p_area )
530             break;
531     }
532
533     /* If the area wasn't found, do nothing */
534     if( i_area_index == p_input->stream.i_area_nb )
535     {
536         msg_Err( p_input, "area does not belong to this input" );
537         return;
538     }
539
540     /* Take care of the navigation variables */
541     val.psz_string = malloc( sizeof("title ") + 5 );
542     if( val.psz_string )
543     {
544         sprintf( val.psz_string, "title %i", p_area->i_id );
545         var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val, NULL );
546         var_Destroy( p_input, val.psz_string );
547
548         free( val.psz_string );
549     }
550
551     /* Remove this area from the stream's list of areas */
552     REMOVE_ELEM( p_input->stream.pp_areas,
553                  p_input->stream.i_area_nb,
554                  i_area_index );
555
556     /* Free the description of this area */
557     free( p_area );
558
559     if( p_input->stream.i_area_nb == 1 )
560     {
561         /* Del unneeded navigation object variables */
562         var_Destroy( p_input, "next-title" );
563         var_Destroy( p_input, "prev-title" );
564         var_Destroy( p_input, "next-chapter" );
565         var_Destroy( p_input, "prev-chapter" );
566     }
567 }
568
569
570 /*****************************************************************************
571  * input_FindES: returns a pointer to an ES described by its ID
572  *****************************************************************************/
573 es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id )
574 {
575     unsigned int i;
576
577     for( i = 0; i < p_input->stream.i_es_number; i++ )
578     {
579         if( p_input->stream.pp_es[i]->i_id == i_es_id )
580         {
581             return p_input->stream.pp_es[i];
582         }
583     }
584
585     return NULL;
586 }
587
588 /*****************************************************************************
589  * input_AddES:
590  *****************************************************************************
591  * Reserve a slot in the table of ES descriptors for the ES and add it to the
592  * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
593  * alone (PSI ?)
594  *****************************************************************************/
595 es_descriptor_t * input_AddES( input_thread_t * p_input,
596                                pgrm_descriptor_t * p_pgrm, uint16_t i_es_id,
597                                int i_category, char const *psz_desc,
598                                size_t i_data_len )
599 {
600     es_descriptor_t * p_es;
601     vlc_value_t val, text;
602     char *psz_var = NULL;
603
604     p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
605     if( p_es == NULL )
606     {
607         msg_Err( p_input, "out of memory" );
608         return( NULL);
609     }
610
611     INSERT_ELEM( p_input->stream.pp_es,
612                  p_input->stream.i_es_number,
613                  p_input->stream.i_es_number,
614                  p_es );
615
616     /* Init its values */
617     p_es->i_id = i_es_id;
618     p_es->i_stream_id = 0;
619     p_es->p_pes = NULL;
620     p_es->p_dec = NULL;
621     p_es->i_cat = i_category;
622     p_es->i_demux_fd = 0;
623     p_es->c_packets = 0;
624     p_es->c_invalid_packets = 0;
625     p_es->b_force_decoder = VLC_FALSE;
626     es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 );
627
628     if( i_data_len )
629     {
630         p_es->p_demux_data = malloc( i_data_len );
631         if( p_es->p_demux_data == NULL )
632         {
633             msg_Err( p_input, "out of memory" );
634             return( NULL );
635         }
636         memset( p_es->p_demux_data, 0, i_data_len );
637     }
638     else
639     {
640         p_es->p_demux_data = NULL;
641     }
642     p_es->p_waveformatex     = NULL;
643     p_es->p_bitmapinfoheader = NULL;
644     p_es->p_spuinfo = NULL;
645
646     /* Add this ES to the program definition if one is given */
647     if( p_pgrm )
648     {
649         INSERT_ELEM( p_pgrm->pp_es,
650                      p_pgrm->i_es_number,
651                      p_pgrm->i_es_number,
652                      p_es );
653         p_es->p_pgrm = p_pgrm;
654     }
655     else
656     {
657         p_es->p_pgrm = NULL;
658     }
659
660     switch( i_category )
661     {
662     case AUDIO_ES:
663         psz_var = "audio-es";
664         break;
665     case SPU_ES:
666         psz_var = "spu-es";
667         break;
668     case VIDEO_ES:
669         psz_var = "video-es";
670         break;
671     }
672
673     if( psz_var )
674     {
675         /* Get the number of ES already added */
676         var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
677         if( val.i_int == 0 )
678         {
679             vlc_value_t val2;
680
681             /* First one, we need to add the "Disable" choice */
682             val2.i_int = -1; text.psz_string = _("Disable");
683             var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
684             val.i_int++;
685         }
686
687         /* Take care of the ES description */
688         if( psz_desc && *psz_desc )
689         {
690             p_es->psz_desc = strdup( psz_desc );
691         }
692         else
693         {
694             p_es->psz_desc = malloc( strlen( _("Track %i") ) + 20 );
695             if( p_es->psz_desc )
696                 sprintf( p_es->psz_desc, _("Track %i"), val.i_int );
697         }
698
699         val.i_int = p_es->i_id;
700         text.psz_string = p_es->psz_desc;
701         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
702     }
703     else p_es->psz_desc = NULL;
704
705     return p_es;
706 }
707
708 /*****************************************************************************
709  * input_DelES:
710  *****************************************************************************/
711 void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
712 {
713     unsigned int            i_index, i_es_index;
714     pgrm_descriptor_t *     p_pgrm;
715     char *                  psz_var = NULL;
716     vlc_value_t             val;
717
718     /* Find the ES in the ES table */
719     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
720          i_es_index++ )
721     {
722         if( p_input->stream.pp_es[i_es_index] == p_es )
723             break;
724     }
725
726     /* If the ES wasn't found, do nothing */
727     if( i_es_index == p_input->stream.i_es_number )
728     {
729         msg_Err( p_input, "ES does not belong to this input" );
730         return;
731     }
732
733     /* Remove es from its associated variable */
734     switch( p_es->i_cat )
735     {
736     case AUDIO_ES:
737         psz_var = "audio-es";
738         break;
739     case SPU_ES:
740         psz_var = "spu-es";
741         break;
742     case VIDEO_ES:
743         psz_var = "video-es";
744         break;
745     }
746
747     if( psz_var )
748     {
749         val.i_int = p_es->i_id;
750         var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
751
752         /* Remove the "Disable" entry if needed */
753         var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
754         if( val.i_int == 1 )
755         {
756             val.i_int = -1;
757             var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
758         }
759     }
760
761     /* Kill associated decoder, if any. */
762     if( p_es->p_dec != NULL )
763     {
764         input_UnselectES( p_input, p_es );
765     }
766
767     /* Remove this ES from the description of the program if it is associated
768      * to one */
769     p_pgrm = p_es->p_pgrm;
770     if( p_pgrm )
771     {
772         for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
773         {
774             if( p_pgrm->pp_es[i_index] == p_es )
775             {
776                 REMOVE_ELEM( p_pgrm->pp_es,
777                              p_pgrm->i_es_number,
778                              i_index );
779                 break;
780             }
781         }
782     }
783
784     /* Free the demux data */
785     if( p_es->p_demux_data != NULL )
786     {
787         free( p_es->p_demux_data );
788     }
789     if( p_es->p_waveformatex )
790     {
791         free( p_es->p_waveformatex );
792     }
793     if( p_es->p_bitmapinfoheader )
794     {
795         free( p_es->p_bitmapinfoheader );
796     }
797     if( p_es->p_spuinfo )
798     {
799         free( p_es->p_spuinfo );
800     }
801
802     /* Free the description string */
803     if( p_es->psz_desc != NULL )
804     {
805         free( p_es->psz_desc );
806     }
807
808     /* Clean the es format */
809     es_format_Clean( &p_es->fmt );
810
811     /* Find the ES in the ES table */
812     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
813          i_es_index++ )
814     {
815         if( p_input->stream.pp_es[i_es_index] == p_es )
816             break;
817     }
818
819     /* Remove this ES from the stream's list of ES */
820     REMOVE_ELEM( p_input->stream.pp_es,
821                  p_input->stream.i_es_number,
822                  i_es_index );
823
824     /* Free the ES */
825     free( p_es );
826 }
827
828 /*****************************************************************************
829  * input_SelectES: selects an ES and spawns the associated decoder
830  *****************************************************************************
831  * Remember we are still supposed to have stream_lock when entering this
832  * function ?
833  *****************************************************************************/
834 int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
835 {
836     vlc_value_t val;
837     char *psz_var = NULL;
838
839     if( p_es == NULL )
840     {
841         msg_Err( p_input, "nothing to do in input_SelectES" );
842         return -1;
843     }
844
845     if( p_es->i_cat == VIDEO_ES || p_es->i_cat == SPU_ES )
846     {
847         var_Get( p_input, "video", &val );
848         if( !val.b_bool )
849         {
850             msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
851                      p_es->i_id );
852             return -1;
853         }
854     }
855
856     if( p_es->i_cat == AUDIO_ES )
857     {
858         var_Get( p_input, "audio", &val );
859         if( !val.b_bool )
860         {
861             msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
862                      p_es->i_id );
863             return -1;
864         }
865     }
866
867     msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
868
869     if( p_es->p_dec != NULL )
870     {
871         msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
872         return -1;
873     }
874
875     /* Release the lock, not to block the input thread during
876      * the creation of the thread. */
877     vlc_mutex_unlock( &p_input->stream.stream_lock );
878     p_es->p_dec = input_RunDecoder( p_input, p_es );
879     vlc_mutex_lock( &p_input->stream.stream_lock );
880
881     if( p_es->p_dec == NULL )
882     {
883         return -1;
884     }
885
886     /* Update the es variable without triggering a callback */
887     switch( p_es->i_cat )
888     {
889     case AUDIO_ES:
890         psz_var = "audio-es";
891         break;
892     case SPU_ES:
893         psz_var = "spu-es";
894         break;
895     case VIDEO_ES:
896         psz_var = "video-es";
897         break;
898     }
899
900     if( psz_var )
901     {
902         val.i_int = p_es->i_id;
903         var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
904     }
905
906     return 0;
907 }
908
909 /*****************************************************************************
910  * input_UnselectES: removes an ES from the list of selected ES
911  *****************************************************************************/
912 int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
913 {
914     unsigned int i_index = 0;
915     vlc_value_t val;
916     char *psz_var = NULL;
917
918     if( p_es == NULL )
919     {
920         msg_Err( p_input, "nothing to do in input_UnselectES" );
921         return -1;
922     }
923
924     msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
925
926     if( p_es->p_dec == NULL )
927     {
928         msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
929         return( -1 );
930     }
931
932     /* Update the es variable without triggering a callback */
933     switch( p_es->i_cat )
934     {
935     case AUDIO_ES:
936         psz_var = "audio-es";
937         break;
938     case SPU_ES:
939         psz_var = "spu-es";
940         break;
941     case VIDEO_ES:
942         psz_var = "video-es";
943         break;
944     }
945
946     if( psz_var )
947     {
948         val.i_int = -1;
949         var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
950     }
951
952     /* Actually unselect the ES */
953     input_EndDecoder( p_input, p_es );
954     p_es->p_pes = NULL;
955
956     if( ( p_es->p_dec == NULL ) &&
957         ( p_input->stream.i_selected_es_number > 0 ) )
958     {
959         while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
960                ( p_input->stream.pp_selected_es[i_index] != p_es ) )
961         {
962             i_index++;
963         }
964
965         /* XXX: no need to memmove, we have unsorted data */
966         REMOVE_ELEM( p_input->stream.pp_selected_es,
967                      p_input->stream.i_selected_es_number,
968                      i_index );
969
970         if( p_input->stream.i_selected_es_number == 0 )
971         {
972             msg_Dbg( p_input, "no more selected ES" );
973             return 1;
974         }
975     }
976
977     return 0;
978 }
979
980 /*****************************************************************************
981  * Navigation callback: a bunch of navigation variables are used as an
982  *  alternative to the navigation API.
983  *****************************************************************************/
984 static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
985                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
986 {
987     input_thread_t *p_input = (input_thread_t *)p_this;
988     vlc_value_t val;
989
990     if( oldval.i_int == newval.i_int )
991        return VLC_SUCCESS;
992
993     vlc_mutex_lock( &p_input->stream.stream_lock );
994     if( ( newval.i_int > 0 ) )
995     {
996         vlc_mutex_unlock( &p_input->stream.stream_lock );
997         input_ChangeProgram( p_input, (uint16_t)newval.i_int );
998         input_SetStatus( p_input, INPUT_STATUS_PLAY );
999         vlc_mutex_lock( &p_input->stream.stream_lock );
1000     }
1001     vlc_mutex_unlock( &p_input->stream.stream_lock );
1002
1003     val.b_bool = VLC_TRUE;
1004     var_Set( p_input, "intf-change", val );
1005
1006     return VLC_SUCCESS;
1007 }
1008
1009 static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
1010                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1011 {
1012     input_thread_t *p_input = (input_thread_t *)p_this;
1013     input_area_t *p_area;
1014     vlc_value_t val, val_list;
1015     int i, i_step = 0;
1016
1017     if( !strcmp( psz_cmd, "next-title" ) ) i_step++;
1018     else if( !strcmp( psz_cmd, "prev-title" ) ) i_step--;
1019
1020     if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
1021
1022     /* Sanity check should have already been done by var_Set(). */
1023     vlc_mutex_lock( &p_input->stream.stream_lock );
1024
1025     if( i_step )
1026     {
1027         var_Get( p_this, "title", &newval );
1028         var_Change( p_this, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1029         for( i = 0; i < val_list.p_list->i_count; i++ )
1030         {
1031             if( val_list.p_list->p_values[i].i_int == newval.i_int &&
1032                 i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
1033             {
1034                 newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
1035                 break;
1036             }
1037         }
1038         var_Change( p_this, "title", VLC_VAR_FREELIST, &val_list, NULL );
1039     }
1040
1041     p_area = p_input->stream.pp_areas[newval.i_int];
1042     p_area->i_part = 1;
1043
1044     vlc_mutex_unlock( &p_input->stream.stream_lock );
1045
1046     input_ChangeArea( p_input, p_area );
1047     input_SetStatus( p_input, INPUT_STATUS_PLAY );
1048
1049     val.b_bool = VLC_TRUE;
1050     var_Set( p_input, "intf-change", val );
1051
1052     return VLC_SUCCESS;
1053 }
1054
1055 static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
1056                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1057 {
1058     input_thread_t *p_input = (input_thread_t *)p_this;
1059     input_area_t *p_area;
1060     vlc_value_t val, val_list;
1061     int i, i_step = 0;
1062
1063     if( !strcmp( psz_cmd, "next-chapter" ) ) i_step++;
1064     else if( !strcmp( psz_cmd, "prev-chapter" ) ) i_step--;
1065
1066     if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
1067
1068     /* Sanity check should have already been done by var_Set(). */
1069     vlc_mutex_lock( &p_input->stream.stream_lock );
1070
1071     if( i_step )
1072     {
1073         var_Get( p_this, "chapter", &newval );
1074         var_Change( p_this, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1075         for( i = 0; i < val_list.p_list->i_count; i++ )
1076         {
1077             if( val_list.p_list->p_values[i].i_int == newval.i_int &&
1078                 i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
1079             {
1080                 newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
1081                 break;
1082             }
1083         }
1084         var_Change( p_this, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1085     }
1086
1087     p_area = p_input->stream.p_selected_area;
1088     p_input->stream.p_selected_area->i_part = newval.i_int;
1089     vlc_mutex_unlock( &p_input->stream.stream_lock );
1090
1091     input_ChangeArea( p_input, p_area );
1092     input_SetStatus( p_input, INPUT_STATUS_PLAY );
1093
1094     val.b_bool = VLC_TRUE;
1095     var_Set( p_input, "intf-change", val );
1096
1097     return VLC_SUCCESS;
1098 }
1099
1100 static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
1101                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1102 {
1103     input_thread_t *p_input = (input_thread_t *)p_this;
1104     uint16_t i_area_id = (int)p_data;
1105     vlc_value_t val;
1106
1107     vlc_mutex_lock( &p_input->stream.stream_lock );
1108
1109     if( p_input->stream.p_selected_area->i_id == i_area_id &&
1110         oldval.i_int == newval.i_int )
1111     {
1112         /* Nothing to do */
1113         vlc_mutex_unlock( &p_input->stream.stream_lock );
1114         return VLC_SUCCESS;
1115     }
1116
1117     if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
1118         ( (uint16_t)newval.i_int <=
1119           p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
1120     {
1121         input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
1122         p_area->i_part = newval.i_int;
1123         vlc_mutex_unlock( &p_input->stream.stream_lock );
1124         input_ChangeArea( p_input, p_area );
1125         input_SetStatus( p_input, INPUT_STATUS_PLAY );
1126         vlc_mutex_lock( &p_input->stream.stream_lock );
1127     }
1128     vlc_mutex_unlock( &p_input->stream.stream_lock );
1129
1130     val.b_bool = VLC_TRUE;
1131     var_Set( p_input, "intf-change", val );
1132
1133     return VLC_SUCCESS;
1134 }
1135
1136 static int ESCallback( vlc_object_t *p_this, char const *psz_cmd,
1137                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1138 {
1139     input_thread_t *p_input = (input_thread_t *)p_this;
1140     unsigned int i;
1141     vlc_value_t val;
1142     unsigned int i_cat = UNKNOWN_ES;
1143     es_descriptor_t *p_es = NULL;
1144
1145     vlc_mutex_lock( &p_input->stream.stream_lock );
1146
1147     /* First search old es type */
1148     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1149     {
1150         if( p_input->stream.pp_es[i]->i_id == oldval.i_int )
1151         {
1152             i_cat = p_input->stream.pp_es[i]->i_cat;
1153         }
1154     }
1155
1156     /* Unselect all old ES */
1157     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1158     {
1159         if( p_input->stream.pp_es[i]->i_cat == i_cat &&
1160             p_input->stream.pp_es[i]->i_id != newval.i_int &&
1161             p_input->stream.pp_es[i]->p_dec != NULL )
1162         {
1163             input_UnselectES( p_input, p_input->stream.pp_es[i] );
1164         }
1165     }
1166
1167     /* Select new ES */
1168     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1169     {
1170         if( p_input->stream.pp_es[i]->i_id == newval.i_int )
1171         {
1172             p_es = p_input->stream.pp_es[i];
1173             if( p_es->p_dec == NULL )
1174             {
1175                 input_SelectES( p_input, p_es );
1176             }
1177         }
1178     }
1179
1180     if( p_es )
1181     {
1182         /* Fix value (mainly for multiple selected ES */
1183         val.i_int = p_es->i_id;
1184         switch( p_es->i_cat )
1185         {
1186         case AUDIO_ES:
1187             var_Change( p_input, "audio-es", VLC_VAR_SETVALUE, &val, NULL );
1188             break;
1189         case SPU_ES:
1190             var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL );
1191             break;
1192         case VIDEO_ES:
1193             var_Change( p_input, "video-es", VLC_VAR_SETVALUE, &val, NULL );
1194             break;
1195         }
1196     }
1197
1198     vlc_mutex_unlock( &p_input->stream.stream_lock );
1199
1200     val.b_bool = VLC_TRUE;
1201     var_Set( p_input, "intf-change", val );
1202
1203     return VLC_SUCCESS;
1204 }