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