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