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