]> git.sesse.net Git - vlc/blob - src/input/es_out.c
input: Fix asprintf usage.
[vlc] / src / input / es_out.c
1 /*****************************************************************************
2  * es_out.c: Es Out handler for input.
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33
34 #include <stdio.h>
35
36 #include <vlc_input.h>
37 #include <vlc_es_out.h>
38 #include <vlc_block.h>
39 #include <vlc_aout.h>
40
41 #include "input_internal.h"
42
43 #include <vlc_iso_lang.h>
44 /* FIXME we should find a better way than including that */
45 #include "../text/iso-639_def.h"
46
47 /*****************************************************************************
48  * Local prototypes
49  *****************************************************************************/
50 typedef struct
51 {
52     /* Program ID */
53     int i_id;
54
55     /* Number of es for this pgrm */
56     int i_es;
57
58     bool b_selected;
59
60     /* Clock for this program */
61     input_clock_t clock;
62
63     char    *psz_name;
64     char    *psz_now_playing;
65     char    *psz_publisher;
66
67     vlc_epg_t *p_epg;
68 } es_out_pgrm_t;
69
70 struct es_out_id_t
71 {
72     /* ES ID */
73     int       i_id;
74     es_out_pgrm_t *p_pgrm;
75
76     /* Misc. */
77     int64_t i_preroll_end;
78
79     /* Channel in the track type */
80     int         i_channel;
81     es_format_t fmt;
82     char        *psz_language;
83     char        *psz_language_code;
84
85     decoder_t   *p_dec;
86
87     /* Fields for Video with CC */
88     bool  pb_cc_present[4];
89     es_out_id_t  *pp_cc_es[4];
90
91     /* Field for CC track from a master video */
92     es_out_id_t *p_master;
93 };
94
95 struct es_out_sys_t
96 {
97     input_thread_t *p_input;
98
99     /* all programs */
100     int           i_pgrm;
101     es_out_pgrm_t **pgrm;
102     es_out_pgrm_t **pp_selected_pgrm; /* --programs */
103     es_out_pgrm_t *p_pgrm;  /* Master program */
104
105     /* all es */
106     int         i_id;
107     int         i_es;
108     es_out_id_t **es;
109
110     /* mode gestion */
111     bool  b_active;
112     int         i_mode;
113
114     /* es count */
115     int         i_audio;
116     int         i_video;
117     int         i_sub;
118
119     /* es to select */
120     int         i_audio_last, i_audio_id;
121     int         i_sub_last, i_sub_id;
122     int         i_default_sub_id;   /* As specified in container; if applicable */
123     char        **ppsz_audio_language;
124     char        **ppsz_sub_language;
125
126     /* current main es */
127     es_out_id_t *p_es_audio;
128     es_out_id_t *p_es_video;
129     es_out_id_t *p_es_sub;
130
131     /* delay */
132     int64_t i_audio_delay;
133     int64_t i_spu_delay;
134
135     /* Rate used to rescale ES ts */
136     int         i_rate;
137 };
138
139 static es_out_id_t *EsOutAdd    ( es_out_t *, es_format_t * );
140 static int          EsOutSend   ( es_out_t *, es_out_id_t *, block_t * );
141 static void         EsOutDel    ( es_out_t *, es_out_id_t * );
142 static void         EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force );
143 static int          EsOutControl( es_out_t *, int i_query, va_list );
144
145 static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );
146
147 static bool EsIsSelected( es_out_id_t *es );
148 static void EsSelect( es_out_t *out, es_out_id_t *es );
149 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
150 static char *LanguageGetName( const char *psz_code );
151 static char *LanguageGetCode( const char *psz_lang );
152 static char **LanguageSplit( const char *psz_langs );
153 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );
154
155 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
156
157 static const vlc_fourcc_t EsOutFourccClosedCaptions[4] = {
158     VLC_FOURCC('c', 'c', '1', ' '),
159     VLC_FOURCC('c', 'c', '2', ' '),
160     VLC_FOURCC('c', 'c', '3', ' '),
161     VLC_FOURCC('c', 'c', '4', ' '),
162 };
163 static inline int EsOutGetClosedCaptionsChannel( vlc_fourcc_t fcc )
164 {
165     int i;
166     for( i = 0; i < 4; i++ )
167     {
168         if( fcc == EsOutFourccClosedCaptions[i] )
169             return i;
170     }
171     return -1;
172 }
173
174
175 /*****************************************************************************
176  * input_EsOutNew:
177  *****************************************************************************/
178 es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
179 {
180     vlc_value_t  val;
181     int i;
182
183     es_out_t     *out = malloc( sizeof( es_out_t ) );
184     if( !out ) return NULL;
185
186     es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );
187     if( !p_sys )
188     {
189         free( out );
190         return NULL;
191     }
192
193     out->pf_add     = EsOutAdd;
194     out->pf_send    = EsOutSend;
195     out->pf_del     = EsOutDel;
196     out->pf_control = EsOutControl;
197     out->p_sys      = p_sys;
198     out->b_sout     = p_input->p->p_sout != NULL;
199
200     p_sys->p_input = p_input;
201
202     p_sys->b_active = false;
203     p_sys->i_mode   = ES_OUT_MODE_AUTO;
204
205
206     TAB_INIT( p_sys->i_pgrm, p_sys->pgrm );
207     p_sys->p_pgrm   = NULL;
208
209     p_sys->i_id    = 0;
210
211     TAB_INIT( p_sys->i_es, p_sys->es );
212
213     p_sys->i_audio = 0;
214     p_sys->i_video = 0;
215     p_sys->i_sub   = 0;
216
217     /* */
218     var_Get( p_input, "audio-track", &val );
219     p_sys->i_audio_last = val.i_int;
220
221     var_Get( p_input, "sub-track", &val );
222     p_sys->i_sub_last = val.i_int;
223
224     p_sys->i_default_sub_id   = -1;
225
226     if( !p_input->b_preparsing )
227     {
228         var_Get( p_input, "audio-language", &val );
229         p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);
230         if( p_sys->ppsz_audio_language )
231         {
232             for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
233                 msg_Dbg( p_input, "selected audio language[%d] %s",
234                          i, p_sys->ppsz_audio_language[i] );
235         }
236         free( val.psz_string );
237
238         var_Get( p_input, "sub-language", &val );
239         p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);
240         if( p_sys->ppsz_sub_language )
241         {
242             for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
243                 msg_Dbg( p_input, "selected subtitle language[%d] %s",
244                          i, p_sys->ppsz_sub_language[i] );
245         }
246         free( val.psz_string );
247     }
248     else
249     {
250         p_sys->ppsz_sub_language = NULL;
251         p_sys->ppsz_audio_language = NULL;
252     }
253
254     var_Get( p_input, "audio-track-id", &val );
255     p_sys->i_audio_id = val.i_int;
256
257     var_Get( p_input, "sub-track-id", &val );
258     p_sys->i_sub_id = val.i_int;
259
260     p_sys->p_es_audio = NULL;
261     p_sys->p_es_video = NULL;
262     p_sys->p_es_sub   = NULL;
263
264     p_sys->i_audio_delay= 0;
265     p_sys->i_spu_delay  = 0;
266
267     p_sys->i_rate = i_rate;
268
269     return out;
270 }
271
272 /*****************************************************************************
273  * input_EsOutDelete:
274  *****************************************************************************/
275 void input_EsOutDelete( es_out_t *out )
276 {
277     es_out_sys_t *p_sys = out->p_sys;
278     int i;
279
280     for( i = 0; i < p_sys->i_es; i++ )
281     {
282         if( p_sys->es[i]->p_dec )
283         {
284             input_DecoderDelete( p_sys->es[i]->p_dec );
285         }
286         free( p_sys->es[i]->psz_language );
287         free( p_sys->es[i]->psz_language_code );
288         es_format_Clean( &p_sys->es[i]->fmt );
289
290         free( p_sys->es[i] );
291     }
292     if( p_sys->ppsz_audio_language )
293     {
294         for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
295             free( p_sys->ppsz_audio_language[i] );
296         free( p_sys->ppsz_audio_language );
297     }
298     if( p_sys->ppsz_sub_language )
299     {
300         for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
301             free( p_sys->ppsz_sub_language[i] );
302         free( p_sys->ppsz_sub_language );
303     }
304
305     free( p_sys->es );
306
307     /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
308     for( i = 0; i < p_sys->i_pgrm; i++ )
309     {
310         es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
311         free( p_pgrm->psz_now_playing );
312         free( p_pgrm->psz_publisher );
313         free( p_pgrm->psz_name );
314         if( p_pgrm->p_epg )
315             vlc_epg_Delete( p_pgrm->p_epg );
316
317         free( p_pgrm );
318     }
319     TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
320
321     free( p_sys );
322     free( out );
323 }
324
325 es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )
326 {
327     int i;
328     if( i_id < 0 )
329     {
330         /* Special HACK, -i_id is tha cat of the stream */
331         return (es_out_id_t*)((uint8_t*)NULL-i_id);
332     }
333
334     for( i = 0; i < out->p_sys->i_es; i++ )
335     {
336         if( out->p_sys->es[i]->i_id == i_id )
337             return out->p_sys->es[i];
338     }
339     return NULL;
340 }
341
342 static void EsOutDiscontinuity( es_out_t *out, bool b_flush, bool b_audio )
343 {
344     es_out_sys_t      *p_sys = out->p_sys;
345     int i;
346
347     for( i = 0; i < p_sys->i_es; i++ )
348     {
349         es_out_id_t *es = p_sys->es[i];
350
351         /* Send a dummy block to let decoder know that
352          * there is a discontinuity */
353         if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )
354             input_DecoderDiscontinuity( es->p_dec, b_flush );
355     }
356 }
357 void input_EsOutChangeRate( es_out_t *out, int i_rate )
358 {
359     es_out_sys_t      *p_sys = out->p_sys;
360     int i;
361
362     p_sys->i_rate = i_rate;
363     EsOutDiscontinuity( out, false, false );
364
365     for( i = 0; i < p_sys->i_pgrm; i++ )
366         input_ClockSetRate( &p_sys->pgrm[i]->clock, i_rate );
367 }
368
369 void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
370 {
371     es_out_sys_t *p_sys = out->p_sys;
372
373     if( i_cat == AUDIO_ES )
374         p_sys->i_audio_delay = i_delay;
375     else if( i_cat == SPU_ES )
376         p_sys->i_spu_delay = i_delay;
377 }
378 void input_EsOutChangeState( es_out_t *out )
379 {
380     es_out_sys_t *p_sys = out->p_sys;
381     input_thread_t *p_input = p_sys->p_input;
382
383     if( p_input->i_state  == PAUSE_S )
384     {
385         /* Send discontinuity to decoders (it will allow them to flush
386          *                  * if implemented */
387         EsOutDiscontinuity( out, false, false );
388     }
389     else
390     {
391         /* Out of pause, reset pcr */
392         es_out_Control( out, ES_OUT_RESET_PCR );
393     }
394 }
395 void input_EsOutChangePosition( es_out_t *out )
396 {
397     //es_out_sys_t *p_sys = out->p_sys;
398
399     es_out_Control( out, ES_OUT_RESET_PCR );
400     EsOutDiscontinuity( out, true, false );
401 }
402
403 bool input_EsOutDecodersEmpty( es_out_t *out )
404 {
405     es_out_sys_t      *p_sys = out->p_sys;
406     int i;
407
408     for( i = 0; i < p_sys->i_es; i++ )
409     {
410         es_out_id_t *es = p_sys->es[i];
411
412         if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )
413             return false;
414     }
415     return true;
416 }
417
418 /*****************************************************************************
419  *
420  *****************************************************************************/
421 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
422                                      bool b_delete )
423 {
424     es_out_sys_t      *p_sys = out->p_sys;
425     input_thread_t    *p_input = p_sys->p_input;
426     const  bool b_teletext = fmt->i_cat == SPU_ES && fmt->i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' );
427     vlc_value_t       val, text;
428
429     const char *psz_var;
430
431     if( fmt->i_cat == AUDIO_ES )
432         psz_var = "audio-es";
433     else if( fmt->i_cat == VIDEO_ES )
434         psz_var = "video-es";
435     else if( fmt->i_cat == SPU_ES )
436         psz_var = "spu-es";
437     else
438         return;
439
440     if( b_delete )
441     {
442         if( b_teletext )
443             var_SetInteger( p_sys->p_input, "teletext-es", -1 );
444
445         val.i_int = i_id;
446         var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
447
448         var_SetBool( p_sys->p_input, "intf-change", true );
449         return;
450     }
451
452     /* Get the number of ES already added */
453     var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
454     if( val.i_int == 0 )
455     {
456         vlc_value_t val2;
457
458         /* First one, we need to add the "Disable" choice */
459         val2.i_int = -1; text.psz_string = _("Disable");
460         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
461         val.i_int++;
462     }
463
464     /* Take care of the ES description */
465     if( fmt->psz_description && *fmt->psz_description )
466     {
467         if( psz_language && *psz_language )
468         {
469             text.psz_string = malloc( strlen( fmt->psz_description) +
470                                       strlen( psz_language ) + 10 );
471             sprintf( text.psz_string, "%s - [%s]", fmt->psz_description,
472                                                    psz_language );
473         }
474         else text.psz_string = strdup( fmt->psz_description );
475     }
476     else
477     {
478         if( psz_language && *psz_language )
479         {
480             if( asprintf( &text.psz_string, "%s %i - [%s]", _( "Track" ), val.i_int, psz_language ) == -1 )
481                 text.psz_string = NULL;
482         }
483         else
484         {
485             if( asprintf( &text.psz_string, "%s %i", _( "Track" ), val.i_int ) == -1 )
486                 text.psz_string = NULL;
487         }
488     }
489
490     val.i_int = i_id;
491     var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
492
493     free( text.psz_string );
494
495     if( b_teletext )
496         var_SetInteger( p_sys->p_input, "teletext-es", i_id );
497
498     var_SetBool( p_sys->p_input, "intf-change", true );
499 }
500
501 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
502                               bool b_delete )
503 {
504     EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete );
505 }
506
507 /* EsOutProgramSelect:
508  *  Select a program and update the object variable
509  */
510 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
511 {
512     es_out_sys_t      *p_sys = out->p_sys;
513     input_thread_t    *p_input = p_sys->p_input;
514     vlc_value_t       val;
515     int               i;
516
517     if( p_sys->p_pgrm == p_pgrm )
518         return; /* Nothing to do */
519
520     if( p_sys->p_pgrm )
521     {
522         es_out_pgrm_t *old = p_sys->p_pgrm;
523         msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
524
525         for( i = 0; i < p_sys->i_es; i++ )
526         {
527             if( p_sys->es[i]->p_pgrm == old && EsIsSelected( p_sys->es[i] ) &&
528                 p_sys->i_mode != ES_OUT_MODE_ALL )
529                 EsUnselect( out, p_sys->es[i], true );
530         }
531
532         p_sys->p_es_audio = NULL;
533         p_sys->p_es_sub = NULL;
534         p_sys->p_es_video = NULL;
535     }
536
537     msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
538
539     /* Mark it selected */
540     p_pgrm->b_selected = true;
541
542     /* Switch master stream */
543     if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )
544     {
545         p_sys->p_pgrm->clock.b_master = false;
546     }
547     p_pgrm->clock.b_master = true;
548     p_sys->p_pgrm = p_pgrm;
549
550     /* Update "program" */
551     val.i_int = p_pgrm->i_id;
552     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
553
554     /* Update "es-*" */
555     var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
556     var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
557     var_Change( p_input, "spu-es",   VLC_VAR_CLEARCHOICES, NULL, NULL );
558     var_SetInteger( p_input, "teletext-es", -1 );
559     for( i = 0; i < p_sys->i_es; i++ )
560     {
561         if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
562             EsOutESVarUpdate( out, p_sys->es[i], false );
563         EsOutSelect( out, p_sys->es[i], false );
564     }
565
566     /* Update now playing */
567     input_item_SetNowPlaying( p_input->p->input.p_item,
568                               p_pgrm->psz_now_playing );
569     input_item_SetPublisher( p_input->p->input.p_item,
570                              p_pgrm->psz_publisher );
571
572     var_SetBool( p_sys->p_input, "intf-change", true );
573 }
574
575 /* EsOutAddProgram:
576  *  Add a program
577  */
578 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
579 {
580     es_out_sys_t      *p_sys = out->p_sys;
581     input_thread_t    *p_input = p_sys->p_input;
582     vlc_value_t       val;
583
584     es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
585     if( !p_pgrm ) return NULL;
586
587     /* Init */
588     p_pgrm->i_id = i_group;
589     p_pgrm->i_es = 0;
590     p_pgrm->b_selected = false;
591     p_pgrm->psz_name = NULL;
592     p_pgrm->psz_now_playing = NULL;
593     p_pgrm->psz_publisher = NULL;
594     p_pgrm->p_epg = NULL;
595     input_ClockInit( &p_pgrm->clock, false, p_input->p->input.i_cr_average, p_sys->i_rate );
596
597     /* Append it */
598     TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
599
600     /* Update "program" variable */
601     val.i_int = i_group;
602     var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
603
604     if( i_group == var_GetInteger( p_input, "program" ) )
605     {
606         EsOutProgramSelect( out, p_pgrm );
607     }
608     else
609     {
610         var_SetBool( p_sys->p_input, "intf-change", true );
611     }
612     return p_pgrm;
613 }
614
615 /* EsOutDelProgram:
616  *  Delete a program
617  */
618 static int EsOutProgramDel( es_out_t *out, int i_group )
619 {
620     es_out_sys_t      *p_sys = out->p_sys;
621     input_thread_t    *p_input = p_sys->p_input;
622     es_out_pgrm_t     *p_pgrm = NULL;
623     vlc_value_t       val;
624     int               i;
625
626     for( i = 0; i < p_sys->i_pgrm; i++ )
627     {
628         if( p_sys->pgrm[i]->i_id == i_group )
629         {
630             p_pgrm = p_sys->pgrm[i];
631             break;
632         }
633     }
634
635     if( p_pgrm == NULL )
636         return VLC_EGENERIC;
637
638     if( p_pgrm->i_es )
639     {
640         msg_Dbg( p_input, "can't delete program %d which still has %i ES",
641                  i_group, p_pgrm->i_es );
642         return VLC_EGENERIC;
643     }
644
645     TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
646
647     /* If program is selected we need to unselect it */
648     if( p_sys->p_pgrm == p_pgrm ) p_sys->p_pgrm = NULL;
649
650     free( p_pgrm->psz_name );
651     free( p_pgrm->psz_now_playing );
652     free( p_pgrm->psz_publisher );
653     if( p_pgrm->p_epg )
654         vlc_epg_Delete( p_pgrm->p_epg );
655     free( p_pgrm );
656
657     /* Update "program" variable */
658     val.i_int = i_group;
659     var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
660
661     var_SetBool( p_sys->p_input, "intf-change", true );
662
663     return VLC_SUCCESS;
664 }
665
666 /* EsOutProgramMeta:
667  */
668 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
669 {
670     char *psz = NULL;
671     if( p_pgrm->psz_name )
672     {
673         if( asprintf( &psz, _("%s [%s %d]"), p_pgrm->psz_name, _("Program"), p_pgrm->i_id ) == -1 )
674             psz = NULL;
675     }
676     else
677     {
678         if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
679             psz = NULL;
680     }
681     return psz;
682 }
683
684 static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
685 {
686     es_out_sys_t      *p_sys = out->p_sys;
687     es_out_pgrm_t     *p_pgrm = NULL;
688     input_thread_t    *p_input = p_sys->p_input;
689     char              *psz_cat;
690     const char        *psz_title = NULL;
691     const char        *psz_provider = NULL;
692     int i;
693
694     msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
695
696     /* Check against empty meta data (empty for what we handle) */
697     if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
698         !vlc_meta_Get( p_meta, vlc_meta_NowPlaying) &&
699         !vlc_meta_Get( p_meta, vlc_meta_Publisher) &&
700         vlc_dictionary_keys_count( &p_meta->extra_tags ) <= 0 )
701     {
702         return;
703     }
704     /* Find program */
705     for( i = 0; i < p_sys->i_pgrm; i++ )
706     {
707         if( p_sys->pgrm[i]->i_id == i_group )
708         {
709             p_pgrm = p_sys->pgrm[i];
710             break;
711         }
712     }
713     if( p_pgrm == NULL )
714         p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
715
716     /* */
717     psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
718     psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
719
720     /* Update the description text of the program */
721     if( psz_title && *psz_title )
722     {
723         vlc_value_t val;
724         vlc_value_t text;
725
726         if( !p_pgrm->psz_name || strcmp( p_pgrm->psz_name, psz_title ) )
727         {
728             char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
729
730             /* Remove old entries */
731             input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL );
732             /* TODO update epg name */
733             free( psz_cat );
734         }
735         free( p_pgrm->psz_name );
736         p_pgrm->psz_name = strdup( psz_title );
737
738         /* ugly but it works */
739         val.i_int = i_group;
740         var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
741
742         if( psz_provider && *psz_provider )
743         {
744             if( asprintf( &text.psz_string, "%s [%s]", psz_title, psz_provider ) != -1 )
745             {
746                 var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
747                 free( text.psz_string );
748             }
749         }
750         else
751         {
752             text.psz_string = (char *)psz_title;
753             var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
754         }
755     }
756
757     psz_cat = EsOutProgramGetMetaName( p_pgrm );
758     if( psz_provider )
759     {
760         if( p_sys->p_pgrm == p_pgrm )
761             input_item_SetPublisher( p_input->p->input.p_item, psz_provider );
762         input_Control( p_input, INPUT_ADD_INFO, psz_cat, input_MetaTypeToLocalizedString(vlc_meta_Publisher), psz_provider );
763     }
764     char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags );
765     for( i = 0; ppsz_all_keys[i]; i++ )
766     {
767         input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(ppsz_all_keys[i]),
768                        vlc_dictionary_value_for_key( &p_meta->extra_tags, ppsz_all_keys[i] ) );
769         free( ppsz_all_keys[i] );
770     }
771     free( ppsz_all_keys );
772
773     free( psz_cat );
774 }
775
776 static void vlc_epg_Merge( vlc_epg_t *p_dst, const vlc_epg_t *p_src )
777 {
778     int i;
779
780     /* Add new event */
781     for( i = 0; i < p_src->i_event; i++ )
782     {
783         vlc_epg_event_t *p_evt = p_src->pp_event[i];
784         bool b_add = true;
785         int j;
786
787         for( j = 0; j < p_dst->i_event; j++ )
788         {
789             if( p_dst->pp_event[j]->i_start == p_evt->i_start && p_dst->pp_event[j]->i_duration == p_evt->i_duration )
790             {
791                 b_add = false;
792                 break;
793             }
794             if( p_dst->pp_event[j]->i_start > p_evt->i_start )
795                 break;
796         }
797         if( b_add )
798         {
799             vlc_epg_event_t *p_copy = malloc( sizeof(vlc_epg_event_t) );
800             if( !p_copy )
801                 break;
802             memset( p_copy, 0, sizeof(vlc_epg_event_t) );
803             p_copy->i_start = p_evt->i_start;
804             p_copy->i_duration = p_evt->i_duration;
805             p_copy->psz_name = p_evt->psz_name ? strdup( p_evt->psz_name ) : NULL;
806             p_copy->psz_short_description = p_evt->psz_short_description ? strdup( p_evt->psz_short_description ) : NULL;
807             p_copy->psz_description = p_evt->psz_description ? strdup( p_evt->psz_description ) : NULL;
808             TAB_INSERT( p_dst->i_event, p_dst->pp_event, p_copy, j );
809         }
810     }
811     /* Update current */
812     vlc_epg_SetCurrent( p_dst, p_src->p_current ? p_src->p_current->i_start : -1 );
813
814     /* Keep only 1 old event  */
815     if( p_dst->p_current )
816     {
817         while( p_dst->i_event > 1 && p_dst->pp_event[0] != p_dst->p_current && p_dst->pp_event[1] != p_dst->p_current )
818             TAB_REMOVE( p_dst->i_event, p_dst->pp_event, p_dst->pp_event[0] );
819     }
820 }
821
822 static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg )
823 {
824     es_out_sys_t      *p_sys = out->p_sys;
825     input_thread_t    *p_input = p_sys->p_input;
826     es_out_pgrm_t     *p_pgrm = NULL;
827     char *psz_cat;
828     int i;
829
830     /* Find program */
831     for( i = 0; i < p_sys->i_pgrm; i++ )
832     {
833         if( p_sys->pgrm[i]->i_id == i_group )
834         {
835             p_pgrm = p_sys->pgrm[i];
836             break;
837         }
838     }
839     if( p_pgrm == NULL )
840         p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
841
842     /* Merge EPG */
843     if( !p_pgrm->p_epg )
844         p_pgrm->p_epg = vlc_epg_New( p_pgrm->psz_name );
845     vlc_epg_Merge( p_pgrm->p_epg, p_epg );
846
847     /* Update info */
848     psz_cat = EsOutProgramGetMetaName( p_pgrm );
849 #ifdef HAVE_LOCALTIME_R
850     char *psz_epg;
851     if( asprintf( &psz_epg, "EPG %s", psz_cat ) == -1 )
852         psz_epg = NULL;
853     input_Control( p_input, INPUT_DEL_INFO, psz_epg, NULL );
854     msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, p_pgrm->p_epg->psz_name );
855     for( i = 0; i < p_pgrm->p_epg->i_event; i++ )
856     {
857         const vlc_epg_event_t *p_evt = p_pgrm->p_epg->pp_event[i];
858         time_t t_start = (time_t)p_evt->i_start;
859         struct tm tm_start;
860         char psz_start[128];
861
862         localtime_r( &t_start, &tm_start );
863
864         snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d:%2.2d", tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
865         if( p_evt->psz_short_description || p_evt->psz_description )
866             input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s",
867                            p_evt->psz_name,
868                            p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
869                            p_evt->psz_short_description ? p_evt->psz_short_description : p_evt->psz_description );
870         else
871             input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
872                            p_evt->psz_name,
873                            p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
874     }
875     free( psz_epg );
876 #endif
877     /* Update now playing */
878     free( p_pgrm->psz_now_playing );
879     p_pgrm->psz_now_playing = NULL;
880     if( p_epg->p_current && p_epg->p_current->psz_name && *p_epg->p_current->psz_name )
881         p_pgrm->psz_now_playing = strdup( p_epg->p_current->psz_name );
882
883     if( p_pgrm == p_sys->p_pgrm )
884         input_item_SetNowPlaying( p_input->p->input.p_item, p_pgrm->psz_now_playing );
885
886     if( p_pgrm->psz_now_playing )
887     {
888         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
889             input_MetaTypeToLocalizedString(vlc_meta_NowPlaying),
890             p_pgrm->psz_now_playing );
891     }
892     else
893     {
894         input_Control( p_input, INPUT_DEL_INFO, psz_cat,
895             input_MetaTypeToLocalizedString(vlc_meta_NowPlaying) );
896     }
897
898     free( psz_cat );
899 }
900
901 /* EsOutAdd:
902  *  Add an es_out
903  */
904 static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
905 {
906     es_out_sys_t      *p_sys = out->p_sys;
907     input_thread_t    *p_input = p_sys->p_input;
908
909     es_out_id_t       *es = malloc( sizeof( es_out_id_t ) );
910     es_out_pgrm_t     *p_pgrm = NULL;
911     int i;
912
913     if( !es ) return NULL;
914
915     if( fmt->i_group < 0 )
916     {
917         msg_Err( p_input, "invalid group number" );
918         free( es );
919         return NULL;
920     }
921
922     /* Search the program */
923     for( i = 0; i < p_sys->i_pgrm; i++ )
924     {
925         if( fmt->i_group == p_sys->pgrm[i]->i_id )
926         {
927             p_pgrm = p_sys->pgrm[i];
928             break;
929         }
930     }
931     if( p_pgrm == NULL )
932     {
933         /* Create a new one */
934         p_pgrm = EsOutProgramAdd( out, fmt->i_group );
935     }
936
937     /* Increase ref count for program */
938     p_pgrm->i_es++;
939
940     /* Set up ES */
941     if( fmt->i_id < 0 )
942         fmt->i_id = out->p_sys->i_id;
943     es->i_id = fmt->i_id;
944     es->p_pgrm = p_pgrm;
945     es_format_Copy( &es->fmt, fmt );
946     es->i_preroll_end = -1;
947
948     switch( fmt->i_cat )
949     {
950     case AUDIO_ES:
951     {
952         audio_replay_gain_t rg;
953
954         es->i_channel = p_sys->i_audio;
955
956         vlc_mutex_lock( &p_input->p->input.p_item->lock );
957         memset( &rg, 0, sizeof(rg) );
958         vlc_audio_replay_gain_MergeFromMeta( &rg, p_input->p->input.p_item->p_meta );
959         vlc_mutex_unlock( &p_input->p->input.p_item->lock );
960
961         for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
962         {
963             if( !es->fmt.audio_replay_gain.pb_peak[i] )
964             {
965                 es->fmt.audio_replay_gain.pb_peak[i] = rg.pb_peak[i];
966                 es->fmt.audio_replay_gain.pf_peak[i] = rg.pf_peak[i];
967             }
968             if( !es->fmt.audio_replay_gain.pb_gain[i] )
969             {
970                 es->fmt.audio_replay_gain.pb_gain[i] = rg.pb_gain[i];
971                 es->fmt.audio_replay_gain.pf_gain[i] = rg.pf_gain[i];
972             }
973         }
974         break;
975     }
976
977     case VIDEO_ES:
978         es->i_channel = p_sys->i_video;
979         if( fmt->video.i_frame_rate && fmt->video.i_frame_rate_base )
980             vlc_ureduce( &es->fmt.video.i_frame_rate,
981                          &es->fmt.video.i_frame_rate_base,
982                          fmt->video.i_frame_rate,
983                          fmt->video.i_frame_rate_base, 0 );
984         break;
985
986     case SPU_ES:
987         es->i_channel = p_sys->i_sub;
988         break;
989
990     default:
991         es->i_channel = 0;
992         break;
993     }
994     es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */
995     es->psz_language_code = LanguageGetCode( fmt->psz_language );
996     es->p_dec = NULL;
997     for( i = 0; i < 4; i++ )
998         es->pb_cc_present[i] = false;
999     es->p_master = false;
1000
1001     if( es->p_pgrm == p_sys->p_pgrm )
1002         EsOutESVarUpdate( out, es, false );
1003
1004     /* Select it if needed */
1005     EsOutSelect( out, es, false );
1006
1007
1008     TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
1009     p_sys->i_id++;  /* always incremented */
1010     switch( fmt->i_cat )
1011     {
1012         case AUDIO_ES:
1013             p_sys->i_audio++;
1014             break;
1015         case SPU_ES:
1016             p_sys->i_sub++;
1017             break;
1018         case VIDEO_ES:
1019             p_sys->i_video++;
1020             break;
1021     }
1022
1023     EsOutAddInfo( out, es );
1024
1025     return es;
1026 }
1027
1028 static bool EsIsSelected( es_out_id_t *es )
1029 {
1030     if( es->p_master )
1031     {
1032         bool b_decode = false;
1033         if( es->p_master->p_dec )
1034         {
1035             int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1036             if( i_channel != -1 )
1037                 input_DecoderGetCcState( es->p_master->p_dec, &b_decode, i_channel );
1038         }
1039         return b_decode;
1040     }
1041     else
1042     {
1043         return es->p_dec != NULL;
1044     }
1045 }
1046 static void EsSelect( es_out_t *out, es_out_id_t *es )
1047 {
1048     es_out_sys_t   *p_sys = out->p_sys;
1049     input_thread_t *p_input = p_sys->p_input;
1050     vlc_value_t    val;
1051     const char     *psz_var;
1052
1053     if( EsIsSelected( es ) )
1054     {
1055         msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
1056         return;
1057     }
1058
1059     if( es->p_master )
1060     {
1061         int i_channel;
1062         if( !es->p_master->p_dec )
1063             return;
1064
1065         i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1066         if( i_channel == -1 || input_DecoderSetCcState( es->p_master->p_dec, true, i_channel ) )
1067             return;
1068     }
1069     else
1070     {
1071         if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
1072         {
1073             if( !var_GetBool( p_input, out->b_sout ? "sout-video" : "video" ) )
1074             {
1075                 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
1076                          es->i_id );
1077                 return;
1078             }
1079         }
1080         else if( es->fmt.i_cat == AUDIO_ES )
1081         {
1082             var_Get( p_input, "audio", &val );
1083             if( !var_GetBool( p_input, out->b_sout ? "sout-audio" : "audio" ) )
1084             {
1085                 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
1086                          es->i_id );
1087                 return;
1088             }
1089         }
1090         if( es->fmt.i_cat == SPU_ES )
1091         {
1092             var_Get( p_input, "spu", &val );
1093             if( !var_GetBool( p_input, out->b_sout ? "sout-spu" : "spu" ) )
1094             {
1095                 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
1096                          es->i_id );
1097                 return;
1098             }
1099         }
1100
1101         es->i_preroll_end = -1;
1102         es->p_dec = input_DecoderNew( p_input, &es->fmt, false );
1103         if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
1104             return;
1105     }
1106
1107     if( es->fmt.i_cat == VIDEO_ES )
1108         psz_var = "video-es";
1109     else if( es->fmt.i_cat == AUDIO_ES )
1110         psz_var = "audio-es";
1111     else if( es->fmt.i_cat == SPU_ES )
1112         psz_var = "spu-es";
1113     else
1114         return;
1115
1116     /* Mark it as selected */
1117     val.i_int = es->i_id;
1118     var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
1119
1120     var_SetBool( p_sys->p_input, "intf-change", true );
1121 }
1122
1123 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
1124 {
1125     es_out_sys_t   *p_sys = out->p_sys;
1126     input_thread_t *p_input = p_sys->p_input;
1127     vlc_value_t    val;
1128     const char     *psz_var;
1129
1130     if( !EsIsSelected( es ) )
1131     {
1132         msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
1133         return;
1134     }
1135
1136     if( es->p_master )
1137     {
1138         if( es->p_master->p_dec )
1139         {
1140             int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1141             if( i_channel != -1 )
1142                 input_DecoderSetCcState( es->p_master->p_dec, false, i_channel );
1143         }
1144     }
1145     else
1146     {
1147         const int i_spu_id = var_GetInteger( p_input, "spu-es");
1148         int i;
1149         for( i = 0; i < 4; i++ )
1150         {
1151             if( !es->pb_cc_present[i] || !es->pp_cc_es[i] )
1152                 continue;
1153
1154             if( i_spu_id == es->pp_cc_es[i]->i_id )
1155             {
1156                 /* Force unselection of the CC */
1157                 val.i_int = -1;
1158                 var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL );
1159                 if( !b_update )
1160                     var_SetBool( p_sys->p_input, "intf-change", true );
1161             }
1162             EsOutDel( out, es->pp_cc_es[i] );
1163
1164             es->pb_cc_present[i] = false;
1165         }
1166         input_DecoderDelete( es->p_dec );
1167         es->p_dec = NULL;
1168     }
1169
1170     if( !b_update )
1171         return;
1172
1173     /* Update var */
1174     if( es->p_dec == NULL )
1175         return;
1176     if( es->fmt.i_cat == VIDEO_ES )
1177         psz_var = "video-es";
1178     else if( es->fmt.i_cat == AUDIO_ES )
1179         psz_var = "audio-es";
1180     else if( es->fmt.i_cat == SPU_ES )
1181         psz_var = "spu-es";
1182     else
1183         return;
1184
1185     /* Mark it as unselected */
1186     val.i_int = -1;
1187     var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
1188
1189     var_SetBool( p_sys->p_input, "intf-change", true );
1190 }
1191
1192 /**
1193  * Select an ES given the current mode
1194  * XXX: you need to take a the lock before (stream.stream_lock)
1195  *
1196  * \param out The es_out structure
1197  * \param es es_out_id structure
1198  * \param b_force ...
1199  * \return nothing
1200  */
1201 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
1202 {
1203     es_out_sys_t      *p_sys = out->p_sys;
1204
1205     int i_cat = es->fmt.i_cat;
1206
1207     if( !p_sys->b_active ||
1208         ( !b_force && es->fmt.i_priority < 0 ) )
1209     {
1210         return;
1211     }
1212
1213     if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
1214     {
1215         if( !EsIsSelected( es ) )
1216             EsSelect( out, es );
1217     }
1218     else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
1219     {
1220         vlc_value_t val;
1221         int i;
1222         var_Get( p_sys->p_input, "programs", &val );
1223         for ( i = 0; i < val.p_list->i_count; i++ )
1224         {
1225             if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )
1226             {
1227                 if( !EsIsSelected( es ) )
1228                     EsSelect( out, es );
1229                 break;
1230             }
1231         }
1232         var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );
1233     }
1234     else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
1235     {
1236         int i_wanted  = -1;
1237
1238         if( es->p_pgrm != p_sys->p_pgrm )
1239             return;
1240
1241         if( i_cat == AUDIO_ES )
1242         {
1243             int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,
1244                                      es->psz_language_code );
1245
1246             if( p_sys->p_es_audio &&
1247                 p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
1248             {
1249                 int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,
1250                                          p_sys->p_es_audio->psz_language_code );
1251
1252                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
1253                     return;
1254                 i_wanted = es->i_channel;
1255             }
1256             else
1257             {
1258                 /* Select audio if (no audio selected yet)
1259                  * - no audio-language
1260                  * - no audio code for the ES
1261                  * - audio code in the requested list */
1262                 if( idx1 >= 0 ||
1263                     !strcmp( es->psz_language_code, "??" ) ||
1264                     !p_sys->ppsz_audio_language )
1265                     i_wanted = es->i_channel;
1266             }
1267
1268             if( p_sys->i_audio_last >= 0 )
1269                 i_wanted = p_sys->i_audio_last;
1270
1271             if( p_sys->i_audio_id >= 0 )
1272             {
1273                 if( es->i_id == p_sys->i_audio_id )
1274                     i_wanted = es->i_channel;
1275                 else
1276                     return;
1277             }
1278         }
1279         else if( i_cat == SPU_ES )
1280         {
1281             int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,
1282                                      es->psz_language_code );
1283
1284             if( p_sys->p_es_sub &&
1285                 p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )
1286             {
1287                 int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,
1288                                          p_sys->p_es_sub->psz_language_code );
1289
1290                 msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",
1291                         idx1, es->psz_language_code, idx2,
1292                         p_sys->p_es_sub->psz_language_code );
1293
1294                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
1295                     return;
1296                 /* We found a SPU that matches our language request */
1297                 i_wanted  = es->i_channel;
1298             }
1299             else if( idx1 >= 0 )
1300             {
1301                 msg_Dbg( p_sys->p_input, "idx1=%d(%s)",
1302                         idx1, es->psz_language_code );
1303
1304                 i_wanted  = es->i_channel;
1305             }
1306             else if( p_sys->i_default_sub_id >= 0 )
1307             {
1308                 if( es->i_id == p_sys->i_default_sub_id )
1309                     i_wanted = es->i_channel;
1310             }
1311
1312             if( p_sys->i_sub_last >= 0 )
1313                 i_wanted  = p_sys->i_sub_last;
1314
1315             if( p_sys->i_sub_id >= 0 )
1316             {
1317                 if( es->i_id == p_sys->i_sub_id )
1318                     i_wanted = es->i_channel;
1319                 else
1320                     return;
1321             }
1322         }
1323         else if( i_cat == VIDEO_ES )
1324         {
1325             i_wanted  = es->i_channel;
1326         }
1327
1328         if( i_wanted == es->i_channel && !EsIsSelected( es ) )
1329             EsSelect( out, es );
1330     }
1331
1332     /* FIXME TODO handle priority here */
1333     if( EsIsSelected( es ) )
1334     {
1335         if( i_cat == AUDIO_ES )
1336         {
1337             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
1338                 p_sys->p_es_audio &&
1339                 p_sys->p_es_audio != es &&
1340                 EsIsSelected( p_sys->p_es_audio ) )
1341             {
1342                 EsUnselect( out, p_sys->p_es_audio, false );
1343             }
1344             p_sys->p_es_audio = es;
1345         }
1346         else if( i_cat == SPU_ES )
1347         {
1348             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
1349                 p_sys->p_es_sub &&
1350                 p_sys->p_es_sub != es &&
1351                 EsIsSelected( p_sys->p_es_sub ) )
1352             {
1353                 EsUnselect( out, p_sys->p_es_sub, false );
1354             }
1355             p_sys->p_es_sub = es;
1356         }
1357         else if( i_cat == VIDEO_ES )
1358         {
1359             p_sys->p_es_video = es;
1360         }
1361     }
1362 }
1363
1364 /**
1365  * Send a block for the given es_out
1366  *
1367  * \param out the es_out to send from
1368  * \param es the es_out_id
1369  * \param p_block the data block to send
1370  */
1371 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
1372 {
1373     es_out_sys_t *p_sys = out->p_sys;
1374     input_thread_t    *p_input = p_sys->p_input;
1375     es_out_pgrm_t *p_pgrm = es->p_pgrm;
1376     int64_t i_delay;
1377     int i_total=0;
1378
1379     if( es->fmt.i_cat == AUDIO_ES )
1380         i_delay = p_sys->i_audio_delay;
1381     else if( es->fmt.i_cat == SPU_ES )
1382         i_delay = p_sys->i_spu_delay;
1383     else
1384         i_delay = 0;
1385
1386     if( libvlc_stats (p_input) )
1387     {
1388         vlc_mutex_lock( &p_input->p->counters.counters_lock );
1389         stats_UpdateInteger( p_input, p_input->p->counters.p_demux_read,
1390                              p_block->i_buffer, &i_total );
1391         stats_UpdateFloat( p_input , p_input->p->counters.p_demux_bitrate,
1392                            (float)i_total, NULL );
1393         vlc_mutex_unlock( &p_input->p->counters.counters_lock );
1394     }
1395
1396     /* Mark preroll blocks */
1397     if( es->i_preroll_end >= 0 )
1398     {
1399         int64_t i_date = p_block->i_pts;
1400         if( i_date <= 0 )
1401             i_date = p_block->i_dts;
1402
1403         if( i_date < es->i_preroll_end )
1404             p_block->i_flags |= BLOCK_FLAG_PREROLL;
1405         else
1406             es->i_preroll_end = -1;
1407     }
1408
1409     if( p_block->i_dts > 0 && (p_block->i_flags&BLOCK_FLAG_PREROLL) )
1410     {
1411         p_block->i_dts += i_delay;
1412     }
1413     else if( p_block->i_dts > 0 )
1414     {
1415         p_block->i_dts =
1416             input_ClockGetTS( p_input, &p_pgrm->clock, p_block->i_dts ) + i_delay;
1417     }
1418     if( p_block->i_pts > 0 && (p_block->i_flags&BLOCK_FLAG_PREROLL) )
1419     {
1420         p_block->i_pts += i_delay;
1421     }
1422     else if( p_block->i_pts > 0 )
1423     {
1424         p_block->i_pts =
1425             input_ClockGetTS( p_input, &p_pgrm->clock, p_block->i_pts ) + i_delay;
1426     }
1427     if ( p_block->i_rate == INPUT_RATE_DEFAULT &&
1428          es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) )
1429     {
1430         mtime_t current_date = mdate();
1431         if( !p_block->i_pts
1432                || p_block->i_pts > current_date + 10000000
1433                || current_date > p_block->i_pts )
1434         {
1435             /* ETSI EN 300 472 Annex A : do not take into account the PTS
1436              * for teletext streams. */
1437             p_block->i_pts = current_date + 400000
1438                                + p_input->i_pts_delay + i_delay;
1439         }
1440     }
1441
1442     p_block->i_rate = p_sys->i_rate;
1443
1444     /* TODO handle mute */
1445     if( es->p_dec &&
1446         ( es->fmt.i_cat != AUDIO_ES ||
1447           ( p_sys->i_rate >= INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE &&
1448             p_sys->i_rate <= INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE ) ) )
1449     {
1450         bool pb_cc[4];
1451         bool b_cc_new = false;
1452         int i;
1453         input_DecoderDecode( es->p_dec, p_block );
1454
1455         /* Check CC status */
1456         input_DecoderIsCcPresent( es->p_dec, pb_cc );
1457         for( i = 0; i < 4; i++ )
1458         {
1459             static const vlc_fourcc_t fcc[4] = {
1460                 VLC_FOURCC('c', 'c', '1', ' '),
1461                 VLC_FOURCC('c', 'c', '2', ' '),
1462                 VLC_FOURCC('c', 'c', '3', ' '),
1463                 VLC_FOURCC('c', 'c', '4', ' '),
1464             };
1465             static const char ppsz_description[4][18] = {
1466                 N_("Closed captions 1"),
1467                 N_("Closed captions 2"),
1468                 N_("Closed captions 3"),
1469                 N_("Closed captions 4"),
1470             };
1471             es_format_t fmt;
1472
1473             if(  es->pb_cc_present[i] || !pb_cc[i] )
1474                 continue;
1475             msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id );
1476
1477             es_format_Init( &fmt, SPU_ES, fcc[i] );
1478             fmt.i_group = es->fmt.i_group;
1479             fmt.psz_description = strdup( _(ppsz_description[i] ) );
1480             es->pp_cc_es[i] = EsOutAdd( out, &fmt );
1481             es->pp_cc_es[i]->p_master = es;
1482             es_format_Clean( &fmt );
1483
1484             /* */
1485             es->pb_cc_present[i] = true;
1486             b_cc_new = true;
1487         }
1488         if( b_cc_new )
1489             var_SetBool( p_sys->p_input, "intf-change", true );
1490     }
1491     else
1492     {
1493         block_Release( p_block );
1494     }
1495
1496     return VLC_SUCCESS;
1497 }
1498
1499 /*****************************************************************************
1500  * EsOutDel:
1501  *****************************************************************************/
1502 static void EsOutDel( es_out_t *out, es_out_id_t *es )
1503 {
1504     es_out_sys_t *p_sys = out->p_sys;
1505     bool b_reselect = false;
1506     int i;
1507
1508     /* We don't try to reselect */
1509     if( es->p_dec )
1510     {
1511         while( !out->p_sys->p_input->b_die && es->p_dec )
1512         {
1513             if( input_DecoderEmpty( es->p_dec ) )
1514                 break;
1515             msleep( 20*1000 );
1516         }
1517         EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
1518     }
1519
1520     if( es->p_pgrm == p_sys->p_pgrm )
1521         EsOutESVarUpdate( out, es, true );
1522
1523     TAB_REMOVE( p_sys->i_es, p_sys->es, es );
1524
1525     es->p_pgrm->i_es--;
1526     if( es->p_pgrm->i_es == 0 )
1527     {
1528         msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" );
1529     }
1530
1531     if( p_sys->p_es_audio == es || p_sys->p_es_video == es ||
1532         p_sys->p_es_sub == es ) b_reselect = true;
1533
1534     if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
1535     if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
1536     if( p_sys->p_es_sub   == es ) p_sys->p_es_sub   = NULL;
1537
1538     switch( es->fmt.i_cat )
1539     {
1540         case AUDIO_ES:
1541             p_sys->i_audio--;
1542             break;
1543         case SPU_ES:
1544             p_sys->i_sub--;
1545             break;
1546         case VIDEO_ES:
1547             p_sys->i_video--;
1548             break;
1549     }
1550
1551     /* Re-select another track when needed */
1552     if( b_reselect )
1553         for( i = 0; i < p_sys->i_es; i++ )
1554         {
1555             if( es->fmt.i_cat == p_sys->es[i]->fmt.i_cat )
1556                 EsOutSelect( out, p_sys->es[i], false );
1557         }
1558
1559     free( es->psz_language );
1560     free( es->psz_language_code );
1561
1562     es_format_Clean( &es->fmt );
1563
1564     free( es );
1565 }
1566
1567 /**
1568  * Control query handler
1569  *
1570  * \param out the es_out to control
1571  * \param i_query A es_out query as defined in include/ninput.h
1572  * \param args a variable list of arguments for the query
1573  * \return VLC_SUCCESS or an error code
1574  */
1575 static int EsOutControl( es_out_t *out, int i_query, va_list args )
1576 {
1577     es_out_sys_t *p_sys = out->p_sys;
1578     bool  b, *pb;
1579     int         i, *pi;
1580
1581     es_out_id_t *es;
1582
1583     switch( i_query )
1584     {
1585         case ES_OUT_SET_ES_STATE:
1586             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1587             b = (bool) va_arg( args, int );
1588             if( b && !EsIsSelected( es ) )
1589             {
1590                 EsSelect( out, es );
1591                 return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC;
1592             }
1593             else if( !b && EsIsSelected( es ) )
1594             {
1595                 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
1596                 return VLC_SUCCESS;
1597             }
1598             return VLC_SUCCESS;
1599
1600         case ES_OUT_GET_ES_STATE:
1601             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1602             pb = (bool*) va_arg( args, bool * );
1603
1604             *pb = EsIsSelected( es );
1605             return VLC_SUCCESS;
1606
1607         case ES_OUT_SET_ACTIVE:
1608         {
1609             b = (bool) va_arg( args, int );
1610             p_sys->b_active = b;
1611             /* Needed ? */
1612             if( b )
1613                 var_SetBool( p_sys->p_input, "intf-change", true );
1614             return VLC_SUCCESS;
1615         }
1616
1617         case ES_OUT_GET_ACTIVE:
1618             pb = (bool*) va_arg( args, bool * );
1619             *pb = p_sys->b_active;
1620             return VLC_SUCCESS;
1621
1622         case ES_OUT_SET_MODE:
1623             i = (int) va_arg( args, int );
1624             if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
1625                 i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )
1626             {
1627                 p_sys->i_mode = i;
1628
1629                 /* Reapply policy mode */
1630                 for( i = 0; i < p_sys->i_es; i++ )
1631                 {
1632                     if( EsIsSelected( p_sys->es[i] ) )
1633                     {
1634                         EsUnselect( out, p_sys->es[i],
1635                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1636                     }
1637                 }
1638                 for( i = 0; i < p_sys->i_es; i++ )
1639                 {
1640                     EsOutSelect( out, p_sys->es[i], false );
1641                 }
1642                 return VLC_SUCCESS;
1643             }
1644             return VLC_EGENERIC;
1645
1646         case ES_OUT_GET_MODE:
1647             pi = (int*) va_arg( args, int* );
1648             *pi = p_sys->i_mode;
1649             return VLC_SUCCESS;
1650
1651         case ES_OUT_SET_ES:
1652             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1653             /* Special case NULL, NULL+i_cat */
1654             if( es == NULL )
1655             {
1656                 for( i = 0; i < p_sys->i_es; i++ )
1657                 {
1658                     if( EsIsSelected( p_sys->es[i] ) )
1659                         EsUnselect( out, p_sys->es[i],
1660                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1661                 }
1662             }
1663             else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
1664             {
1665                 for( i = 0; i < p_sys->i_es; i++ )
1666                 {
1667                     if( p_sys->es[i]->fmt.i_cat == AUDIO_ES &&
1668                         EsIsSelected( p_sys->es[i] ) )
1669                         EsUnselect( out, p_sys->es[i],
1670                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1671                 }
1672             }
1673             else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
1674             {
1675                 for( i = 0; i < p_sys->i_es; i++ )
1676                 {
1677                     if( p_sys->es[i]->fmt.i_cat == VIDEO_ES &&
1678                         EsIsSelected( p_sys->es[i] ) )
1679                         EsUnselect( out, p_sys->es[i],
1680                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1681                 }
1682             }
1683             else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
1684             {
1685                 for( i = 0; i < p_sys->i_es; i++ )
1686                 {
1687                     if( p_sys->es[i]->fmt.i_cat == SPU_ES &&
1688                         EsIsSelected( p_sys->es[i] ) )
1689                         EsUnselect( out, p_sys->es[i],
1690                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
1691                 }
1692             }
1693             else
1694             {
1695                 for( i = 0; i < p_sys->i_es; i++ )
1696                 {
1697                     if( es == p_sys->es[i] )
1698                     {
1699                         EsOutSelect( out, es, true );
1700                         break;
1701                     }
1702                 }
1703             }
1704             {
1705                 vlc_event_t event;
1706                 event.type = vlc_InputSelectedStreamChanged;
1707                 vlc_event_send( &p_sys->p_input->p->event_manager, &event );
1708             }
1709             return VLC_SUCCESS;
1710  
1711         case ES_OUT_SET_DEFAULT:
1712         {
1713             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1714
1715             if( es == NULL )
1716             {
1717                 /*p_sys->i_default_video_id = -1;*/
1718                 /*p_sys->i_default_audio_id = -1;*/
1719                 p_sys->i_default_sub_id = -1;
1720             }
1721             else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
1722             {
1723                 /*p_sys->i_default_video_id = -1;*/
1724             }
1725             else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
1726             {
1727                 /*p_sys->i_default_audio_id = -1;*/
1728             }
1729             else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
1730             {
1731                 p_sys->i_default_sub_id = -1;
1732             }
1733             else
1734             {
1735                 /*if( es->fmt.i_cat == VIDEO_ES )
1736                     p_sys->i_default_video_id = es->i_id;
1737                 else
1738                 if( es->fmt.i_cat == AUDIO_ES )
1739                     p_sys->i_default_audio_id = es->i_id;
1740                 else*/
1741                 if( es->fmt.i_cat == SPU_ES )
1742                     p_sys->i_default_sub_id = es->i_id;
1743             }
1744             return VLC_SUCCESS;
1745         }
1746
1747         case ES_OUT_SET_PCR:
1748         case ES_OUT_SET_GROUP_PCR:
1749         {
1750             es_out_pgrm_t *p_pgrm = NULL;
1751             int            i_group = 0;
1752             int64_t        i_pcr;
1753
1754             if( i_query == ES_OUT_SET_PCR )
1755             {
1756                 p_pgrm = p_sys->p_pgrm;
1757             }
1758             else
1759             {
1760                 int i;
1761                 i_group = (int)va_arg( args, int );
1762                 for( i = 0; i < p_sys->i_pgrm; i++ )
1763                 {
1764                     if( p_sys->pgrm[i]->i_id == i_group )
1765                     {
1766                         p_pgrm = p_sys->pgrm[i];
1767                         break;
1768                     }
1769                 }
1770             }
1771             if( p_pgrm == NULL )
1772                 p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
1773
1774             i_pcr = (int64_t)va_arg( args, int64_t );
1775             /* search program */
1776             input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock, i_pcr );
1777             return VLC_SUCCESS;
1778         }
1779
1780         case ES_OUT_RESET_PCR:
1781             for( i = 0; i < p_sys->i_pgrm; i++ )
1782                 input_ClockResetPCR( &p_sys->pgrm[i]->clock );
1783             return VLC_SUCCESS;
1784
1785         case ES_OUT_GET_TS:
1786             if( p_sys->p_pgrm )
1787             {
1788                 int64_t i_ts = (int64_t)va_arg( args, int64_t );
1789                 int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * );
1790                 *pi_ts = input_ClockGetTS( p_sys->p_input,
1791                                            &p_sys->p_pgrm->clock, i_ts );
1792                 return VLC_SUCCESS;
1793             }
1794             return VLC_EGENERIC;
1795
1796         case ES_OUT_GET_GROUP:
1797             pi = (int*) va_arg( args, int* );
1798             if( p_sys->p_pgrm )
1799                 *pi = p_sys->p_pgrm->i_id;
1800             else
1801                 *pi = -1;    /* FIXME */
1802             return VLC_SUCCESS;
1803
1804         case ES_OUT_SET_GROUP:
1805         {
1806             int j;
1807             i = (int) va_arg( args, int );
1808             for( j = 0; j < p_sys->i_pgrm; j++ )
1809             {
1810                 es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
1811                 if( p_pgrm->i_id == i )
1812                 {
1813                     EsOutProgramSelect( out, p_pgrm );
1814                     return VLC_SUCCESS;
1815                 }
1816             }
1817             return VLC_EGENERIC;
1818         }
1819
1820         case ES_OUT_SET_FMT:
1821         {
1822             /* This ain't pretty but is need by some demuxers (eg. Ogg )
1823              * to update the p_extra data */
1824             es_format_t *p_fmt;
1825             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1826             p_fmt = (es_format_t*) va_arg( args, es_format_t * );
1827             if( es == NULL ) return VLC_EGENERIC;
1828
1829             if( p_fmt->i_extra )
1830             {
1831                 es->fmt.i_extra = p_fmt->i_extra;
1832                 es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );
1833                 memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );
1834
1835                 if( !es->p_dec ) return VLC_SUCCESS;
1836
1837 #if 1
1838                 input_DecoderDelete( es->p_dec );
1839                 es->p_dec = input_DecoderNew( p_sys->p_input,
1840                                               &es->fmt, false );
1841
1842 #else
1843                 es->p_dec->fmt_in.i_extra = p_fmt->i_extra;
1844                 es->p_dec->fmt_in.p_extra =
1845                     realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );
1846                 memcpy( es->p_dec->fmt_in.p_extra,
1847                         p_fmt->p_extra, p_fmt->i_extra );
1848 #endif
1849             }
1850
1851             return VLC_SUCCESS;
1852         }
1853
1854         case ES_OUT_SET_NEXT_DISPLAY_TIME:
1855         {
1856             int64_t i_date;
1857
1858             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1859             i_date = (int64_t)va_arg( args, int64_t );
1860
1861             if( !es || !es->p_dec )
1862                 return VLC_EGENERIC;
1863
1864             /* XXX We should call input_ClockGetTS but PCR has been reseted
1865              * and it will return 0, so we won't call input_ClockGetTS on all preroll samples
1866              * but that's ugly(more time discontinuity), it need to be improved -- fenrir */
1867             es->i_preroll_end = i_date;
1868
1869             return VLC_SUCCESS;
1870         }
1871         case ES_OUT_SET_GROUP_META:
1872         {
1873             int i_group = (int)va_arg( args, int );
1874             vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
1875
1876             EsOutProgramMeta( out, i_group, p_meta );
1877             return VLC_SUCCESS;
1878         }
1879         case ES_OUT_SET_GROUP_EPG:
1880         {
1881             int i_group = (int)va_arg( args, int );
1882             vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
1883
1884             EsOutProgramEpg( out, i_group, p_epg );
1885             return VLC_SUCCESS;
1886         }
1887         case ES_OUT_DEL_GROUP:
1888         {
1889             int i_group = (int)va_arg( args, int );
1890
1891             return EsOutProgramDel( out, i_group );
1892         }
1893
1894         default:
1895             msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
1896             return VLC_EGENERIC;
1897     }
1898 }
1899
1900 /****************************************************************************
1901  * LanguageGetName: try to expend iso639 into plain name
1902  ****************************************************************************/
1903 static char *LanguageGetName( const char *psz_code )
1904 {
1905     const iso639_lang_t *pl;
1906
1907     if( psz_code == NULL )
1908     {
1909         return strdup( "" );
1910     }
1911
1912     if( strlen( psz_code ) == 2 )
1913     {
1914         pl = GetLang_1( psz_code );
1915     }
1916     else if( strlen( psz_code ) == 3 )
1917     {
1918         pl = GetLang_2B( psz_code );
1919         if( !strcmp( pl->psz_iso639_1, "??" ) )
1920         {
1921             pl = GetLang_2T( psz_code );
1922         }
1923     }
1924     else
1925     {
1926         return strdup( psz_code );
1927     }
1928
1929     if( !strcmp( pl->psz_iso639_1, "??" ) )
1930     {
1931        return strdup( psz_code );
1932     }
1933     else
1934     {
1935         if( *pl->psz_native_name )
1936         {
1937             return strdup( pl->psz_native_name );
1938         }
1939         return strdup( pl->psz_eng_name );
1940     }
1941 }
1942
1943 /* Get a 2 char code */
1944 static char *LanguageGetCode( const char *psz_lang )
1945 {
1946     const iso639_lang_t *pl;
1947
1948     if( psz_lang == NULL || *psz_lang == '\0' )
1949         return strdup("??");
1950
1951     for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
1952     {
1953         if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
1954             !strcasecmp( pl->psz_native_name, psz_lang ) ||
1955             !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
1956             !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
1957             !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
1958             break;
1959     }
1960
1961     if( pl->psz_iso639_1 != NULL )
1962         return strdup( pl->psz_iso639_1 );
1963
1964     return strdup("??");
1965 }
1966
1967 static char **LanguageSplit( const char *psz_langs )
1968 {
1969     char *psz_dup;
1970     char *psz_parser;
1971     char **ppsz = NULL;
1972     int i_psz = 0;
1973
1974     if( psz_langs == NULL ) return NULL;
1975
1976     psz_parser = psz_dup = strdup(psz_langs);
1977
1978     while( psz_parser && *psz_parser )
1979     {
1980         char *psz;
1981         char *psz_code;
1982
1983         psz = strchr(psz_parser, ',' );
1984         if( psz ) *psz++ = '\0';
1985
1986         if( !strcmp( psz_parser, "any" ) )
1987         {
1988             TAB_APPEND( i_psz, ppsz, strdup("any") );
1989         }
1990         else
1991         {
1992             psz_code = LanguageGetCode( psz_parser );
1993             if( strcmp( psz_code, "??" ) )
1994             {
1995                 TAB_APPEND( i_psz, ppsz, psz_code );
1996             }
1997             else
1998             {
1999                 free( psz_code );
2000             }
2001         }
2002
2003         psz_parser = psz;
2004     }
2005
2006     if( i_psz )
2007     {
2008         TAB_APPEND( i_psz, ppsz, NULL );
2009     }
2010
2011     free( psz_dup );
2012     return ppsz;
2013 }
2014
2015 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )
2016 {
2017     int i;
2018
2019     if( !ppsz_langs || !psz_lang ) return -1;
2020
2021     for( i = 0; ppsz_langs[i]; i++ )
2022     {
2023         if( !strcasecmp( ppsz_langs[i], psz_lang ) ||
2024             !strcasecmp( ppsz_langs[i], "any" ) )
2025         {
2026             return i;
2027         }
2028     }
2029
2030     return -1;
2031 }
2032
2033 /****************************************************************************
2034  * EsOutAddInfo:
2035  * - add meta info to the playlist item
2036  ****************************************************************************/
2037 static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )
2038 {
2039     es_out_sys_t   *p_sys = out->p_sys;
2040     input_thread_t *p_input = p_sys->p_input;
2041     es_format_t    *fmt = &es->fmt;
2042     char           *psz_cat;
2043     lldiv_t         div;
2044
2045     /* Add stream info */
2046     if( asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 ) == -1 )
2047         return;
2048
2049     input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),
2050                    "%.4s", (char*)&fmt->i_codec );
2051
2052     input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),
2053                    "%s", es->psz_language );
2054
2055     /* Add information */
2056     switch( fmt->i_cat )
2057     {
2058     case AUDIO_ES:
2059         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2060                        _("Type"), _("Audio") );
2061
2062         if( fmt->audio.i_channels > 0 )
2063             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),
2064                            "%u", fmt->audio.i_channels );
2065
2066         if( fmt->audio.i_rate > 0 )
2067         {
2068             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),
2069                            _("%u Hz"), fmt->audio.i_rate );
2070             var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate );
2071         }
2072
2073         if( fmt->audio.i_bitspersample > 0 )
2074             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2075                            _("Bits per sample"), "%u",
2076                            fmt->audio.i_bitspersample );
2077
2078         if( fmt->i_bitrate > 0 )
2079         {
2080             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),
2081                            _("%u kb/s"), fmt->i_bitrate / 1000 );
2082             var_SetInteger( p_input, "bit-rate", fmt->i_bitrate );
2083         }
2084         break;
2085
2086     case VIDEO_ES:
2087         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2088                        _("Type"), _("Video") );
2089
2090         if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
2091             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2092                            _("Resolution"), "%ux%u",
2093                            fmt->video.i_width, fmt->video.i_height );
2094
2095         if( fmt->video.i_visible_width > 0 &&
2096             fmt->video.i_visible_height > 0 )
2097             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2098                            _("Display resolution"), "%ux%u",
2099                            fmt->video.i_visible_width,
2100                            fmt->video.i_visible_height);
2101        if( fmt->video.i_frame_rate > 0 &&
2102            fmt->video.i_frame_rate_base > 0 )
2103        {
2104            div = lldiv( (float)fmt->video.i_frame_rate /
2105                                fmt->video.i_frame_rate_base * 1000000,
2106                                1000000 );
2107            input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2108                           _("Frame rate"), "%"PRId64".%06u",
2109                           div.quot, (unsigned int )div.rem );
2110        }
2111        break;
2112
2113     case SPU_ES:
2114         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2115                        _("Type"), _("Subtitle") );
2116         break;
2117
2118     default:
2119         break;
2120     }
2121
2122     free( psz_cat );
2123 }