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