]> git.sesse.net Git - vlc/blob - src/input/input_programs.c
* input_program.c: input_DelES destroyed the decoder (if any) without
[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.114 2003/05/18 23:16:57 fenrir 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     /* Get the number of the required audio stream */
431     if( config_GetInt( p_input, "audio" ) )
432     {
433         /* Default is the first one */
434         i_required_audio_es = config_GetInt( p_input, "audio-channel" );
435         if( i_required_audio_es < 0 )
436         {
437             i_required_audio_es = 1;
438         }
439     }
440     else
441     {
442         i_required_audio_es = 0;
443     }
444
445     /* Same thing for subtitles */
446     if( config_GetInt( p_input, "video" ) )
447     {
448         /* for spu, default is none */
449         i_required_spu_es = config_GetInt( p_input, "spu-channel" );
450         if( i_required_spu_es < 0 )
451         {
452             i_required_spu_es = 0;
453         }
454     }
455     else
456     {
457         i_required_spu_es = 0;
458     }
459
460     for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ )
461     {
462         switch( p_new_prg->pp_es[i_es_index]->i_cat )
463         {
464             case VIDEO_ES:
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                 break;
469             case AUDIO_ES:
470                 i_audio_es += 1;
471                 if( i_audio_es <= i_required_audio_es )
472                 {
473                     msg_Dbg( p_input, "selecting ES %x",
474                              p_new_prg->pp_es[i_es_index]->i_id );
475                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index]);
476                 }
477                 break;
478             /* Not sure this one is fully specification-compliant */
479             case SPU_ES :
480                 i_spu_es += 1;
481                 if( i_spu_es <= i_required_spu_es )
482                 {
483                     msg_Dbg( p_input, "selecting ES %x",
484                              p_new_prg->pp_es[i_es_index]->i_id );
485                     input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
486                 }
487             break;
488             default :
489                 msg_Dbg( p_input, "ES %x has unknown type",
490                          p_new_prg->pp_es[i_es_index]->i_id );
491                 break;
492         }
493
494     }
495
496
497     p_input->stream.p_selected_program = p_new_prg;
498
499     /* Update the navigation variables without triggering a callback */
500     val.i_int = p_new_prg->i_number;
501     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
502
503     return( 0 );
504 }
505
506 /*****************************************************************************
507  * input_DelArea: destroy a area descriptor
508  *****************************************************************************
509  * All ES descriptions referenced in the descriptor will be deleted.
510  *****************************************************************************/
511 void input_DelArea( input_thread_t * p_input, input_area_t * p_area )
512 {
513     unsigned int i_area_index;
514     vlc_value_t val;
515
516     /* Find the area in the areas table */
517     for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb;
518          i_area_index++ )
519     {
520         if( p_input->stream.pp_areas[i_area_index] == p_area )
521             break;
522     }
523
524     /* If the area wasn't found, do nothing */
525     if( i_area_index == p_input->stream.i_area_nb )
526     {
527         msg_Err( p_input, "area does not belong to this input" );
528         return;
529     }
530
531     /* Take care of the navigation variables */
532     val.psz_string = malloc( sizeof("title ") + 5 );
533     if( val.psz_string )
534     {
535         sprintf( val.psz_string, "title %i", p_area->i_id );
536         var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val, NULL );
537         var_Destroy( p_input, val.psz_string );
538     }
539
540     /* Remove this area from the stream's list of areas */
541     REMOVE_ELEM( p_input->stream.pp_areas,
542                  p_input->stream.i_area_nb,
543                  i_area_index );
544
545     /* Free the description of this area */
546     free( p_area );
547
548     if( p_input->stream.i_area_nb == 1 )
549     {
550         /* Del unneeded navigation object variables */
551         var_Destroy( p_input, "next-title" );
552         var_Destroy( p_input, "prev-title" );
553         var_Destroy( p_input, "next-chapter" );
554         var_Destroy( p_input, "prev-chapter" );
555     }
556 }
557
558
559 /*****************************************************************************
560  * input_FindES: returns a pointer to an ES described by its ID
561  *****************************************************************************/
562 es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id )
563 {
564     unsigned int i;
565
566     for( i = 0; i < p_input->stream.i_es_number; i++ )
567     {
568         if( p_input->stream.pp_es[i]->i_id == i_es_id )
569         {
570             return p_input->stream.pp_es[i];
571         }
572     }
573
574     return NULL;
575 }
576
577 /*****************************************************************************
578  * input_AddES:
579  *****************************************************************************
580  * Reserve a slot in the table of ES descriptors for the ES and add it to the
581  * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
582  * alone (PSI ?)
583  *****************************************************************************/
584 es_descriptor_t * input_AddES( input_thread_t * p_input,
585                                pgrm_descriptor_t * p_pgrm, u16 i_es_id,
586                                int i_category, char const *psz_desc,
587                                size_t i_data_len )
588 {
589     es_descriptor_t * p_es;
590     vlc_value_t val, text;
591     char *psz_var = NULL;
592
593     p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
594     if( p_es == NULL )
595     {
596         msg_Err( p_input, "out of memory" );
597         return( NULL);
598     }
599
600     INSERT_ELEM( p_input->stream.pp_es,
601                  p_input->stream.i_es_number,
602                  p_input->stream.i_es_number,
603                  p_es );
604
605     /* Init its values */
606     p_es->i_id = i_es_id;
607     p_es->p_pes = NULL;
608     p_es->p_decoder_fifo = NULL;
609     p_es->i_cat = i_category;
610     p_es->i_demux_fd = 0;
611     p_es->c_packets = 0;
612     p_es->c_invalid_packets = 0;
613     p_es->b_force_decoder = VLC_FALSE;
614
615     if( i_data_len )
616     {
617         p_es->p_demux_data = malloc( i_data_len );
618         if( p_es->p_demux_data == NULL )
619         {
620             msg_Err( p_input, "out of memory" );
621             return( NULL );
622         }
623         memset( p_es->p_demux_data, 0, i_data_len );
624     }
625     else
626     {
627         p_es->p_demux_data = NULL;
628     }
629     p_es->p_waveformatex     = NULL;
630     p_es->p_bitmapinfoheader = NULL;
631
632     /* Add this ES to the program definition if one is given */
633     if( p_pgrm )
634     {
635         INSERT_ELEM( p_pgrm->pp_es,
636                      p_pgrm->i_es_number,
637                      p_pgrm->i_es_number,
638                      p_es );
639         p_es->p_pgrm = p_pgrm;
640     }
641     else
642     {
643         p_es->p_pgrm = NULL;
644     }
645
646     switch( i_category )
647     {
648     case AUDIO_ES:
649         psz_var = "audio-es";
650         break;
651     case SPU_ES:
652         psz_var = "spu-es";
653         break;
654     case VIDEO_ES:
655         psz_var = "video-es";
656         break;
657     }
658
659     if( psz_var )
660     {
661         /* Get the number of ES already added */
662         var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
663         if( val.i_int == 0 )
664         {
665             vlc_value_t val2;
666
667             /* First one, we need to add the "Disable" choice */
668             val2.i_int = -1; text.psz_string = _("Disable");
669             var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
670             val.i_int++;
671         }
672
673         /* Take care of the ES description */
674         if( psz_desc )
675         {
676             p_es->psz_desc = strdup( psz_desc );
677         }
678         else
679         {
680             p_es->psz_desc = malloc( strlen( _("Track %i") ) + 20 );
681             if( p_es->psz_desc )
682                 sprintf( p_es->psz_desc, _("Track %i"), val.i_int );
683         }
684
685         val.i_int = p_es->i_id;
686         text.psz_string = p_es->psz_desc;
687         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
688     }
689     else p_es->psz_desc = NULL;
690
691     return p_es;
692 }
693
694 /*****************************************************************************
695  * input_DelES:
696  *****************************************************************************/
697 void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
698 {
699     unsigned int            i_index, i_es_index;
700     pgrm_descriptor_t *     p_pgrm;
701     char *                  psz_var = NULL;
702     vlc_value_t             val;
703
704     /* Find the ES in the ES table */
705     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
706          i_es_index++ )
707     {
708         if( p_input->stream.pp_es[i_es_index] == p_es )
709             break;
710     }
711
712     /* If the ES wasn't found, do nothing */
713     if( i_es_index == p_input->stream.i_es_number )
714     {
715         msg_Err( p_input, "ES does not belong to this input" );
716         return;
717     }
718
719     /* Remove es from its associated variable */
720     switch( p_es->i_cat )
721     {
722     case AUDIO_ES:
723         psz_var = "audio-es";
724         break;
725     case SPU_ES:
726         psz_var = "spu-es";
727         break;
728     case VIDEO_ES:
729         psz_var = "video-es";
730         break;
731     }
732
733     if( psz_var )
734     {
735         val.i_int = p_es->i_id;
736         var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
737
738         /* Remove the "Disable" entry if needed */
739         var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
740         if( val.i_int == 1 )
741         {
742             val.i_int = -1;
743             var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
744         }
745     }
746
747     /* Kill associated decoder, if any. */
748     if( p_es->p_decoder_fifo != NULL )
749     {
750         input_UnselectES( p_input, p_es );
751     }
752
753     /* Remove this ES from the description of the program if it is associated
754      * to one */
755     p_pgrm = p_es->p_pgrm;
756     if( p_pgrm )
757     {
758         for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
759         {
760             if( p_pgrm->pp_es[i_index] == p_es )
761             {
762                 REMOVE_ELEM( p_pgrm->pp_es,
763                              p_pgrm->i_es_number,
764                              i_index );
765                 break;
766             }
767         }
768     }
769
770     /* Free the demux data */
771     if( p_es->p_demux_data != NULL )
772     {
773         free( p_es->p_demux_data );
774     }
775     if( p_es->p_waveformatex )
776     {
777         free( p_es->p_waveformatex );
778     }
779     if( p_es->p_bitmapinfoheader )
780     {
781         free( p_es->p_bitmapinfoheader );
782     }
783
784     /* Free the description string */
785     if( p_es->psz_desc != NULL )
786     {
787         free( p_es->psz_desc );
788     }
789
790     /* Find the ES in the ES table */
791     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
792          i_es_index++ )
793     {
794         if( p_input->stream.pp_es[i_es_index] == p_es )
795             break;
796     }
797
798     /* Remove this ES from the stream's list of ES */
799     REMOVE_ELEM( p_input->stream.pp_es,
800                  p_input->stream.i_es_number,
801                  i_es_index );
802
803     /* Free the ES */
804     free( p_es );
805 }
806
807 /*****************************************************************************
808  * input_SelectES: selects an ES and spawns the associated decoder
809  *****************************************************************************
810  * Remember we are still supposed to have stream_lock when entering this
811  * function ?
812  *****************************************************************************/
813 int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
814 {
815     vlc_value_t val;
816     char *psz_var = NULL;
817
818     if( p_es == NULL )
819     {
820         msg_Err( p_input, "nothing to do in input_SelectES" );
821         return -1;
822     }
823
824     if( ((p_es->i_cat == VIDEO_ES) || (p_es->i_cat == SPU_ES))
825         && !config_GetInt( p_input, "video" ) )
826     {
827         msg_Dbg( p_input,
828                  "video is disabled, not selecting ES 0x%x", p_es->i_id );
829         return -1;
830     }
831
832     if( (p_es->i_cat == AUDIO_ES) && !config_GetInt( p_input, "audio" ) )
833     {
834         msg_Dbg( p_input,
835                  "audio is disabled, not selecting ES 0x%x", p_es->i_id );
836         return -1;
837     }
838
839     msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
840
841     if( p_es->p_decoder_fifo != NULL )
842     {
843         msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
844         return -1;
845     }
846
847     /* Release the lock, not to block the input thread during
848      * the creation of the thread. */
849     vlc_mutex_unlock( &p_input->stream.stream_lock );
850     p_es->p_decoder_fifo = input_RunDecoder( p_input, p_es );
851     vlc_mutex_lock( &p_input->stream.stream_lock );
852
853     if( p_es->p_decoder_fifo == NULL )
854     {
855         return -1;
856     }
857
858     /* Update the es variable without triggering a callback */
859     switch( p_es->i_cat )
860     {
861     case AUDIO_ES:
862         psz_var = "audio-es";
863         break;
864     case SPU_ES:
865         psz_var = "spu-es";
866         break;
867     case VIDEO_ES:
868         psz_var = "video-es";
869         break;
870     }
871
872     if( psz_var )
873     {
874         val.i_int = p_es->i_id;
875         var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
876     }
877
878     return 0;
879 }
880
881 /*****************************************************************************
882  * input_UnselectES: removes an ES from the list of selected ES
883  *****************************************************************************/
884 int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
885 {
886     unsigned int i_index = 0;
887     vlc_value_t val;
888     char *psz_var = NULL;
889
890     if( p_es == NULL )
891     {
892         msg_Err( p_input, "nothing to do in input_UnselectES" );
893         return -1;
894     }
895
896     msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
897
898     if( p_es->p_decoder_fifo == NULL )
899     {
900         msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
901         return( -1 );
902     }
903
904     /* Update the es variable without triggering a callback */
905     switch( p_es->i_cat )
906     {
907     case AUDIO_ES:
908         psz_var = "audio-es";
909         break;
910     case SPU_ES:
911         psz_var = "spu-es";
912         break;
913     case VIDEO_ES:
914         psz_var = "video-es";
915         break;
916     }
917
918     if( psz_var )
919     {
920         val.i_int = -1;
921         var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
922     }
923
924     /* Actually unselect the ES */
925     input_EndDecoder( p_input, p_es );
926     p_es->p_pes = NULL;
927
928     if( ( p_es->p_decoder_fifo == NULL ) &&
929         ( p_input->stream.i_selected_es_number > 0 ) )
930     {
931         while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
932                ( p_input->stream.pp_selected_es[i_index] != p_es ) )
933         {
934             i_index++;
935         }
936
937         /* XXX: no need to memmove, we have unsorted data */
938         REMOVE_ELEM( p_input->stream.pp_selected_es,
939                      p_input->stream.i_selected_es_number,
940                      i_index );
941
942         if( p_input->stream.i_selected_es_number == 0 )
943         {
944             msg_Dbg( p_input, "no more selected ES" );
945             return 1;
946         }
947     }
948
949     return 0;
950 }
951
952 /*****************************************************************************
953  * Navigation callback: a bunch of navigation variables are used as an
954  *  alternative to the navigation API.
955  *****************************************************************************/
956 static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
957                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
958 {
959     input_thread_t *p_input = (input_thread_t *)p_this;
960     vlc_value_t val;
961
962     if( oldval.i_int == newval.i_int )
963        return VLC_SUCCESS;
964
965     vlc_mutex_lock( &p_input->stream.stream_lock );
966     if( ( newval.i_int > 0 ) )
967     {
968         vlc_mutex_unlock( &p_input->stream.stream_lock );
969         input_ChangeProgram( p_input, (uint16_t)newval.i_int );
970         input_SetStatus( p_input, INPUT_STATUS_PLAY );
971         vlc_mutex_lock( &p_input->stream.stream_lock );
972     }
973     vlc_mutex_unlock( &p_input->stream.stream_lock );
974
975     val.b_bool = VLC_TRUE;
976     var_Set( p_input, "intf-change", val );
977
978     return VLC_SUCCESS;
979 }
980
981 static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
982                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
983 {
984     input_thread_t *p_input = (input_thread_t *)p_this;
985     input_area_t *p_area;
986     vlc_value_t val, val_list;
987     int i, i_step = 0;
988
989     if( !strcmp( psz_cmd, "next-title" ) ) i_step++;
990     else if( !strcmp( psz_cmd, "prev-title" ) ) i_step--;
991
992     if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
993
994     /* Sanity check should have already been done by var_Set(). */
995     vlc_mutex_lock( &p_input->stream.stream_lock );
996
997     if( i_step )
998     {
999         var_Get( p_this, "title", &newval );
1000         var_Change( p_this, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1001         for( i = 0; i < val_list.p_list->i_count; i++ )
1002         {
1003             if( val_list.p_list->p_values[i].i_int == newval.i_int &&
1004                 i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
1005             {
1006                 newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
1007                 break;
1008             }
1009         }
1010         var_Change( p_this, "title", VLC_VAR_FREELIST, &val_list, NULL );
1011     }
1012
1013     p_area = p_input->stream.pp_areas[newval.i_int];
1014     p_area->i_part = 1;
1015
1016     vlc_mutex_unlock( &p_input->stream.stream_lock );
1017
1018     input_ChangeArea( p_input, p_area );
1019     input_SetStatus( p_input, INPUT_STATUS_PLAY );
1020
1021     val.b_bool = VLC_TRUE;
1022     var_Set( p_input, "intf-change", val );
1023
1024     return VLC_SUCCESS;
1025 }
1026
1027 static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
1028                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1029 {
1030     input_thread_t *p_input = (input_thread_t *)p_this;
1031     input_area_t *p_area;
1032     vlc_value_t val, val_list;
1033     int i, i_step = 0;
1034
1035     if( !strcmp( psz_cmd, "next-chapter" ) ) i_step++;
1036     else if( !strcmp( psz_cmd, "prev-chapter" ) ) i_step--;
1037
1038     if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
1039
1040     /* Sanity check should have already been done by var_Set(). */
1041     vlc_mutex_lock( &p_input->stream.stream_lock );
1042
1043     if( i_step )
1044     {
1045         var_Get( p_this, "chapter", &newval );
1046         var_Change( p_this, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1047         for( i = 0; i < val_list.p_list->i_count; i++ )
1048         {
1049             if( val_list.p_list->p_values[i].i_int == newval.i_int &&
1050                 i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
1051             {
1052                 newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
1053                 break;
1054             }
1055         }
1056         var_Change( p_this, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1057     }
1058
1059     p_area = p_input->stream.p_selected_area;
1060     p_input->stream.p_selected_area->i_part = newval.i_int;
1061     vlc_mutex_unlock( &p_input->stream.stream_lock );
1062
1063     input_ChangeArea( p_input, p_area );
1064     input_SetStatus( p_input, INPUT_STATUS_PLAY );
1065
1066     val.b_bool = VLC_TRUE;
1067     var_Set( p_input, "intf-change", val );
1068
1069     return VLC_SUCCESS;
1070 }
1071
1072 static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
1073                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1074 {
1075     input_thread_t *p_input = (input_thread_t *)p_this;
1076     uint16_t i_area_id = (int)p_data;
1077     vlc_value_t val;
1078
1079     vlc_mutex_lock( &p_input->stream.stream_lock );
1080
1081     if( p_input->stream.p_selected_area->i_id == i_area_id &&
1082         oldval.i_int == newval.i_int )
1083     {
1084         /* Nothing to do */
1085         vlc_mutex_unlock( &p_input->stream.stream_lock );
1086         return VLC_SUCCESS;
1087     }
1088
1089     if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
1090         ( (uint16_t)newval.i_int <=
1091           p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
1092     {
1093         input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
1094         p_area->i_part = newval.i_int;
1095         vlc_mutex_unlock( &p_input->stream.stream_lock );
1096         input_ChangeArea( p_input, p_area );
1097         input_SetStatus( p_input, INPUT_STATUS_PLAY );
1098         vlc_mutex_lock( &p_input->stream.stream_lock );
1099     }
1100     vlc_mutex_unlock( &p_input->stream.stream_lock );
1101
1102     val.b_bool = VLC_TRUE;
1103     var_Set( p_input, "intf-change", val );
1104
1105     return VLC_SUCCESS;
1106 }
1107
1108 static int ESCallback( vlc_object_t *p_this, char const *psz_cmd,
1109                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1110 {
1111     input_thread_t *p_input = (input_thread_t *)p_this;
1112     unsigned int i;
1113     vlc_value_t val;
1114
1115     vlc_mutex_lock( &p_input->stream.stream_lock );
1116
1117     /* Unselect old ES */
1118     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1119     {
1120         if( p_input->stream.pp_es[i]->i_id == oldval.i_int &&
1121             p_input->stream.pp_es[i]->p_decoder_fifo != NULL )
1122         {
1123             input_UnselectES( p_input, p_input->stream.pp_es[i] );
1124         }
1125     }
1126
1127     /* Select new ES */
1128     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1129     {
1130         if( p_input->stream.pp_es[i]->i_id == newval.i_int &&
1131             p_input->stream.pp_es[i]->p_decoder_fifo == NULL )
1132         {
1133             input_SelectES( p_input, p_input->stream.pp_es[i] );
1134         }
1135     }
1136
1137     vlc_mutex_unlock( &p_input->stream.stream_lock );
1138
1139     val.b_bool = VLC_TRUE;
1140     var_Set( p_input, "intf-change", val );
1141
1142     return VLC_SUCCESS;
1143 }