]> git.sesse.net Git - vlc/blob - src/input/input_programs.c
* input/input_dec.c: we automaticaly switch to minimize thread mode
[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
604     p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
605     if( p_es == NULL )
606     {
607         msg_Err( p_input, "out of memory" );
608         return( NULL);
609     }
610
611     INSERT_ELEM( p_input->stream.pp_es,
612                  p_input->stream.i_es_number,
613                  p_input->stream.i_es_number,
614                  p_es );
615
616     /* Init its values */
617     p_es->i_id = i_es_id;
618     p_es->i_stream_id = 0;
619     p_es->p_pes = NULL;
620     p_es->p_dec = NULL;
621     p_es->i_cat = i_category;
622     p_es->i_demux_fd = 0;
623     p_es->c_packets = 0;
624     p_es->c_invalid_packets = 0;
625     p_es->b_force_decoder = VLC_FALSE;
626     es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 );
627     p_es->fmt.b_packetized = VLC_FALSE; /* Only there for old mpeg demuxers */
628
629     if( i_data_len )
630     {
631         p_es->p_demux_data = malloc( i_data_len );
632         if( p_es->p_demux_data == NULL )
633         {
634             msg_Err( p_input, "out of memory" );
635             return( NULL );
636         }
637         memset( p_es->p_demux_data, 0, i_data_len );
638     }
639     else
640     {
641         p_es->p_demux_data = NULL;
642     }
643     p_es->p_waveformatex     = NULL;
644     p_es->p_bitmapinfoheader = NULL;
645     p_es->p_spuinfo = NULL;
646
647     /* Add this ES to the program definition if one is given */
648     if( p_pgrm )
649     {
650         INSERT_ELEM( p_pgrm->pp_es,
651                      p_pgrm->i_es_number,
652                      p_pgrm->i_es_number,
653                      p_es );
654         p_es->p_pgrm = p_pgrm;
655     }
656     else
657     {
658         p_es->p_pgrm = NULL;
659     }
660
661     switch( i_category )
662     {
663     case AUDIO_ES:
664         psz_var = "audio-es";
665         break;
666     case SPU_ES:
667         psz_var = "spu-es";
668         break;
669     case VIDEO_ES:
670         psz_var = "video-es";
671         break;
672     }
673
674     if( psz_var )
675     {
676         /* Get the number of ES already added */
677         var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
678         if( val.i_int == 0 )
679         {
680             vlc_value_t val2;
681
682             /* First one, we need to add the "Disable" choice */
683             val2.i_int = -1; text.psz_string = _("Disable");
684             var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
685             val.i_int++;
686         }
687
688         /* Take care of the ES description */
689         if( psz_desc && *psz_desc )
690         {
691             p_es->psz_desc = strdup( psz_desc );
692         }
693         else
694         {
695             p_es->psz_desc = malloc( strlen( _("Track %i") ) + 20 );
696             if( p_es->psz_desc )
697                 sprintf( p_es->psz_desc, _("Track %i"), val.i_int );
698         }
699
700         val.i_int = p_es->i_id;
701         text.psz_string = p_es->psz_desc;
702         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
703     }
704     else p_es->psz_desc = NULL;
705
706     return p_es;
707 }
708
709 /*****************************************************************************
710  * input_DelES:
711  *****************************************************************************/
712 void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
713 {
714     unsigned int            i_index, i_es_index;
715     pgrm_descriptor_t *     p_pgrm;
716     char *                  psz_var = NULL;
717     vlc_value_t             val;
718
719     /* Find the ES in the ES table */
720     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
721          i_es_index++ )
722     {
723         if( p_input->stream.pp_es[i_es_index] == p_es )
724             break;
725     }
726
727     /* If the ES wasn't found, do nothing */
728     if( i_es_index == p_input->stream.i_es_number )
729     {
730         msg_Err( p_input, "ES does not belong to this input" );
731         return;
732     }
733
734     /* Remove es from its associated variable */
735     switch( p_es->i_cat )
736     {
737     case AUDIO_ES:
738         psz_var = "audio-es";
739         break;
740     case SPU_ES:
741         psz_var = "spu-es";
742         break;
743     case VIDEO_ES:
744         psz_var = "video-es";
745         break;
746     }
747
748     if( psz_var )
749     {
750         val.i_int = p_es->i_id;
751         var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
752
753         /* Remove the "Disable" entry if needed */
754         var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
755         if( val.i_int == 1 )
756         {
757             val.i_int = -1;
758             var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
759         }
760     }
761
762     /* Kill associated decoder, if any. */
763     if( p_es->p_dec != NULL )
764     {
765         input_UnselectES( p_input, p_es );
766     }
767
768     /* Remove this ES from the description of the program if it is associated
769      * to one */
770     p_pgrm = p_es->p_pgrm;
771     if( p_pgrm )
772     {
773         for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
774         {
775             if( p_pgrm->pp_es[i_index] == p_es )
776             {
777                 REMOVE_ELEM( p_pgrm->pp_es,
778                              p_pgrm->i_es_number,
779                              i_index );
780                 break;
781             }
782         }
783     }
784
785     /* Free the demux data */
786     if( p_es->p_demux_data != NULL )
787     {
788         free( p_es->p_demux_data );
789     }
790     if( p_es->p_waveformatex )
791     {
792         free( p_es->p_waveformatex );
793     }
794     if( p_es->p_bitmapinfoheader )
795     {
796         free( p_es->p_bitmapinfoheader );
797     }
798     if( p_es->p_spuinfo )
799     {
800         free( p_es->p_spuinfo );
801     }
802
803     /* Free the description string */
804     if( p_es->psz_desc != NULL )
805     {
806         free( p_es->psz_desc );
807     }
808
809     /* Clean the es format */
810     es_format_Clean( &p_es->fmt );
811
812     /* Find the ES in the ES table */
813     for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
814          i_es_index++ )
815     {
816         if( p_input->stream.pp_es[i_es_index] == p_es )
817             break;
818     }
819
820     /* Remove this ES from the stream's list of ES */
821     REMOVE_ELEM( p_input->stream.pp_es,
822                  p_input->stream.i_es_number,
823                  i_es_index );
824
825     /* Free the ES */
826     free( p_es );
827 }
828
829 /*****************************************************************************
830  * input_SelectES: selects an ES and spawns the associated decoder
831  *****************************************************************************
832  * Remember we are still supposed to have stream_lock when entering this
833  * function ?
834  *****************************************************************************/
835 int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
836 {
837     vlc_value_t val;
838     char *psz_var = NULL;
839
840     if( p_es == NULL )
841     {
842         msg_Err( p_input, "nothing to do in input_SelectES" );
843         return -1;
844     }
845
846     if( p_es->i_cat == VIDEO_ES || p_es->i_cat == SPU_ES )
847     {
848         var_Get( p_input, "video", &val );
849         if( val.b_bool && p_input->stream.p_sout )
850         {
851             var_Get( p_input, "sout-video", &val );
852         }
853         if( !val.b_bool )
854         {
855             msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
856                      p_es->i_id );
857             return -1;
858         }
859     }
860
861     if( p_es->i_cat == AUDIO_ES )
862     {
863         var_Get( p_input, "audio", &val );
864         if( val.b_bool && p_input->stream.p_sout )
865         {
866             var_Get( p_input, "sout-audio", &val );
867         }
868         if( !val.b_bool )
869         {
870             msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
871                      p_es->i_id );
872             return -1;
873         }
874     }
875
876     msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
877
878     if( p_es->p_dec != NULL )
879     {
880         msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
881         return -1;
882     }
883
884     /* Release the lock, not to block the input thread during
885      * the creation of the thread. */
886     vlc_mutex_unlock( &p_input->stream.stream_lock );
887     p_es->p_dec = input_RunDecoder( p_input, p_es );
888     vlc_mutex_lock( &p_input->stream.stream_lock );
889
890     if( p_es->p_dec == NULL )
891     {
892         return -1;
893     }
894
895     /* Update the es variable without triggering a callback */
896     switch( p_es->i_cat )
897     {
898     case AUDIO_ES:
899         psz_var = "audio-es";
900         break;
901     case SPU_ES:
902         psz_var = "spu-es";
903         break;
904     case VIDEO_ES:
905         psz_var = "video-es";
906         break;
907     }
908
909     if( psz_var )
910     {
911         val.i_int = p_es->i_id;
912         var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
913     }
914
915     return 0;
916 }
917
918 /*****************************************************************************
919  * input_UnselectES: removes an ES from the list of selected ES
920  *****************************************************************************/
921 int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
922 {
923     unsigned int i_index = 0;
924     vlc_value_t val;
925     char *psz_var = NULL;
926
927     if( p_es == NULL )
928     {
929         msg_Err( p_input, "nothing to do in input_UnselectES" );
930         return -1;
931     }
932
933     msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
934
935     if( p_es->p_dec == NULL )
936     {
937         msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
938         return( -1 );
939     }
940
941     /* Update the es variable without triggering a callback */
942     switch( p_es->i_cat )
943     {
944     case AUDIO_ES:
945         psz_var = "audio-es";
946         break;
947     case SPU_ES:
948         psz_var = "spu-es";
949         break;
950     case VIDEO_ES:
951         psz_var = "video-es";
952         break;
953     }
954
955     if( psz_var )
956     {
957         val.i_int = -1;
958         var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
959     }
960
961     /* Actually unselect the ES */
962     input_EndDecoder( p_input, p_es );
963     p_es->p_pes = NULL;
964
965     if( ( p_es->p_dec == NULL ) &&
966         ( p_input->stream.i_selected_es_number > 0 ) )
967     {
968         while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
969                ( p_input->stream.pp_selected_es[i_index] != p_es ) )
970         {
971             i_index++;
972         }
973
974         /* XXX: no need to memmove, we have unsorted data */
975         REMOVE_ELEM( p_input->stream.pp_selected_es,
976                      p_input->stream.i_selected_es_number,
977                      i_index );
978
979         if( p_input->stream.i_selected_es_number == 0 )
980         {
981             msg_Dbg( p_input, "no more selected ES" );
982             return 1;
983         }
984     }
985
986     return 0;
987 }
988
989 /*****************************************************************************
990  * Navigation callback: a bunch of navigation variables are used as an
991  *  alternative to the navigation API.
992  *****************************************************************************/
993 static int ProgramCallback( 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     vlc_value_t val;
998
999     if( oldval.i_int == newval.i_int )
1000        return VLC_SUCCESS;
1001
1002     vlc_mutex_lock( &p_input->stream.stream_lock );
1003     if( ( newval.i_int > 0 ) )
1004     {
1005         vlc_mutex_unlock( &p_input->stream.stream_lock );
1006         input_ChangeProgram( p_input, (uint16_t)newval.i_int );
1007         input_SetStatus( p_input, INPUT_STATUS_PLAY );
1008         vlc_mutex_lock( &p_input->stream.stream_lock );
1009     }
1010     vlc_mutex_unlock( &p_input->stream.stream_lock );
1011
1012     val.b_bool = VLC_TRUE;
1013     var_Set( p_input, "intf-change", val );
1014
1015     return VLC_SUCCESS;
1016 }
1017
1018 static int TitleCallback( 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     input_area_t *p_area;
1023     vlc_value_t val, val_list;
1024     int i, i_step = 0;
1025
1026     if( !strcmp( psz_cmd, "next-title" ) ) i_step++;
1027     else if( !strcmp( psz_cmd, "prev-title" ) ) i_step--;
1028
1029     if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
1030
1031     /* Sanity check should have already been done by var_Set(). */
1032     vlc_mutex_lock( &p_input->stream.stream_lock );
1033
1034     if( i_step )
1035     {
1036         var_Get( p_this, "title", &newval );
1037         var_Change( p_this, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1038         for( i = 0; i < val_list.p_list->i_count; i++ )
1039         {
1040             if( val_list.p_list->p_values[i].i_int == newval.i_int &&
1041                 i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
1042             {
1043                 newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
1044                 break;
1045             }
1046         }
1047         var_Change( p_this, "title", VLC_VAR_FREELIST, &val_list, NULL );
1048     }
1049
1050     p_area = p_input->stream.pp_areas[newval.i_int];
1051     p_area->i_part = 1;
1052
1053     vlc_mutex_unlock( &p_input->stream.stream_lock );
1054
1055     input_ChangeArea( p_input, p_area );
1056     input_SetStatus( p_input, INPUT_STATUS_PLAY );
1057
1058     val.b_bool = VLC_TRUE;
1059     var_Set( p_input, "intf-change", val );
1060
1061     return VLC_SUCCESS;
1062 }
1063
1064 static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
1065                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1066 {
1067     input_thread_t *p_input = (input_thread_t *)p_this;
1068     input_area_t *p_area;
1069     vlc_value_t val, val_list;
1070     int i, i_step = 0;
1071
1072     if( !strcmp( psz_cmd, "next-chapter" ) ) i_step++;
1073     else if( !strcmp( psz_cmd, "prev-chapter" ) ) i_step--;
1074
1075     if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
1076
1077     /* Sanity check should have already been done by var_Set(). */
1078     vlc_mutex_lock( &p_input->stream.stream_lock );
1079
1080     if( i_step )
1081     {
1082         var_Get( p_this, "chapter", &newval );
1083         var_Change( p_this, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1084         for( i = 0; i < val_list.p_list->i_count; i++ )
1085         {
1086             if( val_list.p_list->p_values[i].i_int == newval.i_int &&
1087                 i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
1088             {
1089                 newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
1090                 break;
1091             }
1092         }
1093         var_Change( p_this, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1094     }
1095
1096     p_area = p_input->stream.p_selected_area;
1097     p_input->stream.p_selected_area->i_part = newval.i_int;
1098     vlc_mutex_unlock( &p_input->stream.stream_lock );
1099
1100     input_ChangeArea( p_input, p_area );
1101     input_SetStatus( p_input, INPUT_STATUS_PLAY );
1102
1103     val.b_bool = VLC_TRUE;
1104     var_Set( p_input, "intf-change", val );
1105
1106     return VLC_SUCCESS;
1107 }
1108
1109 static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
1110                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1111 {
1112     input_thread_t *p_input = (input_thread_t *)p_this;
1113     uint16_t i_area_id = (int)p_data;
1114     vlc_value_t val;
1115
1116     vlc_mutex_lock( &p_input->stream.stream_lock );
1117
1118     if( p_input->stream.p_selected_area->i_id == i_area_id &&
1119         oldval.i_int == newval.i_int )
1120     {
1121         /* Nothing to do */
1122         vlc_mutex_unlock( &p_input->stream.stream_lock );
1123         return VLC_SUCCESS;
1124     }
1125
1126     if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
1127         ( (uint16_t)newval.i_int <=
1128           p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
1129     {
1130         input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
1131         p_area->i_part = newval.i_int;
1132         vlc_mutex_unlock( &p_input->stream.stream_lock );
1133         input_ChangeArea( p_input, p_area );
1134         input_SetStatus( p_input, INPUT_STATUS_PLAY );
1135         vlc_mutex_lock( &p_input->stream.stream_lock );
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 }
1144
1145 static int ESCallback( vlc_object_t *p_this, char const *psz_cmd,
1146                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1147 {
1148     input_thread_t *p_input = (input_thread_t *)p_this;
1149     unsigned int i;
1150     vlc_value_t val;
1151     unsigned int i_cat = UNKNOWN_ES;
1152     es_descriptor_t *p_es = NULL;
1153
1154     vlc_mutex_lock( &p_input->stream.stream_lock );
1155
1156     /* First search old es type */
1157     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1158     {
1159         if( p_input->stream.pp_es[i]->i_id == oldval.i_int )
1160         {
1161             i_cat = p_input->stream.pp_es[i]->i_cat;
1162         }
1163     }
1164
1165     /* Unselect all old ES */
1166     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1167     {
1168         if( p_input->stream.pp_es[i]->i_cat == i_cat &&
1169             p_input->stream.pp_es[i]->i_id != newval.i_int &&
1170             p_input->stream.pp_es[i]->p_dec != NULL )
1171         {
1172             input_UnselectES( p_input, p_input->stream.pp_es[i] );
1173         }
1174     }
1175
1176     /* Select new ES */
1177     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1178     {
1179         if( p_input->stream.pp_es[i]->i_id == newval.i_int )
1180         {
1181             p_es = p_input->stream.pp_es[i];
1182             if( p_es->p_dec == NULL )
1183             {
1184                 input_SelectES( p_input, p_es );
1185             }
1186         }
1187     }
1188
1189     if( p_es )
1190     {
1191         /* Fix value (mainly for multiple selected ES */
1192         val.i_int = p_es->i_id;
1193         switch( p_es->i_cat )
1194         {
1195         case AUDIO_ES:
1196             var_Change( p_input, "audio-es", VLC_VAR_SETVALUE, &val, NULL );
1197             break;
1198         case SPU_ES:
1199             var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL );
1200             break;
1201         case VIDEO_ES:
1202             var_Change( p_input, "video-es", VLC_VAR_SETVALUE, &val, NULL );
1203             break;
1204         }
1205     }
1206
1207     vlc_mutex_unlock( &p_input->stream.stream_lock );
1208
1209     val.b_bool = VLC_TRUE;
1210     var_Set( p_input, "intf-change", val );
1211
1212     return VLC_SUCCESS;
1213 }