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