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