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