d89130e9f1515ac26b7b6f6cb3fd71104c313874
[vlc.git] / src / input / input_programs.c
1 /*****************************************************************************
2  * input_programs.c: es_descriptor_t, pgrm_descriptor_t management
3  *****************************************************************************
4  * Copyright (C) 1999-2002 VideoLAN
5  * $Id: input_programs.c,v 1.107 2003/05/05 22:48:23 gbazin 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 val, text;
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.pp_programs = NULL;
71     p_input->stream.p_selected_program = NULL;
72     p_input->stream.p_new_program = NULL;
73
74     if( i_data_len )
75     {
76         if ( (p_input->stream.p_demux_data = malloc( i_data_len )) == NULL )
77         {
78             msg_Err( p_input, "out of memory" );
79             return 1;
80         }
81         memset( p_input->stream.p_demux_data, 0, i_data_len );
82     }
83     else
84     {
85         p_input->stream.p_demux_data = NULL;
86     }
87
88     /* Create a few object variables used for navigation in the interfaces */
89     var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
90     text.psz_string = _("Program");
91     var_Change( p_input, "program", VLC_VAR_SETTEXT, &text, NULL );
92     var_Create( p_input, "title", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
93     text.psz_string = _("Title");
94     var_Change( p_input, "title", VLC_VAR_SETTEXT, &text, NULL );
95     var_Create( p_input, "chapter", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
96     text.psz_string = _("Chapter");
97     var_Change( p_input, "chapter", VLC_VAR_SETTEXT, &text, NULL );
98     var_Create( p_input, "navigation", VLC_VAR_VARIABLE | VLC_VAR_HASCHOICE );
99     text.psz_string = _("Navigation");
100     var_Change( p_input, "navigation", VLC_VAR_SETTEXT, &text, NULL );
101     var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
102     text.psz_string = _("Video track");
103     var_Change( p_input, "video-es", VLC_VAR_SETTEXT, &text, NULL );
104     var_Create( p_input, "audio-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
105     text.psz_string = _("Audio track");
106     var_Change( p_input, "audio-es", VLC_VAR_SETTEXT, &text, NULL );
107     var_Create( p_input, "spu-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
108     text.psz_string = _("Subtitle track");
109     var_Change( p_input, "spu-es", VLC_VAR_SETTEXT, &text, NULL );
110
111     val.i_int = -1; text.psz_string = _("Disable");
112     var_Change( p_input, "video-es", VLC_VAR_ADDCHOICE, &val, &text );
113     var_Change( p_input, "audio-es", VLC_VAR_ADDCHOICE, &val, &text );
114     var_Change( p_input, "spu-es", VLC_VAR_ADDCHOICE, &val, &text );
115
116     var_AddCallback( p_input, "program", ProgramCallback, NULL );
117     var_AddCallback( p_input, "title", TitleCallback, NULL );
118     var_AddCallback( p_input, "chapter", ChapterCallback, NULL );
119     var_AddCallback( p_input, "video-es", ESCallback, NULL );
120     var_AddCallback( p_input, "audio-es", ESCallback, NULL );
121     var_AddCallback( p_input, "spu-es", ESCallback, NULL );
122
123     return 0;
124 }
125
126 /*****************************************************************************
127  * input_EndStream: free all stream descriptors
128  *****************************************************************************/
129 void input_EndStream( input_thread_t * p_input )
130 {
131     /* Free all programs and associated ES, and associated decoders. */
132     while( p_input->stream.i_pgrm_number )
133     {
134         input_DelProgram( p_input, p_input->stream.pp_programs[0] );
135     }
136
137     /* Free standalone ES */
138     while( p_input->stream.i_es_number )
139     {
140         input_DelES( p_input, p_input->stream.pp_es[0] );
141     }
142
143     /* Free all areas */
144     while( p_input->stream.i_area_nb )
145     {
146         input_DelArea( p_input, p_input->stream.pp_areas[0] );
147     }
148
149     /* Free selected ES */
150     if( p_input->stream.pp_selected_es != NULL )
151     {
152         free( p_input->stream.pp_selected_es );
153     }
154
155     if( p_input->stream.p_demux_data != NULL )
156     {
157         free( p_input->stream.p_demux_data );
158     }
159
160     /* Free navigation variables */
161     var_Destroy( p_input, "program" );
162     var_Destroy( p_input, "title" );
163     var_Destroy( p_input, "chapter" );
164     var_Destroy( p_input, "video-es" );
165     var_Destroy( p_input, "audio-es" );
166     var_Destroy( p_input, "spu-es" );
167 }
168
169 /*****************************************************************************
170  * input_FindProgram: returns a pointer to a program described by its ID
171  *****************************************************************************/
172 pgrm_descriptor_t * input_FindProgram( input_thread_t * p_input,
173                                        uint16_t i_pgrm_id )
174 {
175     unsigned int i;
176
177     for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
178     {
179         if( p_input->stream.pp_programs[i]->i_number == i_pgrm_id )
180         {
181             return p_input->stream.pp_programs[i];
182         }
183     }
184
185     return NULL;
186 }
187
188 /*****************************************************************************
189  * input_AddProgram: add and init a program descriptor
190  *****************************************************************************
191  * This program descriptor will be referenced in the given stream descriptor
192  *****************************************************************************/
193 pgrm_descriptor_t * input_AddProgram( input_thread_t * p_input,
194                                       u16 i_pgrm_id, size_t i_data_len )
195 {
196     /* Where to add the pgrm */
197     pgrm_descriptor_t * p_pgrm = malloc( sizeof(pgrm_descriptor_t) );
198     vlc_value_t val;
199
200     if( p_pgrm == NULL )
201     {
202         msg_Err( p_input, "out of memory" );
203         return NULL;
204     }
205
206     /* Init this entry */
207     p_pgrm->i_number = i_pgrm_id;
208     p_pgrm->b_is_ok = 0;
209     p_pgrm->i_version = 0;
210
211     p_pgrm->i_es_number = 0;
212     p_pgrm->pp_es = NULL;
213
214     input_ClockInit( p_pgrm );
215
216     p_pgrm->i_synchro_state = SYNCHRO_START;
217
218     if( i_data_len )
219     {
220         p_pgrm->p_demux_data = malloc( i_data_len );
221         if( p_pgrm->p_demux_data == NULL )
222         {
223             msg_Err( p_input, "out of memory" );
224             return NULL;
225         }
226         memset( p_pgrm->p_demux_data, 0, i_data_len );
227     }
228     else
229     {
230         p_pgrm->p_demux_data = NULL;
231     }
232
233     /* Add an entry to the list of program associated with the stream */
234     INSERT_ELEM( p_input->stream.pp_programs,
235                  p_input->stream.i_pgrm_number,
236                  p_input->stream.i_pgrm_number,
237                  p_pgrm );
238
239     val.i_int = i_pgrm_id;
240     var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
241
242     return p_pgrm;
243 }
244
245 /*****************************************************************************
246  * input_DelProgram: destroy a program descriptor
247  *****************************************************************************
248  * All ES descriptions referenced in the descriptor will be deleted.
249  *****************************************************************************/
250 void input_DelProgram( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm )
251 {
252     unsigned int i_pgrm_index;
253     vlc_value_t val;
254
255     /* Find the program in the programs table */
256     for( i_pgrm_index = 0; i_pgrm_index < p_input->stream.i_pgrm_number;
257          i_pgrm_index++ )
258     {
259         if( p_input->stream.pp_programs[i_pgrm_index] == p_pgrm )
260             break;
261     }
262
263     /* If the program wasn't found, do nothing */
264     if( i_pgrm_index == p_input->stream.i_pgrm_number )
265     {
266         msg_Err( p_input, "program does not belong to this input" );
267         return;
268     }
269
270     val.i_int = i_pgrm_index;
271     var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
272
273     /* Free the structures that describe the es that belongs to that program */
274     while( p_pgrm->i_es_number )
275     {
276         input_DelES( p_input, p_pgrm->pp_es[0] );
277     }
278
279     /* Free the demux data */
280     if( p_pgrm->p_demux_data != NULL )
281     {
282         free( p_pgrm->p_demux_data );
283     }
284
285     /* Remove this program from the stream's list of programs */
286     REMOVE_ELEM( p_input->stream.pp_programs,
287                  p_input->stream.i_pgrm_number,
288                  i_pgrm_index );
289
290     /* Free the description of this program */
291     free( p_pgrm );
292 }
293
294 /*****************************************************************************
295  * input_AddArea: add and init an area descriptor
296  *****************************************************************************
297  * This area descriptor will be referenced in the given stream descriptor
298  *****************************************************************************/
299 input_area_t * input_AddArea( input_thread_t * p_input,
300                               uint16_t i_area_id, uint16_t i_part_nb )
301 {
302     /* Where to add the pgrm */
303     input_area_t * p_area = malloc( sizeof(input_area_t) );
304     vlc_value_t val;
305     int i;
306
307     if( p_area == NULL )
308     {
309         msg_Err( p_input, "out of memory" );
310         return NULL;
311     }
312
313     /* Init this entry */
314     p_area->i_id = i_area_id;
315     p_area->i_part_nb = i_part_nb;
316     p_area->i_part= 0;
317     p_area->i_start = 0;
318     p_area->i_size = 0;
319     p_area->i_tell = 0;
320     p_area->i_seek = NO_SEEK;
321
322     /* Add an entry to the list of program associated with the stream */
323     INSERT_ELEM( p_input->stream.pp_areas,
324                  p_input->stream.i_area_nb,
325                  p_input->stream.i_area_nb,
326                  p_area );
327
328     /* Don't add empty areas */
329     if( i_part_nb == 0 )
330         return NULL;
331
332     /* Take care of the navigation variables */
333     val.i_int = i_area_id;
334     var_Change( p_input, "title", VLC_VAR_ADDCHOICE, &val, NULL );
335
336     val.psz_string = malloc( sizeof("title ") + 5 );
337     if( val.psz_string )
338     {
339         vlc_value_t val2;
340
341         sprintf( val.psz_string, "title %2i", i_area_id );
342         var_Destroy( p_input, val.psz_string );
343         var_Create( p_input, val.psz_string, VLC_VAR_INTEGER |
344                     VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
345         var_AddCallback( p_input, val.psz_string, NavigationCallback,
346                          (void *)(int)i_area_id );
347
348         var_Change( p_input, "navigation", VLC_VAR_ADDCHOICE, &val, NULL );
349
350         for( i = 1; i <= i_part_nb; i++ )
351         {
352             val2.i_int = i;
353             var_Change( p_input, val.psz_string,
354                         VLC_VAR_ADDCHOICE, &val2, NULL );
355         }
356     }
357
358     return p_area;
359 }
360
361 /*****************************************************************************
362  * input_SetProgram: changes the current program
363  *****************************************************************************/
364 int input_SetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_new_prg )
365 {
366     unsigned int i_es_index;
367     int i_required_audio_es;
368     int i_required_spu_es;
369     int i_audio_es = 0;
370     int i_spu_es = 0;
371     vlc_value_t val;
372
373     if ( p_input->stream.p_selected_program )
374     {
375         for ( i_es_index = 1 ; /* 0 should be the PMT */
376                 i_es_index < p_input->stream.p_selected_program->
377                 i_es_number ;
378                 i_es_index ++ )
379         {
380 #define p_es p_input->stream.p_selected_program->pp_es[i_es_index]
381             if ( p_es->p_decoder_fifo ) /* if the ES was selected */
382             {
383                 input_UnselectES( p_input , p_es );
384             }
385 #undef p_es
386         }
387     }
388     /* Get the number of the required audio stream */
389     if( config_GetInt( p_input, "audio" ) )
390     {
391         /* Default is the first one */
392         i_required_audio_es = config_GetInt( p_input, "audio-channel" );
393         if( i_required_audio_es < 0 )
394         {
395             i_required_audio_es = 1;
396         }
397     }
398     else
399     {
400         i_required_audio_es = 0;
401     }
402
403     /* Same thing for subtitles */
404     if( config_GetInt( p_input, "video" ) )
405     {
406         /* for spu, default is none */
407         i_required_spu_es = config_GetInt( p_input, "spu-channel" );
408         if( i_required_spu_es < 0 )
409         {
410             i_required_spu_es = 0;
411         }
412     }
413     else
414     {
415         i_required_spu_es = 0;
416     }
417
418     for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ )
419     {
420         switch( p_new_prg->pp_es[i_es_index]->i_cat )
421         {
422             case VIDEO_ES:
423                 msg_Dbg( p_input, "selecting ES %x",
424                          p_new_prg->pp_es[i_es_index]->i_id );
425                 input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
426                 break;
427             case AUDIO_ES:
428                 i_audio_es += 1;
429                 if( i_audio_es <= i_required_audio_es )
430                 {
431                     msg_Dbg( p_input, "selecting ES %x",
432                              p_new_prg->pp_es[i_es_index]->i_id );
433                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index]);
434                 }
435                 break;
436             /* Not sure this one is fully specification-compliant */
437             case SPU_ES :
438                 i_spu_es += 1;
439                 if( i_spu_es <= i_required_spu_es )
440                 {
441                     msg_Dbg( p_input, "selecting ES %x",
442                              p_new_prg->pp_es[i_es_index]->i_id );
443                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
444                 }
445             break;
446             default :
447                 msg_Dbg( p_input, "ES %x has unknown type",
448                          p_new_prg->pp_es[i_es_index]->i_id );
449                 break;
450         }
451
452     }
453
454
455     p_input->stream.p_selected_program = p_new_prg;
456
457     /* Update the navigation variables without triggering a callback */
458     val.i_int = p_new_prg->i_number;
459     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
460
461     return( 0 );
462 }
463
464 /*****************************************************************************
465  * input_DelArea: destroy a area descriptor
466  *****************************************************************************
467  * All ES descriptions referenced in the descriptor will be deleted.
468  *****************************************************************************/
469 void input_DelArea( input_thread_t * p_input, input_area_t * p_area )
470 {
471     unsigned int i_area_index;
472     vlc_value_t val;
473
474     /* Find the area in the areas table */
475     for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb;
476          i_area_index++ )
477     {
478         if( p_input->stream.pp_areas[i_area_index] == p_area )
479             break;
480     }
481
482     /* If the area wasn't found, do nothing */
483     if( i_area_index == p_input->stream.i_area_nb )
484     {
485         msg_Err( p_input, "area does not belong to this input" );
486         return;
487     }
488
489     /* Take care of the navigation variables */
490     val.psz_string = malloc( sizeof("title ") + 5 );
491     if( val.psz_string )
492     {
493         sprintf( val.psz_string, "title %i", p_area->i_id );
494         var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val, NULL );
495         var_Destroy( p_input, val.psz_string );
496     }
497
498     /* Remove this area from the stream's list of areas */
499     REMOVE_ELEM( p_input->stream.pp_areas,
500                  p_input->stream.i_area_nb,
501                  i_area_index );
502
503     /* Free the description of this area */
504     free( p_area );
505 }
506
507
508 /*****************************************************************************
509  * input_FindES: returns a pointer to an ES described by its ID
510  *****************************************************************************/
511 es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id )
512 {
513     unsigned int i;
514
515     for( i = 0; i < p_input->stream.i_es_number; i++ )
516     {
517         if( p_input->stream.pp_es[i]->i_id == i_es_id )
518         {
519             return p_input->stream.pp_es[i];
520         }
521     }
522
523     return NULL;
524 }
525
526 /*****************************************************************************
527  * input_AddES:
528  *****************************************************************************
529  * Reserve a slot in the table of ES descriptors for the ES and add it to the
530  * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
531  * alone (PSI ?)
532  *****************************************************************************/
533 es_descriptor_t * input_AddES( input_thread_t * p_input,
534                                pgrm_descriptor_t * p_pgrm, u16 i_es_id,
535                                int i_category, char const *psz_desc,
536                                size_t i_data_len )
537 {
538     es_descriptor_t * p_es;
539     vlc_value_t val, text;
540     char *psz_var = NULL;
541
542     p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
543     if( p_es == NULL )
544     {
545         msg_Err( p_input, "out of memory" );
546         return( NULL);
547     }
548
549     INSERT_ELEM( p_input->stream.pp_es,
550                  p_input->stream.i_es_number,
551                  p_input->stream.i_es_number,
552                  p_es );
553
554     /* Init its values */
555     p_es->i_id = i_es_id;
556     p_es->psz_desc = psz_desc ? strdup( psz_desc ) : NULL;
557     p_es->p_pes = NULL;
558     p_es->p_decoder_fifo = NULL;
559     p_es->i_cat = i_category;
560     p_es->i_demux_fd = 0;
561     p_es->c_packets = 0;
562     p_es->c_invalid_packets = 0;
563     p_es->b_force_decoder = VLC_FALSE;
564
565     if( i_data_len )
566     {
567         p_es->p_demux_data = malloc( i_data_len );
568         if( p_es->p_demux_data == NULL )
569         {
570             msg_Err( p_input, "out of memory" );
571             return( NULL );
572         }
573         memset( p_es->p_demux_data, 0, i_data_len );
574     }
575     else
576     {
577         p_es->p_demux_data = NULL;
578     }
579     p_es->p_waveformatex     = NULL;
580     p_es->p_bitmapinfoheader = NULL;
581
582     /* Add this ES to the program definition if one is given */
583     if( p_pgrm )
584     {
585         INSERT_ELEM( p_pgrm->pp_es,
586                      p_pgrm->i_es_number,
587                      p_pgrm->i_es_number,
588                      p_es );
589         p_es->p_pgrm = p_pgrm;
590     }
591     else
592     {
593         p_es->p_pgrm = NULL;
594     }
595
596     switch( i_category )
597     {
598     case AUDIO_ES:
599         psz_var = "audio-es";
600         break;
601     case SPU_ES:
602         psz_var = "spu-es";
603         break;
604     case VIDEO_ES:
605         psz_var = "video-es";
606         break;
607     }
608
609     if( psz_var )
610     {
611         val.i_int = p_es->i_id;
612         text.psz_string = (char *)psz_desc;
613         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
614     }
615
616     return p_es;
617 }
618
619 /*****************************************************************************
620  * input_DelES:
621  *****************************************************************************/
622 void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
623 {
624     unsigned int            i_index, i_es_index;
625     pgrm_descriptor_t *     p_pgrm;
626     char *                  psz_var;
627     vlc_value_t             val;
628
629     /* Find the ES in the ES table */
630     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
631          i_es_index++ )
632     {
633         if( p_input->stream.pp_es[i_es_index] == p_es )
634             break;
635     }
636
637     /* If the ES wasn't found, do nothing */
638     if( i_es_index == p_input->stream.i_es_number )
639     {
640         msg_Err( p_input, "ES does not belong to this input" );
641         return;
642     }
643
644     /* Remove es from its associated variable */
645     switch( p_es->i_cat )
646     {
647     case AUDIO_ES:
648         psz_var = "audio-es";
649         break;
650     case SPU_ES:
651         psz_var = "spu-es";
652         break;
653     case VIDEO_ES:
654     default:
655         psz_var = "video-es";
656         break;
657     }
658     val.i_int = p_es->i_id;
659     var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
660
661     /* Kill associated decoder, if any. */
662     if( p_es->p_decoder_fifo != NULL )
663     {
664         input_EndDecoder( p_input, p_es );
665     }
666
667     /* Remove this ES from the description of the program if it is associated
668      * to one */
669     p_pgrm = p_es->p_pgrm;
670     if( p_pgrm )
671     {
672         for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
673         {
674             if( p_pgrm->pp_es[i_index] == p_es )
675             {
676                 REMOVE_ELEM( p_pgrm->pp_es,
677                              p_pgrm->i_es_number,
678                              i_index );
679                 break;
680             }
681         }
682     }
683
684     /* Free the demux data */
685     if( p_es->p_demux_data != NULL )
686     {
687         free( p_es->p_demux_data );
688     }
689     if( p_es->p_waveformatex )
690     {
691         free( p_es->p_waveformatex );
692     }
693     if( p_es->p_bitmapinfoheader )
694     {
695         free( p_es->p_bitmapinfoheader );
696     }
697
698     /* Free the description string */
699     if( p_es->psz_desc != NULL )
700     {
701         free( p_es->psz_desc );
702     }
703
704     /* Find the ES in the ES table */
705     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
706          i_es_index++ )
707     {
708         if( p_input->stream.pp_es[i_es_index] == p_es )
709             break;
710     }
711
712     /* Remove this ES from the stream's list of ES */
713     REMOVE_ELEM( p_input->stream.pp_es,
714                  p_input->stream.i_es_number,
715                  i_es_index );
716
717     /* Free the ES */
718     free( p_es );
719 }
720
721 /*****************************************************************************
722  * input_SelectES: selects an ES and spawns the associated decoder
723  *****************************************************************************
724  * Remember we are still supposed to have stream_lock when entering this
725  * function ?
726  *****************************************************************************/
727 int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
728 {
729     vlc_value_t val;
730
731     if( p_es == NULL )
732     {
733         msg_Err( p_input, "nothing to do in input_SelectES" );
734         return -1;
735     }
736
737     if( ((p_es->i_cat == VIDEO_ES) || (p_es->i_cat == SPU_ES))
738         && !config_GetInt( p_input, "video" ) )
739     {
740         msg_Dbg( p_input,
741                  "video is disabled, not selecting ES 0x%x", p_es->i_id );
742         return -1;
743     }
744
745     if( (p_es->i_cat == AUDIO_ES) && !config_GetInt( p_input, "audio" ) )
746     {
747         msg_Dbg( p_input,
748                  "audio is disabled, not selecting ES 0x%x", p_es->i_id );
749         return -1;
750     }
751
752     msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
753
754     if( p_es->p_decoder_fifo != NULL )
755     {
756         msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
757         return -1;
758     }
759
760     /* Release the lock, not to block the input thread during
761      * the creation of the thread. */
762     vlc_mutex_unlock( &p_input->stream.stream_lock );
763     p_es->p_decoder_fifo = input_RunDecoder( p_input, p_es );
764     vlc_mutex_lock( &p_input->stream.stream_lock );
765
766     if( p_es->p_decoder_fifo == NULL )
767     {
768         return -1;
769     }
770
771     /* Update the es variable without triggering a callback */
772     val.i_int = p_es->i_id;
773     var_Change( p_input, "audio-es", VLC_VAR_SETVALUE, &val, NULL );
774
775     return 0;
776 }
777
778 /*****************************************************************************
779  * input_UnselectES: removes an ES from the list of selected ES
780  *****************************************************************************/
781 int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
782 {
783
784     unsigned int i_index = 0;
785
786     if( p_es == NULL )
787     {
788         msg_Err( p_input, "nothing to do in input_UnselectES" );
789         return -1;
790     }
791
792     msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
793
794     if( p_es->p_decoder_fifo == NULL )
795     {
796         msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
797         return( -1 );
798     }
799
800     input_EndDecoder( p_input, p_es );
801     p_es->p_pes = NULL;
802
803     if( ( p_es->p_decoder_fifo == NULL ) &&
804         ( p_input->stream.i_selected_es_number > 0 ) )
805     {
806         while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
807                ( p_input->stream.pp_selected_es[i_index] != p_es ) )
808         {
809             i_index++;
810         }
811
812         /* XXX: no need to memmove, we have unsorted data */
813         REMOVE_ELEM( p_input->stream.pp_selected_es,
814                      p_input->stream.i_selected_es_number,
815                      i_index );
816
817         if( p_input->stream.i_selected_es_number == 0 )
818         {
819             msg_Dbg( p_input, "no more selected ES" );
820             return 1;
821         }
822     }
823
824     return 0;
825 }
826
827 /*****************************************************************************
828  * Navigation callback: a bunch of navigation variables are used as an
829  *  alternative to the navigation API.
830  *****************************************************************************/
831 static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
832                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
833 {
834     input_thread_t *p_input = (input_thread_t *)p_this;
835
836     if( oldval.i_int == newval.i_int )
837        return VLC_SUCCESS;
838
839     vlc_mutex_lock( &p_input->stream.stream_lock );
840     if( ( newval.i_int > 0 ) )
841     {
842         vlc_mutex_unlock( &p_input->stream.stream_lock );
843         input_ChangeProgram( p_input, (uint16_t)newval.i_int );
844         input_SetStatus( p_input, INPUT_STATUS_PLAY );
845         vlc_mutex_lock( &p_input->stream.stream_lock );
846     }
847     vlc_mutex_unlock( &p_input->stream.stream_lock );
848
849     return VLC_SUCCESS;
850 }
851
852 static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
853                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
854 {
855     input_thread_t *p_input = (input_thread_t *)p_this;
856     input_area_t *p_area;
857
858     if( oldval.i_int == newval.i_int )
859        return VLC_SUCCESS;
860
861     /* Sanity check should have already be done by var_Set(). */
862     vlc_mutex_lock( &p_input->stream.stream_lock );
863     p_area = p_input->stream.pp_areas[newval.i_int];
864     p_area->i_part = 1;
865     vlc_mutex_unlock( &p_input->stream.stream_lock );
866     input_ChangeArea( p_input, p_area );
867     input_SetStatus( p_input, INPUT_STATUS_PLAY );
868
869     return VLC_SUCCESS;
870 }
871
872 static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
873                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
874 {
875     input_thread_t *p_input = (input_thread_t *)p_this;
876     input_area_t *p_area;
877
878     if( oldval.i_int == newval.i_int )
879        return VLC_SUCCESS;
880
881     /* Sanity check will have already be done by var_Set(). */
882     vlc_mutex_lock( &p_input->stream.stream_lock );
883     p_area = p_input->stream.p_selected_area;
884     p_input->stream.p_selected_area->i_part = newval.i_int;
885     vlc_mutex_unlock( &p_input->stream.stream_lock );
886
887     input_ChangeArea( p_input, p_area );
888     input_SetStatus( p_input, INPUT_STATUS_PLAY );
889
890     return VLC_SUCCESS;
891 }
892
893 static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
894                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
895 {
896     input_thread_t *p_input = (input_thread_t *)p_this;
897     uint16_t i_area_id = (int)p_data;
898
899     vlc_mutex_lock( &p_input->stream.stream_lock );
900
901     if( p_input->stream.p_selected_area->i_id == i_area_id &&
902         oldval.i_int == newval.i_int )
903     {
904         /* Nothing to do */
905         vlc_mutex_unlock( &p_input->stream.stream_lock );
906         return VLC_SUCCESS;
907     }
908
909     if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
910         ( (uint16_t)newval.i_int <=
911           p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
912     {
913         input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
914         p_input->stream.p_selected_area->i_part = newval.i_int;
915         vlc_mutex_unlock( &p_input->stream.stream_lock );
916         input_ChangeArea( p_input, p_area );
917         input_SetStatus( p_input, INPUT_STATUS_PLAY );
918         vlc_mutex_lock( &p_input->stream.stream_lock );
919     }
920     vlc_mutex_unlock( &p_input->stream.stream_lock );
921
922     return VLC_SUCCESS;
923 }
924
925 static int ESCallback( vlc_object_t *p_this, char const *psz_cmd,
926                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
927 {
928     input_thread_t *p_input = (input_thread_t *)p_this;
929     unsigned int i;
930
931     vlc_mutex_lock( &p_input->stream.stream_lock );
932
933     /* Unselect old ES */
934     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
935     {
936         if( p_input->stream.pp_es[i]->i_id == oldval.i_int &&
937             p_input->stream.pp_es[i]->p_decoder_fifo != NULL )
938         {
939             input_UnselectES( p_input, p_input->stream.pp_es[i] );
940         }
941     }
942
943     /* Select new ES */
944     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
945     {
946         if( p_input->stream.pp_es[i]->i_id == newval.i_int &&
947             p_input->stream.pp_es[i]->p_decoder_fifo == NULL )
948         {
949             input_SelectES( p_input, p_input->stream.pp_es[i] );
950         }
951     }
952
953     vlc_mutex_unlock( &p_input->stream.stream_lock );
954
955     return VLC_SUCCESS;
956 }