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