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