]> git.sesse.net Git - vlc/blob - src/input/es_out.c
* vlc_meta.h: added a few const
[vlc] / src / input / es_out.c
1 /*****************************************************************************
2  * es_out.c: Es Out handler for input.
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@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
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31 #include <vlc/decoder.h>
32
33 #include "input_internal.h"
34
35 #include "vlc_playlist.h"
36 #include "iso_lang.h"
37 /* FIXME we should find a better way than including that */
38 #include "../misc/iso-639_def.h"
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 typedef struct
44 {
45     /* Program ID */
46     int i_id;
47
48     /* Number of es for this pgrm */
49     int i_es;
50
51     vlc_bool_t b_selected;
52
53     /* Clock for this program */
54     input_clock_t clock;
55
56 } es_out_pgrm_t;
57
58 struct es_out_id_t
59 {
60     /* ES ID */
61     int       i_id;
62     es_out_pgrm_t *p_pgrm;
63
64     /* */
65     int64_t i_preroll_end;
66
67     /* Channel in the track type */
68     int         i_channel;
69     es_format_t fmt;
70     char        *psz_language;
71     char        *psz_language_code;
72     decoder_t   *p_dec;
73 };
74
75 struct es_out_sys_t
76 {
77     input_thread_t *p_input;
78
79     /* all programs */
80     int           i_pgrm;
81     es_out_pgrm_t **pgrm;
82     es_out_pgrm_t **pp_selected_pgrm; /* --programs */
83     es_out_pgrm_t *p_pgrm;  /* Master program */
84
85     /* all es */
86     int         i_id;
87     int         i_es;
88     es_out_id_t **es;
89
90     /* mode gestion */
91     vlc_bool_t  b_active;
92     int         i_mode;
93
94     /* es count */
95     int         i_audio;
96     int         i_video;
97     int         i_sub;
98
99     /* es to select */
100     int         i_audio_last;
101     int         i_sub_last;
102     char        **ppsz_audio_language;
103     char        **ppsz_sub_language;
104
105     /* current main es */
106     es_out_id_t *p_es_audio;
107     es_out_id_t *p_es_video;
108     es_out_id_t *p_es_sub;
109
110     /* delay */
111     int64_t i_audio_delay;
112     int64_t i_spu_delay;
113 };
114
115 static es_out_id_t *EsOutAdd    ( es_out_t *, es_format_t * );
116 static int          EsOutSend   ( es_out_t *, es_out_id_t *, block_t * );
117 static void         EsOutDel    ( es_out_t *, es_out_id_t * );
118 static void         EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force );
119 static int          EsOutControl( es_out_t *, int i_query, va_list );
120
121 static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );
122
123 static void EsSelect( es_out_t *out, es_out_id_t *es );
124 static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update );
125 static char *LanguageGetName( const char *psz_code );
126 static char *LanguageGetCode( const char *psz_lang );
127 static char **LanguageSplit( const char *psz_langs );
128 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );
129
130 /*****************************************************************************
131  * input_EsOutNew:
132  *****************************************************************************/
133 es_out_t *input_EsOutNew( input_thread_t *p_input )
134 {
135     es_out_t     *out = malloc( sizeof( es_out_t ) );
136     es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );
137     vlc_value_t  val;
138     int i;
139
140     out->pf_add     = EsOutAdd;
141     out->pf_send    = EsOutSend;
142     out->pf_del     = EsOutDel;
143     out->pf_control = EsOutControl;
144     out->p_sys      = p_sys;
145
146     p_sys->p_input = p_input;
147
148     p_sys->b_active = VLC_FALSE;
149     p_sys->i_mode   = ES_OUT_MODE_AUTO;
150
151
152     p_sys->i_pgrm   = 0;
153     p_sys->pgrm     = NULL;
154     p_sys->p_pgrm   = NULL;
155
156     p_sys->i_id    = 0;
157     p_sys->i_es    = 0;
158     p_sys->es      = NULL;
159
160     p_sys->i_audio = 0;
161     p_sys->i_video = 0;
162     p_sys->i_sub   = 0;
163
164     /* */
165     var_Get( p_input, "audio-track", &val );
166     p_sys->i_audio_last = val.i_int;
167
168     var_Get( p_input, "sub-track", &val );
169     p_sys->i_sub_last = val.i_int;
170
171     var_Get( p_input, "audio-language", &val );
172     p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);
173     if( p_sys->ppsz_audio_language )
174     {
175         for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
176             msg_Dbg( p_input, "select audio in language[%d] %s",
177                      i, p_sys->ppsz_audio_language[i] );
178     }
179     if( val.psz_string ) free( val.psz_string );
180
181     var_Get( p_input, "sub-language", &val );
182     p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);
183     if( p_sys->ppsz_sub_language )
184     {
185         for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
186             msg_Dbg( p_input, "select subtitle in language[%d] %s",
187                      i, p_sys->ppsz_sub_language[i] );
188     }
189     if( val.psz_string ) free( val.psz_string );
190
191     p_sys->p_es_audio = NULL;
192     p_sys->p_es_video = NULL;
193     p_sys->p_es_sub   = NULL;
194
195     p_sys->i_audio_delay= 0;
196     p_sys->i_spu_delay  = 0;
197
198     return out;
199 }
200
201 /*****************************************************************************
202  * input_EsOutDelete:
203  *****************************************************************************/
204 void input_EsOutDelete( es_out_t *out )
205 {
206     es_out_sys_t *p_sys = out->p_sys;
207     int i;
208
209     for( i = 0; i < p_sys->i_es; i++ )
210     {
211         if( p_sys->es[i]->p_dec )
212         {
213             input_DecoderDelete( p_sys->es[i]->p_dec );
214         }
215         if( p_sys->es[i]->psz_language )
216             free( p_sys->es[i]->psz_language );
217         if( p_sys->es[i]->psz_language_code )
218             free( p_sys->es[i]->psz_language_code );
219         es_format_Clean( &p_sys->es[i]->fmt );
220
221         free( p_sys->es[i] );
222     }
223     if( p_sys->ppsz_audio_language )
224     {
225         for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
226             free( p_sys->ppsz_audio_language[i] );
227         free( p_sys->ppsz_audio_language );
228     }
229     if( p_sys->ppsz_sub_language )
230     {
231         for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
232             free( p_sys->ppsz_sub_language[i] );
233         free( p_sys->ppsz_sub_language );
234     }
235
236     if( p_sys->es )
237         free( p_sys->es );
238
239     for( i = 0; i < p_sys->i_pgrm; i++ )
240     {
241         free( p_sys->pgrm[i] );
242     }
243     if( p_sys->pgrm )
244         free( p_sys->pgrm );
245
246     free( p_sys );
247     free( out );
248 }
249
250 es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )
251 {
252     int i;
253     if( i_id < 0 )
254     {
255         /* Special HACK, -i_id is tha cat of the stream */
256         return (es_out_id_t*)((uint8_t*)NULL-i_id);
257     }
258
259     for( i = 0; i < out->p_sys->i_es; i++ )
260     {
261         if( out->p_sys->es[i]->i_id == i_id )
262             return out->p_sys->es[i];
263     }
264     return NULL;
265 }
266
267 void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio )
268 {
269     es_out_sys_t      *p_sys = out->p_sys;
270     int i;
271
272     for( i = 0; i < p_sys->i_es; i++ )
273     {
274         es_out_id_t *es = p_sys->es[i];
275
276         /* Send a dummy block to let decoder know that
277          * there is a discontinuity */
278         if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )
279         {
280             input_DecoderDiscontinuity( es->p_dec );
281         }
282     }
283 }
284
285 void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
286 {
287     es_out_sys_t *p_sys = out->p_sys;
288
289     if( i_cat == AUDIO_ES )
290         p_sys->i_audio_delay = i_delay;
291     else if( i_cat == SPU_ES )
292         p_sys->i_spu_delay = i_delay;
293 }
294
295 vlc_bool_t input_EsOutDecodersEmpty( es_out_t *out )
296 {
297     es_out_sys_t      *p_sys = out->p_sys;
298     int i;
299
300     for( i = 0; i < p_sys->i_es; i++ )
301     {
302         es_out_id_t *es = p_sys->es[i];
303
304         if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )
305             return VLC_FALSE;
306     }
307     return VLC_TRUE;
308 }
309
310 /*****************************************************************************
311  *
312  *****************************************************************************/
313 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
314                               vlc_bool_t b_delete )
315 {
316     es_out_sys_t      *p_sys = out->p_sys;
317     input_thread_t    *p_input = p_sys->p_input;
318     vlc_value_t       val, text;
319
320     char *psz_var;
321
322     if( es->fmt.i_cat == AUDIO_ES )
323         psz_var = "audio-es";
324     else if( es->fmt.i_cat == VIDEO_ES )
325         psz_var = "video-es";
326     else if( es->fmt.i_cat == SPU_ES )
327         psz_var = "spu-es";
328     else
329         return;
330
331     if( b_delete )
332     {
333         val.i_int = es->i_id;
334         var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
335         var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
336         return;
337     }
338
339     /* Get the number of ES already added */
340     var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
341     if( val.i_int == 0 )
342     {
343         vlc_value_t val2;
344
345         /* First one, we need to add the "Disable" choice */
346         val2.i_int = -1; text.psz_string = _("Disable");
347         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
348         val.i_int++;
349     }
350
351     /* Take care of the ES description */
352     if( es->fmt.psz_description && *es->fmt.psz_description )
353     {
354         if( es->psz_language && *es->psz_language )
355         {
356             text.psz_string = malloc( strlen( es->fmt.psz_description) + strlen( es->psz_language ) + 10 );
357             sprintf( text.psz_string, "%s - [%s]", es->fmt.psz_description, es->psz_language );
358         }
359         else text.psz_string = strdup( es->fmt.psz_description );
360     }
361     else
362     {
363         if( es->psz_language && *es->psz_language )
364         {
365             char *temp;
366             text.psz_string = malloc( strlen( _("Track %i") )+ strlen( es->psz_language ) + 30 );
367             asprintf( &temp,  _("Track %i"), val.i_int );
368             sprintf( text.psz_string, "%s - [%s]", temp, es->psz_language );
369             free( temp );
370         }
371         else
372         {
373             text.psz_string = malloc( strlen( _("Track %i") ) + 20 );
374             sprintf( text.psz_string, _("Track %i"), val.i_int );
375         }
376     }
377
378     val.i_int = es->i_id;
379     var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
380
381     free( text.psz_string );
382
383     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
384 }
385
386 /* EsOutProgramSelect:
387  *  Select a program and update the object variable
388  */
389 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
390 {
391     es_out_sys_t      *p_sys = out->p_sys;
392     input_thread_t    *p_input = p_sys->p_input;
393     vlc_value_t       val;
394     int               i;
395
396     if( p_sys->p_pgrm == p_pgrm )
397         return; /* Nothing to do */
398
399     if( p_sys->p_pgrm )
400     {
401         es_out_pgrm_t *old = p_sys->p_pgrm;
402         msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
403
404         for( i = 0; i < p_sys->i_es; i++ )
405         {
406             if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec &&
407                 p_sys->i_mode != ES_OUT_MODE_ALL )
408                 EsUnselect( out, p_sys->es[i], VLC_TRUE );
409         }
410
411         p_sys->p_es_audio = NULL;
412         p_sys->p_es_sub = NULL;
413         p_sys->p_es_video = NULL;
414     }
415
416     msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
417
418     /* Mark it selected */
419     p_pgrm->b_selected = VLC_TRUE;
420
421     /* Switch master stream */
422     if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )
423     {
424         p_sys->p_pgrm->clock.b_master = VLC_FALSE;
425     }
426     p_pgrm->clock.b_master = VLC_TRUE;
427     p_sys->p_pgrm = p_pgrm;
428
429     /* Update "program" */
430     val.i_int = p_pgrm->i_id;
431     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
432
433     /* Update "es-*" */
434     var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
435     var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
436     var_Change( p_input, "spu-es",   VLC_VAR_CLEARCHOICES, NULL, NULL );
437     for( i = 0; i < p_sys->i_es; i++ )
438     {
439         if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
440             EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE );
441         EsOutSelect( out, p_sys->es[i], VLC_FALSE );
442     }
443
444     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
445 }
446
447 /* EsOutAddProgram:
448  *  Add a program
449  */
450 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
451 {
452     es_out_sys_t      *p_sys = out->p_sys;
453     input_thread_t    *p_input = p_sys->p_input;
454     vlc_value_t       val;
455
456     es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
457
458     /* Init */
459     p_pgrm->i_id = i_group;
460     p_pgrm->i_es = 0;
461     p_pgrm->b_selected = VLC_FALSE;
462     input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average );
463
464     /* Append it */
465     TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
466
467     /* Update "program" variable */
468     val.i_int = i_group;
469     var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
470
471     if( i_group == var_GetInteger( p_input, "program" ) )
472     {
473         EsOutProgramSelect( out, p_pgrm );
474     }
475     else
476     {
477         var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
478     }
479     return p_pgrm;
480 }
481
482 /* EsOutProgramMeta:
483  */
484 static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
485 {
486     es_out_sys_t      *p_sys = out->p_sys;
487     input_thread_t    *p_input = p_sys->p_input;
488     char              *psz_cat = malloc( strlen(_("Program")) + 10 );
489     int i;
490
491     msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
492     sprintf( psz_cat, "%s %d", _("Program"), i_group );
493
494     for( i = 0; i < p_meta->i_meta; i++ )
495     {
496         msg_Dbg( p_input, "  - %s = %s", p_meta->name[i], p_meta->value[i] );
497
498         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
499                       _(p_meta->name[i]), "%s", p_meta->value[i] );
500     }
501     free( psz_cat );
502 }
503
504 /* EsOutAdd:
505  *  Add an es_out
506  */
507 static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
508 {
509     es_out_sys_t      *p_sys = out->p_sys;
510     input_thread_t    *p_input = p_sys->p_input;
511
512     es_out_id_t       *es = malloc( sizeof( es_out_id_t ) );
513     es_out_pgrm_t     *p_pgrm = NULL;
514     int i;
515
516     if( fmt->i_group < 0 )
517     {
518         msg_Err( p_input, "invalid group number" );
519         return NULL;
520     }
521
522     /* Search the program */
523     for( i = 0; i < p_sys->i_pgrm; i++ )
524     {
525         if( fmt->i_group == p_sys->pgrm[i]->i_id )
526         {
527             p_pgrm = p_sys->pgrm[i];
528             break;
529         }
530     }
531     if( p_pgrm == NULL )
532     {
533         /* Create a new one */
534         p_pgrm = EsOutProgramAdd( out, fmt->i_group );
535     }
536
537     /* Increase ref count for program */
538     p_pgrm->i_es++;
539
540     /* Set up ES */
541     if( fmt->i_id < 0 )
542         fmt->i_id = out->p_sys->i_id;
543     es->i_id = fmt->i_id;
544     es->p_pgrm = p_pgrm;
545     es_format_Copy( &es->fmt, fmt );
546     es->i_preroll_end = -1;
547
548     switch( fmt->i_cat )
549     {
550     case AUDIO_ES:
551         es->i_channel = p_sys->i_audio;
552         break;
553
554     case VIDEO_ES:
555         es->i_channel = p_sys->i_video;
556         break;
557
558     case SPU_ES:
559         es->i_channel = p_sys->i_sub;
560         break;
561
562     default:
563         es->i_channel = 0;
564         break;
565     }
566     es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */
567     es->psz_language_code = LanguageGetCode( fmt->psz_language );
568     es->p_dec = NULL;
569
570     if( es->p_pgrm == p_sys->p_pgrm )
571         EsOutESVarUpdate( out, es, VLC_FALSE );
572
573     /* Select it if needed */
574     EsOutSelect( out, es, VLC_FALSE );
575
576
577     TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
578     p_sys->i_id++;  /* always incremented */
579     switch( fmt->i_cat )
580     {
581         case AUDIO_ES:
582             p_sys->i_audio++;
583             break;
584         case SPU_ES:
585             p_sys->i_sub++;
586             break;
587         case VIDEO_ES:
588             p_sys->i_video++;
589             break;
590     }
591
592     EsOutAddInfo( out, es );
593
594     return es;
595 }
596
597 static void EsSelect( es_out_t *out, es_out_id_t *es )
598 {
599     es_out_sys_t   *p_sys = out->p_sys;
600     input_thread_t *p_input = p_sys->p_input;
601     vlc_value_t    val;
602     char           *psz_var;
603
604     if( es->p_dec )
605     {
606         msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
607         return;
608     }
609
610     if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
611     {
612         if( !var_GetBool( p_input, "video" ) ||
613             ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) )
614         {
615             msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
616                      es->i_id );
617             return;
618         }
619     }
620     else if( es->fmt.i_cat == AUDIO_ES )
621     {
622         var_Get( p_input, "audio", &val );
623         if( !var_GetBool( p_input, "audio" ) ||
624             ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) )
625         {
626             msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
627                      es->i_id );
628             return;
629         }
630     }
631
632     es->i_preroll_end = -1;
633     es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE );
634     if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
635         return;
636
637     if( es->fmt.i_cat == VIDEO_ES )
638         psz_var = "video-es";
639     else if( es->fmt.i_cat == AUDIO_ES )
640         psz_var = "audio-es";
641     else if( es->fmt.i_cat == SPU_ES )
642         psz_var = "spu-es";
643     else
644         return;
645
646     /* Mark it as selected */
647     val.i_int = es->i_id;
648     var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
649
650
651     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
652 }
653
654 static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update )
655 {
656     es_out_sys_t   *p_sys = out->p_sys;
657     input_thread_t *p_input = p_sys->p_input;
658     vlc_value_t    val;
659     char           *psz_var;
660
661     if( es->p_dec == NULL )
662     {
663         msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
664         return;
665     }
666
667     input_DecoderDelete( es->p_dec );
668     es->p_dec = NULL;
669
670     if( !b_update )
671         return;
672
673     /* Update var */
674     if( es->p_dec == NULL )
675         return;
676     if( es->fmt.i_cat == VIDEO_ES )
677         psz_var = "video-es";
678     else if( es->fmt.i_cat == AUDIO_ES )
679         psz_var = "audio-es";
680     else if( es->fmt.i_cat == SPU_ES )
681         psz_var = "spu-es";
682     else
683         return;
684
685     /* Mark it as selected */
686     val.i_int = -1;
687     var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
688
689     var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
690 }
691
692 /**
693  * Select an ES given the current mode
694  * XXX: you need to take a the lock before (stream.stream_lock)
695  *
696  * \param out The es_out structure
697  * \param es es_out_id structure
698  * \param b_force ...
699  * \return nothing
700  */
701 static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )
702 {
703     es_out_sys_t      *p_sys = out->p_sys;
704
705     int i_cat = es->fmt.i_cat;
706
707     if( !p_sys->b_active ||
708         ( !b_force && es->fmt.i_priority < 0 ) )
709     {
710         return;
711     }
712
713     if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
714     {
715         if( !es->p_dec )
716             EsSelect( out, es );
717     }
718     else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
719     {
720         vlc_value_t val;
721         int i;
722         var_Get( p_sys->p_input, "programs", &val );
723         for ( i = 0; i < val.p_list->i_count; i++ )
724         {
725             if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )
726             {
727                 if( !es->p_dec )
728                     EsSelect( out, es );
729                 break;
730             }
731         }
732         var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );
733     }
734     else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
735     {
736         int i_wanted  = -1;
737
738         if( es->p_pgrm != p_sys->p_pgrm )
739             return;
740
741         if( i_cat == AUDIO_ES )
742         {
743             int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,
744                                      es->psz_language_code );
745
746             if( p_sys->p_es_audio &&
747                 p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
748             {
749                 int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,
750                                          p_sys->p_es_audio->psz_language_code );
751
752                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
753                     return;
754                 i_wanted = es->i_channel;
755             }
756             else
757             {
758                 /* Select audio if (no audio selected yet)
759                  * - no audio-language
760                  * - no audio code for the ES
761                  * - audio code in the requested list */
762                 if( idx1 >= 0 ||
763                     !strcmp( es->psz_language_code, "??" ) ||
764                     !p_sys->ppsz_audio_language )
765                     i_wanted = es->i_channel;
766             }
767
768             if( p_sys->i_audio_last >= 0 )
769                 i_wanted = p_sys->i_audio_last;
770         }
771         else if( i_cat == SPU_ES )
772         {
773             int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,
774                                      es->psz_language_code );
775
776             if( p_sys->p_es_sub &&
777                 p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )
778             {
779                 int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,
780                                          p_sys->p_es_sub->psz_language_code );
781
782                 msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",
783                         idx1, es->psz_language_code, idx2,
784                         p_sys->p_es_sub->psz_language_code );
785
786                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
787                     return;
788                 /* We found a SPU that matches our language request */
789                 i_wanted  = es->i_channel;
790             }
791             else if( idx1 >= 0 )
792             {
793                 msg_Dbg( p_sys->p_input, "idx1=%d(%s)",
794                         idx1, es->psz_language_code );
795
796                 i_wanted  = es->i_channel;
797             }
798             if( p_sys->i_sub_last >= 0 )
799                 i_wanted  = p_sys->i_sub_last;
800         }
801         else if( i_cat == VIDEO_ES )
802         {
803             i_wanted  = es->i_channel;
804         }
805
806         if( i_wanted == es->i_channel && es->p_dec == NULL )
807             EsSelect( out, es );
808     }
809
810     /* FIXME TODO handle priority here */
811     if( es->p_dec )
812     {
813         if( i_cat == AUDIO_ES )
814         {
815             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
816                 p_sys->p_es_audio &&
817                 p_sys->p_es_audio != es &&
818                 p_sys->p_es_audio->p_dec )
819             {
820                 EsUnselect( out, p_sys->p_es_audio, VLC_FALSE );
821             }
822             p_sys->p_es_audio = es;
823         }
824         else if( i_cat == SPU_ES )
825         {
826             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
827                 p_sys->p_es_sub &&
828                 p_sys->p_es_sub != es &&
829                 p_sys->p_es_sub->p_dec )
830             {
831                 EsUnselect( out, p_sys->p_es_sub, VLC_FALSE );
832             }
833             p_sys->p_es_sub = es;
834         }
835         else if( i_cat == VIDEO_ES )
836         {
837             p_sys->p_es_video = es;
838         }
839     }
840 }
841
842 /**
843  * Send a block for the given es_out
844  *
845  * \param out the es_out to send from
846  * \param es the es_out_id
847  * \param p_block the data block to send
848  */
849 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
850 {
851     es_out_sys_t *p_sys = out->p_sys;
852     input_thread_t    *p_input = p_sys->p_input;
853     es_out_pgrm_t *p_pgrm = es->p_pgrm;
854     int64_t i_delay;
855
856     if( es->fmt.i_cat == AUDIO_ES )
857         i_delay = p_sys->i_audio_delay;
858     else if( es->fmt.i_cat == SPU_ES )
859         i_delay = p_sys->i_spu_delay;
860     else
861         i_delay = 0;
862
863     /* Mark preroll blocks */
864     if( es->i_preroll_end >= 0 )
865     {
866         int64_t i_date = p_block->i_pts;
867         if( i_date <= 0 )
868             i_date = p_block->i_dts;
869
870         if( i_date < es->i_preroll_end )
871             p_block->i_flags |= BLOCK_FLAG_PREROLL;
872         else
873             es->i_preroll_end = -1;
874     }
875
876     /* +11 -> avoid null value with non null dts/pts */
877     if( p_block->i_dts > 0 )
878     {
879         p_block->i_dts =
880             input_ClockGetTS( p_input, &p_pgrm->clock,
881                               ( p_block->i_dts + 11 ) * 9 / 100 ) + i_delay;
882     }
883     if( p_block->i_pts > 0 )
884     {
885         p_block->i_pts =
886             input_ClockGetTS( p_input, &p_pgrm->clock,
887                               ( p_block->i_pts + 11 ) * 9 / 100 ) + i_delay;
888     }
889     if ( es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) )
890     {
891         mtime_t current_date = mdate();
892         if( !p_block->i_pts
893                || p_block->i_pts > current_date + 10000000
894                || current_date > p_block->i_pts )
895         {
896             /* ETSI EN 300 472 Annex A : do not take into account the PTS
897              * for teletext streams. */
898             p_block->i_pts = current_date + 400000
899                                + p_input->i_pts_delay + i_delay;
900         }
901     }
902
903     p_block->i_rate = p_input->i_rate;
904
905     /* TODO handle mute */
906     if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES ||
907         p_input->i_rate == INPUT_RATE_DEFAULT ) )
908     {
909         input_DecoderDecode( es->p_dec, p_block );
910     }
911     else
912     {
913         block_Release( p_block );
914     }
915
916     return VLC_SUCCESS;
917 }
918
919 /*****************************************************************************
920  * EsOutDel:
921  *****************************************************************************/
922 static void EsOutDel( es_out_t *out, es_out_id_t *es )
923 {
924     es_out_sys_t *p_sys = out->p_sys;
925
926     /* We don't try to reselect */
927     if( es->p_dec )
928         EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
929
930     if( es->p_pgrm == p_sys->p_pgrm )
931         EsOutESVarUpdate( out, es, VLC_TRUE );
932
933     TAB_REMOVE( p_sys->i_es, p_sys->es, es );
934
935     es->p_pgrm->i_es--;
936     if( es->p_pgrm->i_es == 0 )
937     {
938         msg_Warn( p_sys->p_input, "Program doesn't contain anymore ES, "
939                   "TODO cleaning ?" );
940     }
941
942     if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
943     if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
944     if( p_sys->p_es_sub   == es ) p_sys->p_es_sub   = NULL;
945
946     switch( es->fmt.i_cat )
947     {
948         case AUDIO_ES:
949             p_sys->i_audio--;
950             break;
951         case SPU_ES:
952             p_sys->i_sub--;
953             break;
954         case VIDEO_ES:
955             p_sys->i_video--;
956             break;
957     }
958
959     if( es->psz_language )
960         free( es->psz_language );
961     if( es->psz_language_code )
962         free( es->psz_language_code );
963
964     es_format_Clean( &es->fmt );
965
966     free( es );
967 }
968
969 /**
970  * Control query handler
971  *
972  * \param out the es_out to control
973  * \param i_query A es_out query as defined in include/ninput.h
974  * \param args a variable list of arguments for the query
975  * \return VLC_SUCCESS or an error code
976  */
977 static int EsOutControl( es_out_t *out, int i_query, va_list args )
978 {
979     es_out_sys_t *p_sys = out->p_sys;
980     vlc_bool_t  b, *pb;
981     int         i, *pi;
982
983     es_out_id_t *es;
984
985     switch( i_query )
986     {
987         case ES_OUT_SET_ES_STATE:
988             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
989             b = (vlc_bool_t) va_arg( args, vlc_bool_t );
990             if( b && es->p_dec == NULL )
991             {
992                 EsSelect( out, es );
993                 return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC;
994             }
995             else if( !b && es->p_dec )
996             {
997                 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
998                 return VLC_SUCCESS;
999             }
1000             return VLC_SUCCESS;
1001
1002         case ES_OUT_GET_ES_STATE:
1003             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1004             pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
1005
1006             *pb = es->p_dec ? VLC_TRUE : VLC_FALSE;
1007             return VLC_SUCCESS;
1008
1009         case ES_OUT_SET_ACTIVE:
1010         {
1011             b = (vlc_bool_t) va_arg( args, vlc_bool_t );
1012             p_sys->b_active = b;
1013             /* Needed ? */
1014             if( b )
1015                 var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
1016             return VLC_SUCCESS;
1017         }
1018
1019         case ES_OUT_GET_ACTIVE:
1020             pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
1021             *pb = p_sys->b_active;
1022             return VLC_SUCCESS;
1023
1024         case ES_OUT_SET_MODE:
1025             i = (int) va_arg( args, int );
1026             if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
1027                 i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )
1028             {
1029                 p_sys->i_mode = i;
1030
1031                 /* Reapply policy mode */
1032                 for( i = 0; i < p_sys->i_es; i++ )
1033                 {
1034                     if( p_sys->es[i]->p_dec )
1035                     {
1036                         EsUnselect( out, p_sys->es[i],
1037                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1038                     }
1039                 }
1040                 for( i = 0; i < p_sys->i_es; i++ )
1041                 {
1042                     EsOutSelect( out, p_sys->es[i], VLC_FALSE );
1043                 }
1044                 return VLC_SUCCESS;
1045             }
1046             return VLC_EGENERIC;
1047
1048         case ES_OUT_GET_MODE:
1049             pi = (int*) va_arg( args, int* );
1050             *pi = p_sys->i_mode;
1051             return VLC_SUCCESS;
1052
1053         case ES_OUT_SET_ES:
1054             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1055             /* Special case NULL, NULL+i_cat */
1056             if( es == NULL )
1057             {
1058                 for( i = 0; i < p_sys->i_es; i++ )
1059                 {
1060                     if( p_sys->es[i]->p_dec )
1061                         EsUnselect( out, p_sys->es[i],
1062                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1063                 }
1064             }
1065             else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
1066             {
1067                 for( i = 0; i < p_sys->i_es; i++ )
1068                 {
1069                     if( p_sys->es[i]->p_dec &&
1070                         p_sys->es[i]->fmt.i_cat == AUDIO_ES )
1071                         EsUnselect( out, p_sys->es[i],
1072                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1073                 }
1074             }
1075             else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
1076             {
1077                 for( i = 0; i < p_sys->i_es; i++ )
1078                 {
1079                     if( p_sys->es[i]->p_dec &&
1080                         p_sys->es[i]->fmt.i_cat == VIDEO_ES )
1081                         EsUnselect( out, p_sys->es[i],
1082                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1083                 }
1084             }
1085             else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
1086             {
1087                 for( i = 0; i < p_sys->i_es; i++ )
1088                 {
1089                     if( p_sys->es[i]->p_dec &&
1090                         p_sys->es[i]->fmt.i_cat == SPU_ES )
1091                         EsUnselect( out, p_sys->es[i],
1092                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1093                 }
1094             }
1095             else
1096             {
1097                 for( i = 0; i < p_sys->i_es; i++ )
1098                 {
1099                     if( es == p_sys->es[i] )
1100                     {
1101                         EsOutSelect( out, es, VLC_TRUE );
1102                         break;
1103                     }
1104                 }
1105             }
1106             return VLC_SUCCESS;
1107
1108         case ES_OUT_SET_PCR:
1109         case ES_OUT_SET_GROUP_PCR:
1110         {
1111             es_out_pgrm_t *p_pgrm = NULL;
1112             int            i_group = 0;
1113             int64_t        i_pcr;
1114
1115             if( i_query == ES_OUT_SET_PCR )
1116             {
1117                 p_pgrm = p_sys->p_pgrm;
1118             }
1119             else
1120             {
1121                 int i;
1122                 i_group = (int)va_arg( args, int );
1123                 for( i = 0; i < p_sys->i_pgrm; i++ )
1124                 {
1125                     if( p_sys->pgrm[i]->i_id == i_group )
1126                     {
1127                         p_pgrm = p_sys->pgrm[i];
1128                         break;
1129                     }
1130                 }
1131             }
1132             if( p_pgrm == NULL )
1133                 p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
1134
1135             i_pcr = (int64_t)va_arg( args, int64_t );
1136             /* search program */
1137             /* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */
1138             input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock,
1139                                (i_pcr + 11 ) * 9 / 100);
1140             return VLC_SUCCESS;
1141         }
1142
1143         case ES_OUT_RESET_PCR:
1144             for( i = 0; i < p_sys->i_pgrm; i++ )
1145             {
1146                 p_sys->pgrm[i]->clock.i_synchro_state =  SYNCHRO_REINIT;
1147                 p_sys->pgrm[i]->clock.last_pts = 0;
1148             }
1149             return VLC_SUCCESS;
1150
1151         case ES_OUT_GET_TS:
1152             if( p_sys->p_pgrm )
1153             {
1154                 int64_t i_ts = (int64_t)va_arg( args, int64_t );
1155                 int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * );
1156                 *pi_ts = input_ClockGetTS( p_sys->p_input,
1157                                            &p_sys->p_pgrm->clock,
1158                                            ( i_ts + 11 ) * 9 / 100 );
1159                 return VLC_SUCCESS;
1160             }
1161             return VLC_EGENERIC;
1162
1163         case ES_OUT_GET_GROUP:
1164             pi = (int*) va_arg( args, int* );
1165             if( p_sys->p_pgrm )
1166                 *pi = p_sys->p_pgrm->i_id;
1167             else
1168                 *pi = -1;    /* FIXME */
1169             return VLC_SUCCESS;
1170
1171         case ES_OUT_SET_GROUP:
1172         {
1173             int j;
1174             i = (int) va_arg( args, int );
1175             for( j = 0; j < p_sys->i_pgrm; j++ )
1176             {
1177                 es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
1178                 if( p_pgrm->i_id == i )
1179                 {
1180                     EsOutProgramSelect( out, p_pgrm );
1181                     return VLC_SUCCESS;
1182                 }
1183             }
1184             return VLC_EGENERIC;
1185         }
1186
1187         case ES_OUT_SET_FMT:
1188         {
1189             /* This ain't pretty but is need by some demuxers (eg. Ogg )
1190              * to update the p_extra data */
1191             es_format_t *p_fmt;
1192             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1193             p_fmt = (es_format_t*) va_arg( args, es_format_t * );
1194             if( es == NULL ) return VLC_EGENERIC;
1195
1196             if( p_fmt->i_extra )
1197             {
1198                 es->fmt.i_extra = p_fmt->i_extra;
1199                 es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );
1200                 memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );
1201
1202                 if( !es->p_dec ) return VLC_SUCCESS;
1203
1204 #if 1
1205                 input_DecoderDelete( es->p_dec );
1206                 es->p_dec = input_DecoderNew( p_sys->p_input,
1207                                               &es->fmt, VLC_FALSE );
1208
1209 #else
1210                 es->p_dec->fmt_in.i_extra = p_fmt->i_extra;
1211                 es->p_dec->fmt_in.p_extra =
1212                     realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );
1213                 memcpy( es->p_dec->fmt_in.p_extra,
1214                         p_fmt->p_extra, p_fmt->i_extra );
1215 #endif
1216             }
1217
1218             return VLC_SUCCESS;
1219         }
1220
1221         case ES_OUT_SET_NEXT_DISPLAY_TIME:
1222         {
1223             int64_t i_date;
1224
1225             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1226             i_date = (int64_t)va_arg( args, int64_t );
1227
1228             if( !es || !es->p_dec )
1229                 return VLC_EGENERIC;
1230
1231             es->i_preroll_end = i_date;
1232             input_DecoderPreroll( es->p_dec, i_date );
1233
1234             return VLC_SUCCESS;
1235         }
1236         case ES_OUT_SET_GROUP_META:
1237         {
1238             int i_group = (int)va_arg( args, int );
1239             vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
1240
1241             EsOutProgramMeta( out, i_group, p_meta );
1242             return VLC_SUCCESS;
1243         }
1244
1245         default:
1246             msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
1247             return VLC_EGENERIC;
1248     }
1249 }
1250
1251 /****************************************************************************
1252  * LanguageGetName: try to expend iso639 into plain name
1253  ****************************************************************************/
1254 static char *LanguageGetName( const char *psz_code )
1255 {
1256     const iso639_lang_t *pl;
1257
1258     if( psz_code == NULL )
1259     {
1260         return strdup( "" );
1261     }
1262
1263     if( strlen( psz_code ) == 2 )
1264     {
1265         pl = GetLang_1( psz_code );
1266     }
1267     else if( strlen( psz_code ) == 3 )
1268     {
1269         pl = GetLang_2B( psz_code );
1270         if( !strcmp( pl->psz_iso639_1, "??" ) )
1271         {
1272             pl = GetLang_2T( psz_code );
1273         }
1274     }
1275     else
1276     {
1277         return strdup( psz_code );
1278     }
1279
1280     if( !strcmp( pl->psz_iso639_1, "??" ) )
1281     {
1282        return strdup( psz_code );
1283     }
1284     else
1285     {
1286         if( *pl->psz_native_name )
1287         {
1288             return strdup( pl->psz_native_name );
1289         }
1290         return strdup( pl->psz_eng_name );
1291     }
1292 }
1293
1294 /* Get a 2 char code */
1295 static char *LanguageGetCode( const char *psz_lang )
1296 {
1297     const iso639_lang_t *pl;
1298
1299     if( psz_lang == NULL || *psz_lang == '\0' )
1300         return strdup("??");
1301
1302     for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
1303     {
1304         if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
1305             !strcasecmp( pl->psz_native_name, psz_lang ) ||
1306             !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
1307             !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
1308             !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
1309             break;
1310     }
1311
1312     if( pl->psz_iso639_1 != NULL )
1313         return strdup( pl->psz_iso639_1 );
1314
1315     return strdup("??");
1316 }
1317
1318 static char **LanguageSplit( const char *psz_langs )
1319 {
1320     char *psz_dup;
1321     char *psz_parser;
1322     char **ppsz = NULL;
1323     int i_psz = 0;
1324
1325     if( psz_langs == NULL ) return NULL;
1326
1327     psz_parser = psz_dup = strdup(psz_langs);
1328
1329     while( psz_parser && *psz_parser )
1330     {
1331         char *psz;
1332         char *psz_code;
1333
1334         psz = strchr(psz_parser, ',' );
1335         if( psz ) *psz++ = '\0';
1336
1337         psz_code = LanguageGetCode( psz_parser );
1338         if( strcmp( psz_code, "??" ) )
1339         {
1340             TAB_APPEND( i_psz, ppsz, psz_code );
1341         }
1342
1343         psz_parser = psz;
1344     }
1345
1346     if( i_psz )
1347     {
1348         TAB_APPEND( i_psz, ppsz, NULL );
1349     }
1350
1351     free( psz_dup );
1352     return ppsz;
1353 }
1354
1355 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )
1356 {
1357     int i;
1358
1359     if( !ppsz_langs || !psz_lang ) return -1;
1360
1361     for( i = 0; ppsz_langs[i]; i++ )
1362         if( !strcasecmp( ppsz_langs[i], psz_lang ) ) return i;
1363
1364     return -1;
1365 }
1366
1367 /****************************************************************************
1368  * EsOutAddInfo:
1369  * - add meta info to the playlist item
1370  ****************************************************************************/
1371 static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )
1372 {
1373     es_out_sys_t   *p_sys = out->p_sys;
1374     input_thread_t *p_input = p_sys->p_input;
1375     es_format_t    *fmt = &es->fmt;
1376     char           *psz_cat;
1377
1378     /* Add stream info */
1379     asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 );
1380
1381     input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),
1382                    "%.4s", (char*)&fmt->i_codec );
1383
1384     input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),
1385                    "%s", es->psz_language );
1386
1387     /* Add information */
1388     switch( fmt->i_cat )
1389     {
1390     case AUDIO_ES:
1391         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1392                        _("Type"), _("Audio") );
1393
1394         if( fmt->audio.i_channels > 0 )
1395             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),
1396                            "%d", fmt->audio.i_channels );
1397
1398         if( fmt->audio.i_rate > 0 )
1399             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),
1400                            _("%d Hz"), fmt->audio.i_rate );
1401
1402         if( fmt->audio.i_bitspersample > 0 )
1403             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1404                            _("Bits per sample"), "%d",
1405                            fmt->audio.i_bitspersample );
1406
1407         if( fmt->i_bitrate > 0 )
1408             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),
1409                            _("%d kb/s"), fmt->i_bitrate / 1000 );
1410         break;
1411
1412     case VIDEO_ES:
1413         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1414                        _("Type"), _("Video") );
1415
1416         if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
1417             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1418                            _("Resolution"), "%dx%d",
1419                            fmt->video.i_width, fmt->video.i_height );
1420
1421         if( fmt->video.i_visible_width > 0 &&
1422             fmt->video.i_visible_height > 0 )
1423             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1424                            _("Display resolution"), "%dx%d",
1425                            fmt->video.i_visible_width,
1426                            fmt->video.i_visible_height);
1427         break;
1428
1429     case SPU_ES:
1430         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1431                        _("Type"), _("Subtitle") );
1432         break;
1433
1434     default:
1435         break;
1436     }
1437
1438     free( psz_cat );
1439 }