]> git.sesse.net Git - vlc/blob - src/input/es_out.c
133f2949f8fb6157f60015ff6ac9af001d360872
[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 #include "clock.h"
43 #include "decoder.h"
44 #include "es_out.h"
45 #include "event.h"
46
47 #include "../stream_output/stream_output.h"
48
49 #include <vlc_iso_lang.h>
50 /* FIXME we should find a better way than including that */
51 #include "../text/iso-639_def.h"
52
53 /*****************************************************************************
54  * Local prototypes
55  *****************************************************************************/
56 typedef struct
57 {
58     /* Program ID */
59     int i_id;
60
61     /* Number of es for this pgrm */
62     int i_es;
63
64     bool b_selected;
65
66     /* Clock for this program */
67     input_clock_t *p_clock;
68
69     char    *psz_name;
70     char    *psz_now_playing;
71     char    *psz_publisher;
72
73     vlc_epg_t *p_epg;
74 } es_out_pgrm_t;
75
76 struct es_out_id_t
77 {
78     /* ES ID */
79     int       i_id;
80     es_out_pgrm_t *p_pgrm;
81
82     /* Channel in the track type */
83     int         i_channel;
84     es_format_t fmt;
85     char        *psz_language;
86     char        *psz_language_code;
87
88     decoder_t   *p_dec;
89     decoder_t   *p_dec_record;
90
91     /* Fields for Video with CC */
92     bool  pb_cc_present[4];
93     es_out_id_t  *pp_cc_es[4];
94
95     /* Field for CC track from a master video */
96     es_out_id_t *p_master;
97
98     /* ID for the meta data */
99     int         i_meta_id;
100 };
101
102 struct es_out_sys_t
103 {
104     input_thread_t *p_input;
105
106     /* */
107     vlc_mutex_t   lock;
108
109     /* all programs */
110     int           i_pgrm;
111     es_out_pgrm_t **pgrm;
112     es_out_pgrm_t **pp_selected_pgrm; /* --programs */
113     es_out_pgrm_t *p_pgrm;  /* Master program */
114
115     /* all es */
116     int         i_id;
117     int         i_es;
118     es_out_id_t **es;
119
120     /* mode gestion */
121     bool  b_active;
122     int         i_mode;
123
124     /* es count */
125     int         i_audio;
126     int         i_video;
127     int         i_sub;
128
129     /* es to select */
130     int         i_audio_last, i_audio_id;
131     int         i_sub_last, i_sub_id;
132     int         i_default_sub_id;   /* As specified in container; if applicable */
133     char        **ppsz_audio_language;
134     char        **ppsz_sub_language;
135
136     /* current main es */
137     es_out_id_t *p_es_audio;
138     es_out_id_t *p_es_video;
139     es_out_id_t *p_es_sub;
140
141     /* delay */
142     int64_t i_audio_delay;
143     int64_t i_spu_delay;
144
145     /* Clock configuration */
146     mtime_t     i_pts_delay;
147     int         i_cr_average;
148     int         i_rate;
149
150     /* */
151     bool        b_paused;
152     mtime_t     i_pause_date;
153
154     /* Current preroll */
155     mtime_t     i_preroll_end;
156
157     /* Used for buffering */
158     bool        b_buffering;
159     mtime_t     i_buffering_extra_initial;
160     mtime_t     i_buffering_extra_stream;
161     mtime_t     i_buffering_extra_system;
162
163     /* Record */
164     sout_instance_t *p_sout_record;
165 };
166
167 static es_out_id_t *EsOutAdd    ( es_out_t *, const es_format_t * );
168 static int          EsOutSend   ( es_out_t *, es_out_id_t *, block_t * );
169 static void         EsOutDel    ( es_out_t *, es_out_id_t * );
170 static int          EsOutControl( es_out_t *, int i_query, va_list );
171 static void         EsOutDelete ( es_out_t * );
172
173 static void         EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force );
174 static void         EsOutUpdateInfo( es_out_t *, es_out_id_t *es, const es_format_t *, const vlc_meta_t * );
175 static int          EsOutSetRecord(  es_out_t *, bool b_record );
176
177 static bool EsIsSelected( es_out_id_t *es );
178 static void EsSelect( es_out_t *out, es_out_id_t *es );
179 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
180 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
181 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
182 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
183 static void EsOutProgramsChangeRate( es_out_t *out );
184 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
185 static char *LanguageGetName( const char *psz_code );
186 static char *LanguageGetCode( const char *psz_lang );
187 static char **LanguageSplit( const char *psz_langs );
188 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );
189
190 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
191
192 static const vlc_fourcc_t EsOutFourccClosedCaptions[4] = {
193     VLC_FOURCC('c', 'c', '1', ' '),
194     VLC_FOURCC('c', 'c', '2', ' '),
195     VLC_FOURCC('c', 'c', '3', ' '),
196     VLC_FOURCC('c', 'c', '4', ' '),
197 };
198 static inline int EsOutGetClosedCaptionsChannel( vlc_fourcc_t fcc )
199 {
200     int i;
201     for( i = 0; i < 4; i++ )
202     {
203         if( fcc == EsOutFourccClosedCaptions[i] )
204             return i;
205     }
206     return -1;
207 }
208
209
210 /*****************************************************************************
211  * input_EsOutNew:
212  *****************************************************************************/
213 es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
214 {
215     vlc_value_t  val;
216     int i;
217
218     es_out_t     *out = malloc( sizeof( *out ) );
219     if( !out )
220         return NULL;
221
222     es_out_sys_t *p_sys = malloc( sizeof( *p_sys ) );
223     if( !p_sys )
224     {
225         free( out );
226         return NULL;
227     }
228
229     out->pf_add     = EsOutAdd;
230     out->pf_send    = EsOutSend;
231     out->pf_del     = EsOutDel;
232     out->pf_control = EsOutControl;
233     out->pf_destroy = EsOutDelete;
234     out->p_sys      = p_sys;
235     out->b_sout     = p_input->p->p_sout != NULL;
236
237
238     vlc_mutex_init_recursive( &p_sys->lock );
239     p_sys->p_input = p_input;
240
241     p_sys->b_active = false;
242     p_sys->i_mode   = ES_OUT_MODE_AUTO;
243
244
245     TAB_INIT( p_sys->i_pgrm, p_sys->pgrm );
246     p_sys->p_pgrm   = NULL;
247
248     p_sys->i_id    = 0;
249
250     TAB_INIT( p_sys->i_es, p_sys->es );
251
252     p_sys->i_audio = 0;
253     p_sys->i_video = 0;
254     p_sys->i_sub   = 0;
255
256     /* */
257     var_Get( p_input, "audio-track", &val );
258     p_sys->i_audio_last = val.i_int;
259
260     var_Get( p_input, "sub-track", &val );
261     p_sys->i_sub_last = val.i_int;
262
263     p_sys->i_default_sub_id   = -1;
264
265     if( !p_input->b_preparsing )
266     {
267         var_Get( p_input, "audio-language", &val );
268         p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);
269         if( p_sys->ppsz_audio_language )
270         {
271             for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
272                 msg_Dbg( p_input, "selected audio language[%d] %s",
273                          i, p_sys->ppsz_audio_language[i] );
274         }
275         free( val.psz_string );
276
277         var_Get( p_input, "sub-language", &val );
278         p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);
279         if( p_sys->ppsz_sub_language )
280         {
281             for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
282                 msg_Dbg( p_input, "selected subtitle language[%d] %s",
283                          i, p_sys->ppsz_sub_language[i] );
284         }
285         free( val.psz_string );
286     }
287     else
288     {
289         p_sys->ppsz_sub_language = NULL;
290         p_sys->ppsz_audio_language = NULL;
291     }
292
293     var_Get( p_input, "audio-track-id", &val );
294     p_sys->i_audio_id = val.i_int;
295
296     var_Get( p_input, "sub-track-id", &val );
297     p_sys->i_sub_id = val.i_int;
298
299     p_sys->p_es_audio = NULL;
300     p_sys->p_es_video = NULL;
301     p_sys->p_es_sub   = NULL;
302
303     p_sys->i_audio_delay= 0;
304     p_sys->i_spu_delay  = 0;
305
306     p_sys->b_paused = false;
307     p_sys->i_pause_date = -1;
308
309     p_sys->i_rate = i_rate;
310     p_sys->i_pts_delay = 0;
311     p_sys->i_cr_average = 0;
312
313     p_sys->b_buffering = true;
314     p_sys->i_buffering_extra_initial = 0;
315     p_sys->i_buffering_extra_stream = 0;
316     p_sys->i_buffering_extra_system = 0;
317     p_sys->i_preroll_end = -1;
318
319     p_sys->p_sout_record = NULL;
320
321     return out;
322 }
323
324 /*****************************************************************************
325  *
326  *****************************************************************************/
327 static void EsOutDelete( es_out_t *out )
328 {
329     es_out_sys_t *p_sys = out->p_sys;
330     int i;
331
332     if( p_sys->p_sout_record )
333         EsOutSetRecord( out, false );
334
335     for( i = 0; i < p_sys->i_es; i++ )
336     {
337         if( p_sys->es[i]->p_dec )
338             input_DecoderDelete( p_sys->es[i]->p_dec );
339
340         free( p_sys->es[i]->psz_language );
341         free( p_sys->es[i]->psz_language_code );
342         es_format_Clean( &p_sys->es[i]->fmt );
343
344         free( p_sys->es[i] );
345     }
346     if( p_sys->ppsz_audio_language )
347     {
348         for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
349             free( p_sys->ppsz_audio_language[i] );
350         free( p_sys->ppsz_audio_language );
351     }
352     if( p_sys->ppsz_sub_language )
353     {
354         for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
355             free( p_sys->ppsz_sub_language[i] );
356         free( p_sys->ppsz_sub_language );
357     }
358     free( p_sys->es );
359
360     /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
361     for( i = 0; i < p_sys->i_pgrm; i++ )
362     {
363         es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
364         input_clock_Delete( p_pgrm->p_clock );
365         free( p_pgrm->psz_now_playing );
366         free( p_pgrm->psz_publisher );
367         free( p_pgrm->psz_name );
368         if( p_pgrm->p_epg )
369             vlc_epg_Delete( p_pgrm->p_epg );
370
371         free( p_pgrm );
372     }
373     TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
374     vlc_mutex_destroy( &p_sys->lock );
375
376     free( p_sys );
377     free( out );
378 }
379
380 static mtime_t EsOutGetWakeup( es_out_t *out )
381 {
382     es_out_sys_t   *p_sys = out->p_sys;
383     input_thread_t *p_input = p_sys->p_input;
384
385     if( !p_sys->p_pgrm )
386         return 0;
387
388     /* We do not have a wake up date if the input cannot have its speed
389      * controlled or sout is imposing its own or while buffering
390      *
391      * FIXME for !p_input->p->b_can_pace_control a wkeup time is still needed to avoid too strong buffering */
392     if( !p_input->p->b_can_pace_control ||
393         p_input->p->b_out_pace_control ||
394         p_sys->b_buffering )
395         return 0;
396
397     return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
398 }
399
400 static es_out_id_t *EsOutGetFromID( es_out_t *out, int i_id )
401 {
402     int i;
403     if( i_id < 0 )
404     {
405         /* Special HACK, -i_id is the cat of the stream */
406         return (es_out_id_t*)((uint8_t*)NULL-i_id);
407     }
408
409     for( i = 0; i < out->p_sys->i_es; i++ )
410     {
411         if( out->p_sys->es[i]->i_id == i_id )
412             return out->p_sys->es[i];
413     }
414     return NULL;
415 }
416
417 static bool EsOutDecodersIsEmpty( es_out_t *out )
418 {
419     es_out_sys_t      *p_sys = out->p_sys;
420     int i;
421
422     if( p_sys->b_buffering && p_sys->p_pgrm )
423     {
424         EsOutDecodersStopBuffering( out, true );
425         if( p_sys->b_buffering )
426             return true;
427     }
428
429     for( i = 0; i < p_sys->i_es; i++ )
430     {
431         es_out_id_t *es = p_sys->es[i];
432
433         if( es->p_dec && !input_DecoderIsEmpty( es->p_dec ) )
434             return false;
435         if( es->p_dec_record && !input_DecoderIsEmpty( es->p_dec_record ) )
436             return false;
437     }
438     return true;
439 }
440
441 static void EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
442 {
443     es_out_sys_t *p_sys = out->p_sys;
444
445     if( i_cat == AUDIO_ES )
446         p_sys->i_audio_delay = i_delay;
447     else if( i_cat == SPU_ES )
448         p_sys->i_spu_delay = i_delay;
449
450     for( int i = 0; i < p_sys->i_es; i++ )
451         EsOutDecoderChangeDelay( out, p_sys->es[i] );
452 }
453
454 static int EsOutSetRecord(  es_out_t *out, bool b_record )
455 {
456     es_out_sys_t   *p_sys = out->p_sys;
457     input_thread_t *p_input = p_sys->p_input;
458
459     assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
460
461     if( b_record )
462     {
463         char *psz_path = var_CreateGetString( p_input, "input-record-path" );
464         if( !psz_path || *psz_path == '\0' )
465         {
466             free( psz_path );
467             psz_path = strdup( config_GetHomeDir() );
468         }
469
470         char *psz_sout = NULL;  // TODO conf
471
472         if( !psz_sout && psz_path )
473         {
474             char *psz_file = input_CreateFilename( VLC_OBJECT(p_input), psz_path, INPUT_RECORD_PREFIX, NULL );
475             if( psz_file )
476             {
477                 if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file ) < 0 )
478                     psz_sout = NULL;
479                 free( psz_file );
480             }
481         }
482         free( psz_path );
483
484         if( !psz_sout )
485             return VLC_EGENERIC;
486
487 #ifdef ENABLE_SOUT
488         p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
489 #endif
490         free( psz_sout );
491
492         if( !p_sys->p_sout_record )
493             return VLC_EGENERIC;
494
495         for( int i = 0; i < p_sys->i_es; i++ )
496         {
497             es_out_id_t *p_es = p_sys->es[i];
498
499             if( !p_es->p_dec || p_es->p_master )
500                 continue;
501
502             p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
503             if( p_es->p_dec_record && p_sys->b_buffering )
504                 input_DecoderStartBuffering( p_es->p_dec_record );
505         }
506     }
507     else
508     {
509         for( int i = 0; i < p_sys->i_es; i++ )
510         {
511             es_out_id_t *p_es = p_sys->es[i];
512
513             if( !p_es->p_dec_record )
514                 continue;
515
516             input_DecoderDelete( p_es->p_dec_record );
517             p_es->p_dec_record = NULL;
518         }
519 #ifdef ENABLE_SOUT
520         sout_DeleteInstance( p_sys->p_sout_record );
521 #endif
522         p_sys->p_sout_record = NULL;
523     }
524
525     return VLC_SUCCESS;
526 }
527 static void EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
528 {
529     es_out_sys_t *p_sys = out->p_sys;
530
531     /* XXX the order is important */
532     if( b_paused )
533     {
534         EsOutDecodersChangePause( out, true, i_date );
535         EsOutProgramChangePause( out, true, i_date );
536     }
537     else
538     {
539         if( p_sys->i_buffering_extra_initial > 0 )
540         {
541             mtime_t i_stream_start;
542             mtime_t i_system_start;
543             mtime_t i_stream_duration;
544             mtime_t i_system_duration;
545             int i_ret;
546             i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
547                                           &i_stream_start, &i_system_start,
548                                           &i_stream_duration, &i_system_duration );
549             if( !i_ret )
550             {
551                 /* FIXME pcr != exactly what wanted */
552                 const mtime_t i_used = /*(i_stream_duration - p_sys->p_input->p->i_pts_delay)*/ p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
553                 i_date -= i_used;
554             }
555             p_sys->i_buffering_extra_initial = 0;
556             p_sys->i_buffering_extra_stream = 0;
557             p_sys->i_buffering_extra_system = 0;
558         }
559         EsOutProgramChangePause( out, false, i_date );
560         EsOutDecodersChangePause( out, false, i_date );
561
562         EsOutProgramsChangeRate( out );
563     }
564     p_sys->b_paused = b_paused;
565     p_sys->i_pause_date = i_date;
566 }
567
568 static void EsOutChangeRate( es_out_t *out, int i_rate )
569 {
570     es_out_sys_t      *p_sys = out->p_sys;
571
572     p_sys->i_rate = i_rate;
573
574     if( !p_sys->b_paused )
575         EsOutProgramsChangeRate( out );
576 }
577
578 static void EsOutChangePosition( es_out_t *out )
579 {
580     es_out_sys_t      *p_sys = out->p_sys;
581
582     input_SendEventCache( p_sys->p_input, 0.0 );
583
584     for( int i = 0; i < p_sys->i_es; i++ )
585     {
586         es_out_id_t *p_es = p_sys->es[i];
587
588         if( !p_es->p_dec )
589             continue;
590
591         input_DecoderStartBuffering( p_es->p_dec );
592
593         if( p_es->p_dec_record )
594             input_DecoderStartBuffering( p_es->p_dec_record );
595     }
596
597     for( int i = 0; i < p_sys->i_pgrm; i++ )
598         input_clock_Reset( p_sys->pgrm[i]->p_clock );
599
600     p_sys->b_buffering = true;
601     p_sys->i_buffering_extra_initial = 0;
602     p_sys->i_buffering_extra_stream = 0;
603     p_sys->i_buffering_extra_system = 0;
604     p_sys->i_preroll_end = -1;
605 }
606
607
608
609 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
610 {
611     es_out_sys_t *p_sys = out->p_sys;
612     int i_ret;
613
614     mtime_t i_stream_start;
615     mtime_t i_system_start;
616     mtime_t i_stream_duration;
617     mtime_t i_system_duration;
618     i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
619                                   &i_stream_start, &i_system_start,
620                                   &i_stream_duration, &i_system_duration );
621     assert( !i_ret || b_forced );
622     if( i_ret )
623         return;
624
625     mtime_t i_preroll_duration = 0;
626     if( p_sys->i_preroll_end >= 0 )
627         i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
628
629     const mtime_t i_buffering_duration = p_sys->i_pts_delay +
630                                          i_preroll_duration +
631                                          p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
632
633     if( i_stream_duration <= i_buffering_duration && !b_forced )
634     {
635         const double f_level = (double)i_stream_duration / i_buffering_duration;
636         input_SendEventCache( p_sys->p_input, f_level );
637
638         msg_Dbg( p_sys->p_input, "Buffering %d%%", (int)(100 * f_level) );
639         return;
640     }
641     input_SendEventCache( p_sys->p_input, 1.0 );
642
643     msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
644               (int)(i_stream_duration/1000), (int)(i_system_duration/1000) );
645     p_sys->b_buffering = false;
646     p_sys->i_preroll_end = -1;
647
648     if( p_sys->i_buffering_extra_initial > 0 )
649     {
650         /* FIXME wrong ? */
651         return;
652     }
653
654     const mtime_t i_decoder_buffering_start = mdate();
655     for( int i = 0; i < p_sys->i_es; i++ )
656     {
657         es_out_id_t *p_es = p_sys->es[i];
658
659         if( !p_es->p_dec || p_es->fmt.i_cat == SPU_ES )
660             continue;
661         input_DecoderWaitBuffering( p_es->p_dec );
662         if( p_es->p_dec_record )
663             input_DecoderWaitBuffering( p_es->p_dec_record );
664     }
665
666     msg_Dbg( p_sys->p_input, "Decoder buffering done in %d ms",
667               (int)(mdate() - i_decoder_buffering_start)/1000 );
668
669     /* Here is a good place to destroy unused vout with every demuxer */
670     input_ressource_TerminateVout( p_sys->p_input->p->p_ressource );
671
672     /* */
673     const mtime_t i_wakeup_delay = 10*1000; /* FIXME CLEANUP thread wake up time*/
674     const mtime_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : mdate();
675
676     input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, i_current_date + i_wakeup_delay - i_buffering_duration );
677
678     for( int i = 0; i < p_sys->i_es; i++ )
679     {
680         es_out_id_t *p_es = p_sys->es[i];
681
682         if( !p_es->p_dec )
683             continue;
684
685         input_DecoderStopBuffering( p_es->p_dec );
686         if( p_es->p_dec_record )
687             input_DecoderStopBuffering( p_es->p_dec_record );
688     }
689 }
690 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
691 {
692     es_out_sys_t *p_sys = out->p_sys;
693
694     /* Pause decoders first */
695     for( int i = 0; i < p_sys->i_es; i++ )
696     {
697         es_out_id_t *es = p_sys->es[i];
698
699         if( es->p_dec )
700         {
701             input_DecoderChangePause( es->p_dec, b_paused, i_date );
702             if( es->p_dec_record )
703                 input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
704         }
705     }
706 }
707 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
708 {
709     es_out_sys_t *p_sys = out->p_sys;
710
711     for( int i = 0; i < p_sys->i_pgrm; i++ )
712         input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date );
713 }
714
715 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
716 {
717     es_out_sys_t *p_sys = out->p_sys;
718
719     mtime_t i_delay = 0;
720     if( p_es->fmt.i_cat == AUDIO_ES )
721         i_delay = p_sys->i_audio_delay;
722     else if( p_es->fmt.i_cat == SPU_ES )
723         i_delay = p_sys->i_spu_delay;
724
725     if( i_delay != 0 )
726     {
727         if( p_es->p_dec )
728             input_DecoderChangeDelay( p_es->p_dec, i_delay );
729         if( p_es->p_dec_record )
730             input_DecoderChangeDelay( p_es->p_dec_record, i_delay );
731     }
732 }
733 static void EsOutProgramsChangeRate( es_out_t *out )
734 {
735     es_out_sys_t      *p_sys = out->p_sys;
736
737     for( int i = 0; i < p_sys->i_pgrm; i++ )
738         input_clock_ChangeRate( p_sys->pgrm[i]->p_clock, p_sys->i_rate );
739 }
740
741 static void EsOutFrameNext( es_out_t *out )
742 {
743     es_out_sys_t *p_sys = out->p_sys;
744     es_out_id_t *p_es_video = NULL;
745
746     if( p_sys->b_buffering )
747     {
748         msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" );
749         return;
750     }
751
752     assert( p_sys->b_paused );
753
754     for( int i = 0; i < p_sys->i_es; i++ )
755     {
756         es_out_id_t *p_es = p_sys->es[i];
757
758         if( p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
759         {
760             p_es_video = p_es;
761             break;
762         }
763     }
764
765     if( !p_es_video )
766     {
767         msg_Warn( p_sys->p_input, "No video track selected, ignoring 'frame next'" );
768         return;
769     }
770
771     mtime_t i_duration;
772     input_DecoderFrameNext( p_es_video->p_dec, &i_duration );
773
774     msg_Dbg( out->p_sys->p_input, "EsOutFrameNext consummed %d ms", (int)(i_duration/1000) );
775
776     if( i_duration <= 0 )
777         i_duration = 40*1000;
778
779     /* FIXME it is not a clean way ? */
780     if( p_sys->i_buffering_extra_initial <= 0 )
781     {
782         mtime_t i_stream_start;
783         mtime_t i_system_start;
784         mtime_t i_stream_duration;
785         mtime_t i_system_duration;
786         int i_ret;
787
788         i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
789                                       &i_stream_start, &i_system_start,
790                                       &i_stream_duration, &i_system_duration );
791         if( i_ret )
792             return;
793
794         p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->i_pts_delay; /* FIXME < 0 ? */
795         p_sys->i_buffering_extra_system =
796         p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
797     }
798
799     const int i_rate = input_clock_GetRate( p_sys->p_pgrm->p_clock );
800
801     p_sys->b_buffering = true;
802     p_sys->i_buffering_extra_system += i_duration;
803     p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial +
804                                       ( p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial ) *
805                                                 INPUT_RATE_DEFAULT / i_rate;
806
807     p_sys->i_preroll_end = -1;
808 }
809 static mtime_t EsOutGetBuffering( es_out_t *out )
810 {
811     es_out_sys_t *p_sys = out->p_sys;
812
813     if( !p_sys->p_pgrm )
814         return 0;
815
816     int i_ret;
817     mtime_t i_stream_start;
818     mtime_t i_system_start;
819     mtime_t i_stream_duration;
820     mtime_t i_system_duration;
821     i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
822                                   &i_stream_start, &i_system_start,
823                                   &i_stream_duration, &i_system_duration );
824
825     if( i_ret )
826         return 0;
827
828     mtime_t i_delay;
829
830     if( p_sys->b_buffering && p_sys->i_buffering_extra_initial <= 0 )
831     {
832         i_delay = i_stream_duration;
833     }
834     else
835     {
836         mtime_t i_system_duration;
837         if( p_sys->b_paused )
838         {
839             i_system_duration = p_sys->i_pause_date  - i_system_start;
840             if( p_sys->i_buffering_extra_initial > 0 )
841                 i_system_duration += p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
842         }
843         else
844         {
845             i_system_duration = mdate() - i_system_start;
846         }
847
848         const mtime_t i_consumed = i_system_duration * INPUT_RATE_DEFAULT / p_sys->i_rate - i_stream_duration;
849         i_delay = p_sys->i_pts_delay - i_consumed;
850     }
851     if( i_delay < 0 )
852         return 0;
853     return i_delay;
854 }
855
856 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
857                                      bool b_delete )
858 {
859     es_out_sys_t      *p_sys = out->p_sys;
860     input_thread_t    *p_input = p_sys->p_input;
861     const  bool b_teletext = fmt->i_cat == SPU_ES && fmt->i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' );
862     vlc_value_t       val, text;
863
864     if( b_delete )
865     {
866         /* TODO it should probably be a list */
867         if( b_teletext )
868             input_SendEventTeletext( p_sys->p_input, -1 );
869
870         input_SendEventEsDel( p_input, fmt->i_cat, i_id );
871         return;
872     }
873
874     /* Get the number of ES already added */
875     const char *psz_var;
876     if( fmt->i_cat == AUDIO_ES )
877         psz_var = "audio-es";
878     else if( fmt->i_cat == VIDEO_ES )
879         psz_var = "video-es";
880     else
881         psz_var = "spu-es";
882
883     var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
884     if( val.i_int == 0 )
885     {
886         vlc_value_t val2;
887
888         /* First one, we need to add the "Disable" choice */
889         val2.i_int = -1; text.psz_string = _("Disable");
890         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
891         val.i_int++;
892     }
893
894     /* Take care of the ES description */
895     if( fmt->psz_description && *fmt->psz_description )
896     {
897         if( psz_language && *psz_language )
898         {
899             text.psz_string = malloc( strlen( fmt->psz_description) +
900                                       strlen( psz_language ) + 10 );
901             sprintf( text.psz_string, "%s - [%s]", fmt->psz_description,
902                                                    psz_language );
903         }
904         else text.psz_string = strdup( fmt->psz_description );
905     }
906     else
907     {
908         if( psz_language && *psz_language )
909         {
910             if( asprintf( &text.psz_string, "%s %i - [%s]", _( "Track" ), val.i_int, psz_language ) == -1 )
911                 text.psz_string = NULL;
912         }
913         else
914         {
915             if( asprintf( &text.psz_string, "%s %i", _( "Track" ), val.i_int ) == -1 )
916                 text.psz_string = NULL;
917         }
918     }
919
920     input_SendEventEsAdd( p_input, fmt->i_cat, i_id, text.psz_string );
921
922     free( text.psz_string );
923
924     if( b_teletext )
925     {
926         /* TODO it should probably be a list */
927         if( var_GetInteger( p_sys->p_input, "teletext-es" ) < 0 )
928             input_SendEventTeletext( p_sys->p_input, i_id );
929     }
930 }
931
932 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
933                               bool b_delete )
934 {
935     EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete );
936 }
937
938 /* EsOutProgramSelect:
939  *  Select a program and update the object variable
940  */
941 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
942 {
943     es_out_sys_t      *p_sys = out->p_sys;
944     input_thread_t    *p_input = p_sys->p_input;
945     int               i;
946
947     if( p_sys->p_pgrm == p_pgrm )
948         return; /* Nothing to do */
949
950     if( p_sys->p_pgrm )
951     {
952         es_out_pgrm_t *old = p_sys->p_pgrm;
953         msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
954
955         for( i = 0; i < p_sys->i_es; i++ )
956         {
957             if( p_sys->es[i]->p_pgrm == old && EsIsSelected( p_sys->es[i] ) &&
958                 p_sys->i_mode != ES_OUT_MODE_ALL )
959                 EsUnselect( out, p_sys->es[i], true );
960         }
961
962         p_sys->p_es_audio = NULL;
963         p_sys->p_es_sub = NULL;
964         p_sys->p_es_video = NULL;
965     }
966
967     msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
968
969     /* Mark it selected */
970     p_pgrm->b_selected = true;
971
972     /* Switch master stream */
973     p_sys->p_pgrm = p_pgrm;
974
975     /* Update "program" */
976     input_SendEventProgramSelect( p_input, p_pgrm->i_id );
977
978     /* Update "es-*" */
979     input_SendEventEsDel( p_input, AUDIO_ES, -1 );
980     input_SendEventEsDel( p_input, VIDEO_ES, -1 );
981     input_SendEventEsDel( p_input, SPU_ES, -1 );
982
983     /* TODO event */
984     var_SetInteger( p_input, "teletext-es", -1 );
985
986     for( i = 0; i < p_sys->i_es; i++ )
987     {
988         if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
989             EsOutESVarUpdate( out, p_sys->es[i], false );
990         EsOutSelect( out, p_sys->es[i], false );
991     }
992
993     /* Update now playing */
994     input_item_SetNowPlaying( p_input->p->p_item, p_pgrm->psz_now_playing );
995     input_item_SetPublisher( p_input->p->p_item, p_pgrm->psz_publisher );
996
997     input_SendEventMeta( p_input );
998 }
999
1000 /* EsOutAddProgram:
1001  *  Add a program
1002  */
1003 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
1004 {
1005     es_out_sys_t      *p_sys = out->p_sys;
1006     input_thread_t    *p_input = p_sys->p_input;
1007
1008     es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
1009     if( !p_pgrm )
1010         return NULL;
1011
1012     /* Init */
1013     p_pgrm->i_id = i_group;
1014     p_pgrm->i_es = 0;
1015     p_pgrm->b_selected = false;
1016     p_pgrm->psz_name = NULL;
1017     p_pgrm->psz_now_playing = NULL;
1018     p_pgrm->psz_publisher = NULL;
1019     p_pgrm->p_epg = NULL;
1020     p_pgrm->p_clock = input_clock_New( p_sys->i_rate );
1021     if( !p_pgrm->p_clock )
1022     {
1023         free( p_pgrm );
1024         return NULL;
1025     }
1026     input_clock_SetJitter( p_pgrm->p_clock, p_sys->i_pts_delay, p_sys->i_cr_average );
1027
1028
1029     /* Append it */
1030     TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
1031
1032     /* Update "program" variable */
1033     input_SendEventProgramAdd( p_input, i_group, NULL );
1034
1035     if( i_group == var_GetInteger( p_input, "program" ) )
1036         EsOutProgramSelect( out, p_pgrm );
1037
1038     return p_pgrm;
1039 }
1040
1041 /* EsOutDelProgram:
1042  *  Delete a program
1043  */
1044 static int EsOutProgramDel( es_out_t *out, int i_group )
1045 {
1046     es_out_sys_t      *p_sys = out->p_sys;
1047     input_thread_t    *p_input = p_sys->p_input;
1048     es_out_pgrm_t     *p_pgrm = NULL;
1049     int               i;
1050
1051     for( i = 0; i < p_sys->i_pgrm; i++ )
1052     {
1053         if( p_sys->pgrm[i]->i_id == i_group )
1054         {
1055             p_pgrm = p_sys->pgrm[i];
1056             break;
1057         }
1058     }
1059
1060     if( p_pgrm == NULL )
1061         return VLC_EGENERIC;
1062
1063     if( p_pgrm->i_es )
1064     {
1065         msg_Dbg( p_input, "can't delete program %d which still has %i ES",
1066                  i_group, p_pgrm->i_es );
1067         return VLC_EGENERIC;
1068     }
1069
1070     TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
1071
1072     /* If program is selected we need to unselect it */
1073     if( p_sys->p_pgrm == p_pgrm )
1074         p_sys->p_pgrm = NULL;
1075
1076     input_clock_Delete( p_pgrm->p_clock );
1077
1078     free( p_pgrm->psz_name );
1079     free( p_pgrm->psz_now_playing );
1080     free( p_pgrm->psz_publisher );
1081     if( p_pgrm->p_epg )
1082         vlc_epg_Delete( p_pgrm->p_epg );
1083     free( p_pgrm );
1084
1085     /* Update "program" variable */
1086     input_SendEventProgramDel( p_input, i_group );
1087
1088     return VLC_SUCCESS;
1089 }
1090
1091 /* EsOutProgramMeta:
1092  */
1093 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
1094 {
1095     char *psz = NULL;
1096     if( p_pgrm->psz_name )
1097     {
1098         if( asprintf( &psz, _("%s [%s %d]"), p_pgrm->psz_name, _("Program"), p_pgrm->i_id ) == -1 )
1099             return NULL;
1100     }
1101     else
1102     {
1103         if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1104             return NULL;
1105     }
1106     return psz;
1107 }
1108
1109 static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
1110 {
1111     es_out_sys_t      *p_sys = out->p_sys;
1112     es_out_pgrm_t     *p_pgrm = NULL;
1113     input_thread_t    *p_input = p_sys->p_input;
1114     char              *psz_cat;
1115     const char        *psz_title = NULL;
1116     const char        *psz_provider = NULL;
1117     int i;
1118
1119     msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
1120
1121     /* Check against empty meta data (empty for what we handle) */
1122     if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
1123         !vlc_meta_Get( p_meta, vlc_meta_NowPlaying) &&
1124         !vlc_meta_Get( p_meta, vlc_meta_Publisher) &&
1125         vlc_dictionary_keys_count( &p_meta->extra_tags ) <= 0 )
1126     {
1127         return;
1128     }
1129     /* Find program */
1130     for( i = 0; i < p_sys->i_pgrm; i++ )
1131     {
1132         if( p_sys->pgrm[i]->i_id == i_group )
1133         {
1134             p_pgrm = p_sys->pgrm[i];
1135             break;
1136         }
1137     }
1138     if( p_pgrm == NULL )
1139         p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
1140
1141     /* */
1142     psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
1143     psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
1144
1145     /* Update the description text of the program */
1146     if( psz_title && *psz_title )
1147     {
1148         if( !p_pgrm->psz_name || strcmp( p_pgrm->psz_name, psz_title ) )
1149         {
1150             char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1151
1152             /* Remove old entries */
1153             input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL );
1154             /* TODO update epg name */
1155             free( psz_cat );
1156         }
1157         free( p_pgrm->psz_name );
1158         p_pgrm->psz_name = strdup( psz_title );
1159
1160         char *psz_text;
1161         if( psz_provider && *psz_provider )
1162         {
1163             if( asprintf( &psz_text, "%s [%s]", psz_title, psz_provider ) < 0 )
1164                 psz_text = NULL;
1165         }
1166         else
1167         {
1168             psz_text = strdup( psz_title );
1169         }
1170
1171         /* ugly but it works */
1172         if( psz_text )
1173         {
1174             input_SendEventProgramDel( p_input, i_group );
1175             input_SendEventProgramAdd( p_input, i_group, psz_text );
1176
1177             free( psz_text );
1178         }
1179     }
1180
1181     psz_cat = EsOutProgramGetMetaName( p_pgrm );
1182     if( psz_provider )
1183     {
1184         if( p_sys->p_pgrm == p_pgrm )
1185         {
1186             input_item_SetPublisher( p_input->p->p_item, psz_provider );
1187             input_SendEventMeta( p_input );
1188         }
1189         input_Control( p_input, INPUT_ADD_INFO, psz_cat, input_MetaTypeToLocalizedString(vlc_meta_Publisher), psz_provider );
1190     }
1191     char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags );
1192     for( i = 0; ppsz_all_keys[i]; i++ )
1193     {
1194         input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(ppsz_all_keys[i]),
1195                        vlc_dictionary_value_for_key( &p_meta->extra_tags, ppsz_all_keys[i] ) );
1196         free( ppsz_all_keys[i] );
1197     }
1198     free( ppsz_all_keys );
1199
1200     free( psz_cat );
1201 }
1202
1203 static void vlc_epg_Merge( vlc_epg_t *p_dst, const vlc_epg_t *p_src )
1204 {
1205     int i;
1206
1207     /* Add new event */
1208     for( i = 0; i < p_src->i_event; i++ )
1209     {
1210         vlc_epg_event_t *p_evt = p_src->pp_event[i];
1211         bool b_add = true;
1212         int j;
1213
1214         for( j = 0; j < p_dst->i_event; j++ )
1215         {
1216             if( p_dst->pp_event[j]->i_start == p_evt->i_start && p_dst->pp_event[j]->i_duration == p_evt->i_duration )
1217             {
1218                 b_add = false;
1219                 break;
1220             }
1221             if( p_dst->pp_event[j]->i_start > p_evt->i_start )
1222                 break;
1223         }
1224         if( b_add )
1225         {
1226             vlc_epg_event_t *p_copy = calloc( 1, sizeof(vlc_epg_event_t) );
1227             if( !p_copy )
1228                 break;
1229             p_copy->i_start = p_evt->i_start;
1230             p_copy->i_duration = p_evt->i_duration;
1231             p_copy->psz_name = p_evt->psz_name ? strdup( p_evt->psz_name ) : NULL;
1232             p_copy->psz_short_description = p_evt->psz_short_description ? strdup( p_evt->psz_short_description ) : NULL;
1233             p_copy->psz_description = p_evt->psz_description ? strdup( p_evt->psz_description ) : NULL;
1234             TAB_INSERT( p_dst->i_event, p_dst->pp_event, p_copy, j );
1235         }
1236     }
1237     /* Update current */
1238     vlc_epg_SetCurrent( p_dst, p_src->p_current ? p_src->p_current->i_start : -1 );
1239
1240     /* Keep only 1 old event  */
1241     if( p_dst->p_current )
1242     {
1243         while( p_dst->i_event > 1 && p_dst->pp_event[0] != p_dst->p_current && p_dst->pp_event[1] != p_dst->p_current )
1244             TAB_REMOVE( p_dst->i_event, p_dst->pp_event, p_dst->pp_event[0] );
1245     }
1246 }
1247
1248 static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg )
1249 {
1250     es_out_sys_t      *p_sys = out->p_sys;
1251     input_thread_t    *p_input = p_sys->p_input;
1252     es_out_pgrm_t     *p_pgrm = NULL;
1253     char *psz_cat;
1254     int i;
1255
1256     /* Find program */
1257     for( i = 0; i < p_sys->i_pgrm; i++ )
1258     {
1259         if( p_sys->pgrm[i]->i_id == i_group )
1260         {
1261             p_pgrm = p_sys->pgrm[i];
1262             break;
1263         }
1264     }
1265     if( p_pgrm == NULL )
1266         p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
1267
1268     /* Merge EPG */
1269     if( !p_pgrm->p_epg )
1270         p_pgrm->p_epg = vlc_epg_New( p_pgrm->psz_name );
1271     vlc_epg_Merge( p_pgrm->p_epg, p_epg );
1272
1273     /* Update info */
1274     psz_cat = EsOutProgramGetMetaName( p_pgrm );
1275 #ifdef HAVE_LOCALTIME_R
1276     char *psz_epg;
1277     if( asprintf( &psz_epg, "EPG %s", psz_cat ) == -1 )
1278         psz_epg = NULL;
1279     input_Control( p_input, INPUT_DEL_INFO, psz_epg, NULL );
1280     msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, p_pgrm->p_epg->psz_name );
1281     for( i = 0; i < p_pgrm->p_epg->i_event; i++ )
1282     {
1283         const vlc_epg_event_t *p_evt = p_pgrm->p_epg->pp_event[i];
1284         time_t t_start = (time_t)p_evt->i_start;
1285         struct tm tm_start;
1286         char psz_start[128];
1287
1288         localtime_r( &t_start, &tm_start );
1289
1290         snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d:%2.2d", tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
1291         if( p_evt->psz_short_description || p_evt->psz_description )
1292             input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s",
1293                            p_evt->psz_name,
1294                            p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
1295                            p_evt->psz_short_description ? p_evt->psz_short_description : p_evt->psz_description );
1296         else
1297             input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
1298                            p_evt->psz_name,
1299                            p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
1300     }
1301     free( psz_epg );
1302 #endif
1303     /* Update now playing */
1304     free( p_pgrm->psz_now_playing );
1305     p_pgrm->psz_now_playing = NULL;
1306     if( p_epg->p_current && p_epg->p_current->psz_name && *p_epg->p_current->psz_name )
1307         p_pgrm->psz_now_playing = strdup( p_epg->p_current->psz_name );
1308
1309     if( p_pgrm == p_sys->p_pgrm )
1310     {
1311         input_item_SetNowPlaying( p_input->p->p_item, p_pgrm->psz_now_playing );
1312         input_SendEventMeta( p_input );
1313     }
1314
1315     if( p_pgrm->psz_now_playing )
1316     {
1317         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1318             input_MetaTypeToLocalizedString(vlc_meta_NowPlaying),
1319             p_pgrm->psz_now_playing );
1320     }
1321     else
1322     {
1323         input_Control( p_input, INPUT_DEL_INFO, psz_cat,
1324             input_MetaTypeToLocalizedString(vlc_meta_NowPlaying) );
1325     }
1326
1327     free( psz_cat );
1328 }
1329
1330 /* EsOutAdd:
1331  *  Add an es_out
1332  */
1333 static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt )
1334 {
1335     es_out_sys_t      *p_sys = out->p_sys;
1336     input_thread_t    *p_input = p_sys->p_input;
1337
1338     if( fmt->i_group < 0 )
1339     {
1340         msg_Err( p_input, "invalid group number" );
1341         return NULL;
1342     }
1343
1344     es_out_id_t       *es = malloc( sizeof( *es ) );
1345     es_out_pgrm_t     *p_pgrm = NULL;
1346     int i;
1347
1348     if( !es )
1349         return NULL;
1350
1351     vlc_mutex_lock( &p_sys->lock );
1352
1353     /* Search the program */
1354     for( i = 0; i < p_sys->i_pgrm; i++ )
1355     {
1356         if( fmt->i_group == p_sys->pgrm[i]->i_id )
1357         {
1358             p_pgrm = p_sys->pgrm[i];
1359             break;
1360         }
1361     }
1362     if( p_pgrm == NULL )
1363     {
1364         /* Create a new one */
1365         p_pgrm = EsOutProgramAdd( out, fmt->i_group );
1366     }
1367
1368     /* Increase ref count for program */
1369     p_pgrm->i_es++;
1370
1371     /* Set up ES */
1372     es->p_pgrm = p_pgrm;
1373     es_format_Copy( &es->fmt, fmt );
1374     if( es->fmt.i_id < 0 )
1375         es->fmt.i_id = out->p_sys->i_id;
1376     es->i_id = es->fmt.i_id;
1377     es->i_meta_id = out->p_sys->i_id;
1378
1379     switch( es->fmt.i_cat )
1380     {
1381     case AUDIO_ES:
1382     {
1383         audio_replay_gain_t rg;
1384
1385         es->i_channel = p_sys->i_audio;
1386
1387         memset( &rg, 0, sizeof(rg) );
1388         vlc_mutex_lock( &p_input->p->p_item->lock );
1389         vlc_audio_replay_gain_MergeFromMeta( &rg, p_input->p->p_item->p_meta );
1390         vlc_mutex_unlock( &p_input->p->p_item->lock );
1391
1392         for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
1393         {
1394             if( !es->fmt.audio_replay_gain.pb_peak[i] )
1395             {
1396                 es->fmt.audio_replay_gain.pb_peak[i] = rg.pb_peak[i];
1397                 es->fmt.audio_replay_gain.pf_peak[i] = rg.pf_peak[i];
1398             }
1399             if( !es->fmt.audio_replay_gain.pb_gain[i] )
1400             {
1401                 es->fmt.audio_replay_gain.pb_gain[i] = rg.pb_gain[i];
1402                 es->fmt.audio_replay_gain.pf_gain[i] = rg.pf_gain[i];
1403             }
1404         }
1405         break;
1406     }
1407
1408     case VIDEO_ES:
1409         es->i_channel = p_sys->i_video;
1410         if( es->fmt.video.i_frame_rate && es->fmt.video.i_frame_rate_base )
1411             vlc_ureduce( &es->fmt.video.i_frame_rate,
1412                          &es->fmt.video.i_frame_rate_base,
1413                          es->fmt.video.i_frame_rate,
1414                          es->fmt.video.i_frame_rate_base, 0 );
1415         break;
1416
1417     case SPU_ES:
1418         es->i_channel = p_sys->i_sub;
1419         break;
1420
1421     default:
1422         es->i_channel = 0;
1423         break;
1424     }
1425     es->psz_language = LanguageGetName( es->fmt.psz_language ); /* remember so we only need to do it once */
1426     es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
1427     es->p_dec = NULL;
1428     es->p_dec_record = NULL;
1429     for( i = 0; i < 4; i++ )
1430         es->pb_cc_present[i] = false;
1431     es->p_master = NULL;
1432
1433     if( es->p_pgrm == p_sys->p_pgrm )
1434         EsOutESVarUpdate( out, es, false );
1435
1436     /* Select it if needed */
1437     EsOutSelect( out, es, false );
1438
1439
1440     TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
1441     p_sys->i_id++;  /* always incremented */
1442     switch( es->fmt.i_cat )
1443     {
1444         case AUDIO_ES:
1445             p_sys->i_audio++;
1446             break;
1447         case SPU_ES:
1448             p_sys->i_sub++;
1449             break;
1450         case VIDEO_ES:
1451             p_sys->i_video++;
1452             break;
1453     }
1454
1455     EsOutUpdateInfo( out, es, &es->fmt, NULL );
1456
1457     vlc_mutex_unlock( &p_sys->lock );
1458
1459     return es;
1460 }
1461
1462 static bool EsIsSelected( es_out_id_t *es )
1463 {
1464     if( es->p_master )
1465     {
1466         bool b_decode = false;
1467         if( es->p_master->p_dec )
1468         {
1469             int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1470             if( i_channel != -1 )
1471                 input_DecoderGetCcState( es->p_master->p_dec, &b_decode, i_channel );
1472         }
1473         return b_decode;
1474     }
1475     else
1476     {
1477         return es->p_dec != NULL;
1478     }
1479 }
1480 static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
1481 {
1482     es_out_sys_t   *p_sys = out->p_sys;
1483     input_thread_t *p_input = p_sys->p_input;
1484
1485     p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_input->p->p_sout );
1486     if( p_es->p_dec )
1487     {
1488         if( p_sys->b_buffering )
1489             input_DecoderStartBuffering( p_es->p_dec );
1490
1491         if( !p_es->p_master && p_sys->p_sout_record )
1492         {
1493             p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
1494             if( p_es->p_dec_record && p_sys->b_buffering )
1495                 input_DecoderStartBuffering( p_es->p_dec_record );
1496         }
1497     }
1498
1499     EsOutDecoderChangeDelay( out, p_es );
1500 }
1501 static void EsDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
1502 {
1503     VLC_UNUSED(out);
1504
1505     if( !p_es->p_dec )
1506         return;
1507
1508     input_DecoderDelete( p_es->p_dec );
1509     p_es->p_dec = NULL;
1510
1511     if( p_es->p_dec_record )
1512     {
1513         input_DecoderDelete( p_es->p_dec_record );
1514         p_es->p_dec_record = NULL;
1515     }
1516 }
1517
1518 static void EsSelect( es_out_t *out, es_out_id_t *es )
1519 {
1520     es_out_sys_t   *p_sys = out->p_sys;
1521     input_thread_t *p_input = p_sys->p_input;
1522     vlc_value_t    val;
1523
1524     if( EsIsSelected( es ) )
1525     {
1526         msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
1527         return;
1528     }
1529
1530     if( es->p_master )
1531     {
1532         int i_channel;
1533         if( !es->p_master->p_dec )
1534             return;
1535
1536         i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1537         if( i_channel == -1 || input_DecoderSetCcState( es->p_master->p_dec, true, i_channel ) )
1538             return;
1539     }
1540     else
1541     {
1542         if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
1543         {
1544             if( !var_GetBool( p_input, out->b_sout ? "sout-video" : "video" ) )
1545             {
1546                 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
1547                          es->i_id );
1548                 return;
1549             }
1550         }
1551         else if( es->fmt.i_cat == AUDIO_ES )
1552         {
1553             var_Get( p_input, "audio", &val );
1554             if( !var_GetBool( p_input, out->b_sout ? "sout-audio" : "audio" ) )
1555             {
1556                 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
1557                          es->i_id );
1558                 return;
1559             }
1560         }
1561         if( es->fmt.i_cat == SPU_ES )
1562         {
1563             var_Get( p_input, "spu", &val );
1564             if( !var_GetBool( p_input, out->b_sout ? "sout-spu" : "spu" ) )
1565             {
1566                 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
1567                          es->i_id );
1568                 return;
1569             }
1570         }
1571
1572         EsCreateDecoder( out, es );
1573
1574         if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
1575             return;
1576     }
1577
1578     /* Mark it as selected */
1579     input_SendEventEsSelect( p_input, es->fmt.i_cat, es->i_id );
1580 }
1581
1582 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
1583 {
1584     es_out_sys_t   *p_sys = out->p_sys;
1585     input_thread_t *p_input = p_sys->p_input;
1586
1587     if( !EsIsSelected( es ) )
1588     {
1589         msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
1590         return;
1591     }
1592
1593     if( es->p_master )
1594     {
1595         if( es->p_master->p_dec )
1596         {
1597             int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1598             if( i_channel != -1 )
1599                 input_DecoderSetCcState( es->p_master->p_dec, false, i_channel );
1600         }
1601     }
1602     else
1603     {
1604         const int i_spu_id = var_GetInteger( p_input, "spu-es");
1605         int i;
1606         for( i = 0; i < 4; i++ )
1607         {
1608             if( !es->pb_cc_present[i] || !es->pp_cc_es[i] )
1609                 continue;
1610
1611             if( i_spu_id == es->pp_cc_es[i]->i_id )
1612             {
1613                 /* Force unselection of the CC */
1614                 input_SendEventEsSelect( p_input, SPU_ES, -1 );
1615             }
1616             EsOutDel( out, es->pp_cc_es[i] );
1617
1618             es->pb_cc_present[i] = false;
1619         }
1620         EsDestroyDecoder( out, es );
1621     }
1622
1623     if( !b_update )
1624         return;
1625
1626     /* Update var */
1627     if( es->p_dec == NULL )
1628         return;
1629
1630     /* Mark it as unselected */
1631     input_SendEventEsSelect( p_input, es->fmt.i_cat, -1 );
1632 }
1633
1634 /**
1635  * Select an ES given the current mode
1636  * XXX: you need to take a the lock before (stream.stream_lock)
1637  *
1638  * \param out The es_out structure
1639  * \param es es_out_id structure
1640  * \param b_force ...
1641  * \return nothing
1642  */
1643 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
1644 {
1645     es_out_sys_t      *p_sys = out->p_sys;
1646
1647     int i_cat = es->fmt.i_cat;
1648
1649     if( !p_sys->b_active ||
1650         ( !b_force && es->fmt.i_priority < 0 ) )
1651     {
1652         return;
1653     }
1654
1655     if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
1656     {
1657         if( !EsIsSelected( es ) )
1658             EsSelect( out, es );
1659     }
1660     else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
1661     {
1662         vlc_value_t val;
1663         int i;
1664         var_Get( p_sys->p_input, "programs", &val );
1665         for ( i = 0; i < val.p_list->i_count; i++ )
1666         {
1667             if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )
1668             {
1669                 if( !EsIsSelected( es ) )
1670                     EsSelect( out, es );
1671                 break;
1672             }
1673         }
1674         var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );
1675     }
1676     else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
1677     {
1678         int i_wanted  = -1;
1679
1680         if( es->p_pgrm != p_sys->p_pgrm )
1681             return;
1682
1683         if( i_cat == AUDIO_ES )
1684         {
1685             int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,
1686                                      es->psz_language_code );
1687
1688             if( p_sys->p_es_audio &&
1689                 p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
1690             {
1691                 int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,
1692                                          p_sys->p_es_audio->psz_language_code );
1693
1694                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
1695                     return;
1696                 i_wanted = es->i_channel;
1697             }
1698             else
1699             {
1700                 /* Select audio if (no audio selected yet)
1701                  * - no audio-language
1702                  * - no audio code for the ES
1703                  * - audio code in the requested list */
1704                 if( idx1 >= 0 ||
1705                     !strcmp( es->psz_language_code, "??" ) ||
1706                     !p_sys->ppsz_audio_language )
1707                     i_wanted = es->i_channel;
1708             }
1709
1710             if( p_sys->i_audio_last >= 0 )
1711                 i_wanted = p_sys->i_audio_last;
1712
1713             if( p_sys->i_audio_id >= 0 )
1714             {
1715                 if( es->i_id == p_sys->i_audio_id )
1716                     i_wanted = es->i_channel;
1717                 else
1718                     return;
1719             }
1720         }
1721         else if( i_cat == SPU_ES )
1722         {
1723             int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,
1724                                      es->psz_language_code );
1725
1726             if( p_sys->p_es_sub &&
1727                 p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )
1728             {
1729                 int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,
1730                                          p_sys->p_es_sub->psz_language_code );
1731
1732                 msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",
1733                         idx1, es->psz_language_code, idx2,
1734                         p_sys->p_es_sub->psz_language_code );
1735
1736                 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
1737                     return;
1738                 /* We found a SPU that matches our language request */
1739                 i_wanted  = es->i_channel;
1740             }
1741             else if( idx1 >= 0 )
1742             {
1743                 msg_Dbg( p_sys->p_input, "idx1=%d(%s)",
1744                         idx1, es->psz_language_code );
1745
1746                 i_wanted  = es->i_channel;
1747             }
1748             else if( p_sys->i_default_sub_id >= 0 )
1749             {
1750                 if( es->i_id == p_sys->i_default_sub_id )
1751                     i_wanted = es->i_channel;
1752             }
1753
1754             if( p_sys->i_sub_last >= 0 )
1755                 i_wanted  = p_sys->i_sub_last;
1756
1757             if( p_sys->i_sub_id >= 0 )
1758             {
1759                 if( es->i_id == p_sys->i_sub_id )
1760                     i_wanted = es->i_channel;
1761                 else
1762                     return;
1763             }
1764         }
1765         else if( i_cat == VIDEO_ES )
1766         {
1767             i_wanted  = es->i_channel;
1768         }
1769
1770         if( i_wanted == es->i_channel && !EsIsSelected( es ) )
1771             EsSelect( out, es );
1772     }
1773
1774     /* FIXME TODO handle priority here */
1775     if( EsIsSelected( es ) )
1776     {
1777         if( i_cat == AUDIO_ES )
1778         {
1779             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
1780                 p_sys->p_es_audio &&
1781                 p_sys->p_es_audio != es &&
1782                 EsIsSelected( p_sys->p_es_audio ) )
1783             {
1784                 EsUnselect( out, p_sys->p_es_audio, false );
1785             }
1786             p_sys->p_es_audio = es;
1787         }
1788         else if( i_cat == SPU_ES )
1789         {
1790             if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
1791                 p_sys->p_es_sub &&
1792                 p_sys->p_es_sub != es &&
1793                 EsIsSelected( p_sys->p_es_sub ) )
1794             {
1795                 EsUnselect( out, p_sys->p_es_sub, false );
1796             }
1797             p_sys->p_es_sub = es;
1798         }
1799         else if( i_cat == VIDEO_ES )
1800         {
1801             p_sys->p_es_video = es;
1802         }
1803     }
1804 }
1805
1806 /**
1807  * Send a block for the given es_out
1808  *
1809  * \param out the es_out to send from
1810  * \param es the es_out_id
1811  * \param p_block the data block to send
1812  */
1813 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
1814 {
1815     es_out_sys_t   *p_sys = out->p_sys;
1816     input_thread_t *p_input = p_sys->p_input;
1817     int i_total = 0;
1818
1819     if( libvlc_stats( p_input ) )
1820     {
1821         vlc_mutex_lock( &p_input->p->counters.counters_lock );
1822         stats_UpdateInteger( p_input, p_input->p->counters.p_demux_read,
1823                              p_block->i_buffer, &i_total );
1824         stats_UpdateFloat( p_input , p_input->p->counters.p_demux_bitrate,
1825                            (float)i_total, NULL );
1826         vlc_mutex_unlock( &p_input->p->counters.counters_lock );
1827     }
1828
1829     vlc_mutex_lock( &p_sys->lock );
1830
1831     /* Mark preroll blocks */
1832     if( p_sys->i_preroll_end >= 0 )
1833     {
1834         int64_t i_date = p_block->i_pts;
1835         if( i_date <= 0 )
1836             i_date = p_block->i_dts;
1837
1838         if( i_date < p_sys->i_preroll_end )
1839             p_block->i_flags |= BLOCK_FLAG_PREROLL;
1840     }
1841
1842     p_block->i_rate = 0;
1843
1844     if( !es->p_dec )
1845     {
1846         block_Release( p_block );
1847         vlc_mutex_unlock( &p_sys->lock );
1848         return VLC_SUCCESS;
1849     }
1850
1851     /* Decode */
1852     if( es->p_dec_record )
1853     {
1854         block_t *p_dup = block_Duplicate( p_block );
1855         if( p_dup )
1856             input_DecoderDecode( es->p_dec_record, p_dup );
1857     }
1858     input_DecoderDecode( es->p_dec, p_block );
1859
1860     es_format_t fmt_dsc;
1861     vlc_meta_t  *p_meta_dsc;
1862     if( input_DecoderHasFormatChanged( es->p_dec, &fmt_dsc, &p_meta_dsc ) )
1863     {
1864         EsOutUpdateInfo( out, es, &fmt_dsc, p_meta_dsc );
1865
1866         es_format_Clean( &fmt_dsc );
1867         if( p_meta_dsc )
1868             vlc_meta_Delete( p_meta_dsc );
1869     }
1870
1871     /* Check CC status */
1872     bool pb_cc[4];
1873
1874     input_DecoderIsCcPresent( es->p_dec, pb_cc );
1875     for( int i = 0; i < 4; i++ )
1876     {
1877         static const vlc_fourcc_t fcc[4] = {
1878             VLC_FOURCC('c', 'c', '1', ' '),
1879             VLC_FOURCC('c', 'c', '2', ' '),
1880             VLC_FOURCC('c', 'c', '3', ' '),
1881             VLC_FOURCC('c', 'c', '4', ' '),
1882         };
1883         es_format_t fmt;
1884
1885         if(  es->pb_cc_present[i] || !pb_cc[i] )
1886             continue;
1887         msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id );
1888
1889         es_format_Init( &fmt, SPU_ES, fcc[i] );
1890         fmt.i_group = es->fmt.i_group;
1891         if( asprintf( &fmt.psz_description,
1892                       _("Closed captions %u"), 1 + i ) == -1 )
1893             fmt.psz_description = NULL;
1894         es->pp_cc_es[i] = EsOutAdd( out, &fmt );
1895         es->pp_cc_es[i]->p_master = es;
1896         es_format_Clean( &fmt );
1897
1898         /* */
1899         es->pb_cc_present[i] = true;
1900     }
1901
1902     vlc_mutex_unlock( &p_sys->lock );
1903
1904     return VLC_SUCCESS;
1905 }
1906
1907 /*****************************************************************************
1908  * EsOutDel:
1909  *****************************************************************************/
1910 static void EsOutDel( es_out_t *out, es_out_id_t *es )
1911 {
1912     es_out_sys_t *p_sys = out->p_sys;
1913     bool b_reselect = false;
1914     int i;
1915
1916     vlc_mutex_lock( &p_sys->lock );
1917
1918     /* We don't try to reselect */
1919     if( es->p_dec )
1920     {
1921         while( !p_sys->p_input->b_die && !p_sys->b_buffering && es->p_dec )
1922         {
1923             if( input_DecoderIsEmpty( es->p_dec ) &&
1924                 ( !es->p_dec_record || input_DecoderIsEmpty( es->p_dec_record ) ))
1925                 break;
1926             /* FIXME there should be a way to have auto deleted es, but there will be
1927              * a problem when another codec of the same type is created (mainly video) */
1928             msleep( 20*1000 );
1929         }
1930         EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
1931     }
1932
1933     if( es->p_pgrm == p_sys->p_pgrm )
1934         EsOutESVarUpdate( out, es, true );
1935
1936     TAB_REMOVE( p_sys->i_es, p_sys->es, es );
1937
1938     es->p_pgrm->i_es--;
1939     if( es->p_pgrm->i_es == 0 )
1940     {
1941         msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" );
1942     }
1943
1944     if( p_sys->p_es_audio == es || p_sys->p_es_video == es ||
1945         p_sys->p_es_sub == es ) b_reselect = true;
1946
1947     if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
1948     if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
1949     if( p_sys->p_es_sub   == es ) p_sys->p_es_sub   = NULL;
1950
1951     switch( es->fmt.i_cat )
1952     {
1953         case AUDIO_ES:
1954             p_sys->i_audio--;
1955             break;
1956         case SPU_ES:
1957             p_sys->i_sub--;
1958             break;
1959         case VIDEO_ES:
1960             p_sys->i_video--;
1961             break;
1962     }
1963
1964     /* Re-select another track when needed */
1965     if( b_reselect )
1966     {
1967         for( i = 0; i < p_sys->i_es; i++ )
1968         {
1969             if( es->fmt.i_cat == p_sys->es[i]->fmt.i_cat )
1970                 EsOutSelect( out, p_sys->es[i], false );
1971         }
1972     }
1973
1974     free( es->psz_language );
1975     free( es->psz_language_code );
1976
1977     es_format_Clean( &es->fmt );
1978
1979     vlc_mutex_unlock( &p_sys->lock );
1980
1981     free( es );
1982 }
1983
1984 /**
1985  * Control query handler
1986  *
1987  * \param out the es_out to control
1988  * \param i_query A es_out query as defined in include/ninput.h
1989  * \param args a variable list of arguments for the query
1990  * \return VLC_SUCCESS or an error code
1991  */
1992 static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
1993 {
1994     es_out_sys_t *p_sys = out->p_sys;
1995     bool  b, *pb;
1996     int         i;
1997
1998     es_out_id_t *es;
1999
2000     switch( i_query )
2001     {
2002         case ES_OUT_SET_ES_STATE:
2003             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2004             b = (bool) va_arg( args, int );
2005             if( b && !EsIsSelected( es ) )
2006             {
2007                 EsSelect( out, es );
2008                 return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC;
2009             }
2010             else if( !b && EsIsSelected( es ) )
2011             {
2012                 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
2013                 return VLC_SUCCESS;
2014             }
2015             return VLC_SUCCESS;
2016
2017         case ES_OUT_GET_ES_STATE:
2018             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2019             pb = (bool*) va_arg( args, bool * );
2020
2021             *pb = EsIsSelected( es );
2022             return VLC_SUCCESS;
2023
2024         case ES_OUT_SET_ACTIVE:
2025         {
2026             b = (bool) va_arg( args, int );
2027             if( b && !p_sys->b_active && p_sys->i_es > 0 )
2028             {
2029                 /* XXX Terminate vout if there are tracks but no video one.
2030                  * This one is not mandatory but is he earliest place where it
2031                  * can be done */
2032                 for( i = 0; i < p_sys->i_es; i++ )
2033                 {
2034                     es_out_id_t *p_es = p_sys->es[i];
2035                     if( p_es->fmt.i_cat == VIDEO_ES )
2036                         break;
2037                 }
2038                 if( i >= p_sys->i_es )
2039                     input_ressource_TerminateVout( p_sys->p_input->p->p_ressource );
2040             }
2041             p_sys->b_active = b;
2042             return VLC_SUCCESS;
2043         }
2044
2045         case ES_OUT_SET_MODE:
2046             i = (int) va_arg( args, int );
2047             if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
2048                 i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )
2049             {
2050                 p_sys->i_mode = i;
2051
2052                 /* Reapply policy mode */
2053                 for( i = 0; i < p_sys->i_es; i++ )
2054                 {
2055                     if( EsIsSelected( p_sys->es[i] ) )
2056                     {
2057                         EsUnselect( out, p_sys->es[i],
2058                                     p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2059                     }
2060                 }
2061                 for( i = 0; i < p_sys->i_es; i++ )
2062                 {
2063                     EsOutSelect( out, p_sys->es[i], false );
2064                 }
2065                 return VLC_SUCCESS;
2066             }
2067             return VLC_EGENERIC;
2068
2069         case ES_OUT_SET_ES:
2070         case ES_OUT_RESTART_ES:
2071         {
2072             int i_cat;
2073
2074             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2075
2076             if( es == NULL )
2077                 i_cat = UNKNOWN_ES;
2078             else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
2079                 i_cat = AUDIO_ES;
2080             else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
2081                 i_cat = VIDEO_ES;
2082             else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
2083                 i_cat = SPU_ES;
2084             else
2085                 i_cat = -1;
2086
2087             for( i = 0; i < p_sys->i_es; i++ )
2088             {
2089                 if( i_cat == -1 )
2090                 {
2091                     if( es == p_sys->es[i] )
2092                     {
2093                         EsOutSelect( out, es, true );
2094                         break;
2095                     }
2096                 }
2097                 else
2098                 {
2099                     if( i_cat == UNKNOWN_ES || p_sys->es[i]->fmt.i_cat == i_cat )
2100                     {
2101                         if( EsIsSelected( p_sys->es[i] ) )
2102                         {
2103                             if( i_query == ES_OUT_RESTART_ES )
2104                             {
2105                                 if( p_sys->es[i]->p_dec )
2106                                 {
2107                                     EsDestroyDecoder( out, p_sys->es[i] );
2108                                     EsCreateDecoder( out, p_sys->es[i] );
2109                                 }
2110                             }
2111                             else
2112                             {
2113                                 EsUnselect( out, p_sys->es[i],
2114                                             p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2115                             }
2116                         }
2117                     }
2118                 }
2119             }
2120             return VLC_SUCCESS;
2121         }
2122  
2123         case ES_OUT_SET_ES_DEFAULT:
2124         {
2125             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2126
2127             if( es == NULL )
2128             {
2129                 /*p_sys->i_default_video_id = -1;*/
2130                 /*p_sys->i_default_audio_id = -1;*/
2131                 p_sys->i_default_sub_id = -1;
2132             }
2133             else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
2134             {
2135                 /*p_sys->i_default_video_id = -1;*/
2136             }
2137             else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
2138             {
2139                 /*p_sys->i_default_audio_id = -1;*/
2140             }
2141             else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
2142             {
2143                 p_sys->i_default_sub_id = -1;
2144             }
2145             else
2146             {
2147                 /*if( es->fmt.i_cat == VIDEO_ES )
2148                     p_sys->i_default_video_id = es->i_id;
2149                 else
2150                 if( es->fmt.i_cat == AUDIO_ES )
2151                     p_sys->i_default_audio_id = es->i_id;
2152                 else*/
2153                 if( es->fmt.i_cat == SPU_ES )
2154                     p_sys->i_default_sub_id = es->i_id;
2155             }
2156             return VLC_SUCCESS;
2157         }
2158
2159         case ES_OUT_SET_PCR:
2160         case ES_OUT_SET_GROUP_PCR:
2161         {
2162             es_out_pgrm_t *p_pgrm = NULL;
2163             int            i_group = 0;
2164             int64_t        i_pcr;
2165
2166             if( i_query == ES_OUT_SET_PCR )
2167             {
2168                 p_pgrm = p_sys->p_pgrm;
2169             }
2170             else
2171             {
2172                 int i;
2173                 i_group = (int)va_arg( args, int );
2174                 for( i = 0; i < p_sys->i_pgrm; i++ )
2175                 {
2176                     if( p_sys->pgrm[i]->i_id == i_group )
2177                     {
2178                         p_pgrm = p_sys->pgrm[i];
2179                         break;
2180                     }
2181                 }
2182             }
2183             if( p_pgrm == NULL )
2184                 p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
2185
2186             i_pcr = (int64_t)va_arg( args, int64_t );
2187             /* search program
2188              * TODO do not use mdate() but proper stream acquisition date */
2189             input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input),
2190                                 p_sys->p_input->p->b_can_pace_control || p_sys->b_buffering, i_pcr, mdate() );
2191             /* Check buffering state on master clock update */
2192             if( p_sys->b_buffering && p_pgrm == p_sys->p_pgrm )
2193                 EsOutDecodersStopBuffering( out, false );
2194
2195             return VLC_SUCCESS;
2196         }
2197
2198         case ES_OUT_RESET_PCR:
2199             msg_Err( p_sys->p_input, "ES_OUT_RESET_PCR called" );
2200             EsOutChangePosition( out );
2201             return VLC_SUCCESS;
2202
2203         case ES_OUT_SET_GROUP:
2204         {
2205             int j;
2206             i = (int) va_arg( args, int );
2207             for( j = 0; j < p_sys->i_pgrm; j++ )
2208             {
2209                 es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
2210                 if( p_pgrm->i_id == i )
2211                 {
2212                     EsOutProgramSelect( out, p_pgrm );
2213                     return VLC_SUCCESS;
2214                 }
2215             }
2216             return VLC_EGENERIC;
2217         }
2218
2219         case ES_OUT_SET_ES_FMT:
2220         {
2221             /* This ain't pretty but is need by some demuxers (eg. Ogg )
2222              * to update the p_extra data */
2223             es_format_t *p_fmt;
2224             es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2225             p_fmt = (es_format_t*) va_arg( args, es_format_t * );
2226             if( es == NULL )
2227                 return VLC_EGENERIC;
2228
2229             if( p_fmt->i_extra )
2230             {
2231                 es->fmt.i_extra = p_fmt->i_extra;
2232                 es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );
2233                 memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );
2234
2235                 if( !es->p_dec )
2236                     return VLC_SUCCESS;
2237 #if 1
2238                 EsDestroyDecoder( out, es );
2239
2240                 EsCreateDecoder( out, es );
2241 #else
2242                 es->p_dec->fmt_in.i_extra = p_fmt->i_extra;
2243                 es->p_dec->fmt_in.p_extra =
2244                     realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );
2245                 memcpy( es->p_dec->fmt_in.p_extra,
2246                         p_fmt->p_extra, p_fmt->i_extra );
2247 #endif
2248             }
2249
2250             return VLC_SUCCESS;
2251         }
2252
2253         case ES_OUT_SET_NEXT_DISPLAY_TIME:
2254         {
2255             const int64_t i_date = (int64_t)va_arg( args, int64_t );
2256
2257             if( i_date < 0 )
2258                 return VLC_EGENERIC;
2259
2260             p_sys->i_preroll_end = i_date;
2261
2262             return VLC_SUCCESS;
2263         }
2264         case ES_OUT_SET_GROUP_META:
2265         {
2266             int i_group = (int)va_arg( args, int );
2267             vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
2268
2269             EsOutProgramMeta( out, i_group, p_meta );
2270             return VLC_SUCCESS;
2271         }
2272         case ES_OUT_SET_GROUP_EPG:
2273         {
2274             int i_group = (int)va_arg( args, int );
2275             vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
2276
2277             EsOutProgramEpg( out, i_group, p_epg );
2278             return VLC_SUCCESS;
2279         }
2280         case ES_OUT_DEL_GROUP:
2281         {
2282             int i_group = (int)va_arg( args, int );
2283
2284             return EsOutProgramDel( out, i_group );
2285         }
2286
2287         case ES_OUT_GET_WAKE_UP:
2288         {
2289             mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
2290             *pi_wakeup = EsOutGetWakeup( out );
2291             return VLC_SUCCESS;
2292         }
2293
2294         case ES_OUT_SET_ES_BY_ID:
2295         case ES_OUT_RESTART_ES_BY_ID:
2296         case ES_OUT_SET_ES_DEFAULT_BY_ID:
2297         {
2298             const int i_id = (int)va_arg( args, int );
2299             es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2300             int i_new_query;
2301
2302             switch( i_query )
2303             {
2304             case ES_OUT_SET_ES_BY_ID:         i_new_query = ES_OUT_SET_ES; break;
2305             case ES_OUT_RESTART_ES_BY_ID:     i_new_query = ES_OUT_RESTART_ES; break;
2306             case ES_OUT_SET_ES_DEFAULT_BY_ID: i_new_query = ES_OUT_SET_ES_DEFAULT; break;
2307             default:
2308               assert(0);
2309             }
2310             /* TODO if the lock is made non recursive it should be changed */
2311             int i_ret = es_out_Control( out, i_new_query, p_es );
2312
2313             /* Clean up vout after user action (in active mode only).
2314              * FIXME it does not work well with multiple video windows */
2315             if( p_sys->b_active )
2316                 input_ressource_TerminateVout( p_sys->p_input->p->p_ressource );
2317             return i_ret;
2318         }
2319
2320         case ES_OUT_GET_BUFFERING:
2321             pb = (bool *)va_arg( args, bool* );
2322             *pb = p_sys->b_buffering;
2323             return VLC_SUCCESS;
2324
2325         case ES_OUT_GET_EMPTY:
2326             pb = (bool *)va_arg( args, bool* );
2327             *pb = EsOutDecodersIsEmpty( out );
2328             return VLC_SUCCESS;
2329
2330         case ES_OUT_SET_DELAY:
2331         {
2332             const int i_cat = (int)va_arg( args, int );
2333             const mtime_t i_delay = (mtime_t)va_arg( args, mtime_t );
2334             EsOutSetDelay( out, i_cat, i_delay );
2335             return VLC_SUCCESS;
2336         }
2337
2338         case ES_OUT_SET_RECORD_STATE:
2339             b = (bool) va_arg( args, int );
2340             return EsOutSetRecord( out, b );
2341
2342         case ES_OUT_SET_PAUSE_STATE:
2343         {
2344             const bool b_source_paused = (bool)va_arg( args, int );
2345             const bool b_paused = (bool)va_arg( args, int );
2346             const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
2347
2348             assert( !b_source_paused == !b_paused );
2349             EsOutChangePause( out, b_paused, i_date );
2350
2351             return VLC_SUCCESS;
2352         }
2353
2354         case ES_OUT_SET_RATE:
2355         {
2356             const int i_src_rate = (int)va_arg( args, int );
2357             const int i_rate = (int)va_arg( args, int );
2358
2359             assert( i_src_rate == i_rate );
2360             EsOutChangeRate( out, i_rate );
2361
2362             return VLC_SUCCESS;
2363         }
2364
2365         case ES_OUT_SET_TIME:
2366         {
2367             const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
2368
2369             assert( i_date == -1 );
2370             EsOutChangePosition( out );
2371
2372             return VLC_SUCCESS;
2373         }
2374
2375         case ES_OUT_SET_FRAME_NEXT:
2376             EsOutFrameNext( out );
2377             return VLC_SUCCESS;
2378
2379         case ES_OUT_SET_TIMES:
2380         {
2381             double f_position = (double)va_arg( args, double );
2382             mtime_t i_time = (mtime_t)va_arg( args, mtime_t );
2383             mtime_t i_length = (mtime_t)va_arg( args, mtime_t );
2384
2385             /* Fix for buffering delay */
2386             const mtime_t i_delay = EsOutGetBuffering( out );
2387
2388             i_time -= i_delay;
2389             if( i_time < 0 )
2390                 i_time = 0;
2391
2392             if( i_length > 0 )
2393                 f_position -= (double)i_delay / i_length;
2394             if( f_position < 0 )
2395                 f_position = 0;
2396
2397             if( !p_sys->b_buffering )
2398                 input_SendEventTimes( p_sys->p_input, f_position, i_time, i_length );
2399             return VLC_SUCCESS;
2400         }
2401         case ES_OUT_SET_JITTER:
2402         {
2403             mtime_t i_pts_delay = (mtime_t)va_arg( args, mtime_t );
2404             int     i_cr_average = (int)va_arg( args, int );
2405
2406             if( i_pts_delay == p_sys->i_pts_delay &&
2407                 i_cr_average == p_sys->i_cr_average )
2408                 return VLC_SUCCESS;
2409
2410             p_sys->i_pts_delay = i_pts_delay;
2411             p_sys->i_cr_average = i_cr_average;
2412
2413             for( int i = 0; i < p_sys->i_pgrm; i++ )
2414                 input_clock_SetJitter( p_sys->pgrm[i]->p_clock,
2415                                        i_pts_delay, i_cr_average );
2416             return VLC_SUCCESS;
2417         }
2418
2419         default:
2420             msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
2421             return VLC_EGENERIC;
2422     }
2423 }
2424 static int EsOutControl( es_out_t *out, int i_query, va_list args )
2425 {
2426     es_out_sys_t *p_sys = out->p_sys;
2427     int i_ret;
2428
2429     vlc_mutex_lock( &p_sys->lock );
2430     i_ret = EsOutControlLocked( out, i_query, args );
2431     vlc_mutex_unlock( &p_sys->lock );
2432
2433     return i_ret;
2434 }
2435
2436 /****************************************************************************
2437  * LanguageGetName: try to expend iso639 into plain name
2438  ****************************************************************************/
2439 static char *LanguageGetName( const char *psz_code )
2440 {
2441     const iso639_lang_t *pl;
2442
2443     if( psz_code == NULL )
2444     {
2445         return strdup( "" );
2446     }
2447
2448     if( strlen( psz_code ) == 2 )
2449     {
2450         pl = GetLang_1( psz_code );
2451     }
2452     else if( strlen( psz_code ) == 3 )
2453     {
2454         pl = GetLang_2B( psz_code );
2455         if( !strcmp( pl->psz_iso639_1, "??" ) )
2456         {
2457             pl = GetLang_2T( psz_code );
2458         }
2459     }
2460     else
2461     {
2462         return strdup( psz_code );
2463     }
2464
2465     if( !strcmp( pl->psz_iso639_1, "??" ) )
2466     {
2467        return strdup( psz_code );
2468     }
2469     else
2470     {
2471         if( *pl->psz_native_name )
2472         {
2473             return strdup( pl->psz_native_name );
2474         }
2475         return strdup( pl->psz_eng_name );
2476     }
2477 }
2478
2479 /* Get a 2 char code */
2480 static char *LanguageGetCode( const char *psz_lang )
2481 {
2482     const iso639_lang_t *pl;
2483
2484     if( psz_lang == NULL || *psz_lang == '\0' )
2485         return strdup("??");
2486
2487     for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
2488     {
2489         if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
2490             !strcasecmp( pl->psz_native_name, psz_lang ) ||
2491             !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
2492             !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
2493             !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
2494             break;
2495     }
2496
2497     if( pl->psz_iso639_1 != NULL )
2498         return strdup( pl->psz_iso639_1 );
2499
2500     return strdup("??");
2501 }
2502
2503 static char **LanguageSplit( const char *psz_langs )
2504 {
2505     char *psz_dup;
2506     char *psz_parser;
2507     char **ppsz = NULL;
2508     int i_psz = 0;
2509
2510     if( psz_langs == NULL ) return NULL;
2511
2512     psz_parser = psz_dup = strdup(psz_langs);
2513
2514     while( psz_parser && *psz_parser )
2515     {
2516         char *psz;
2517         char *psz_code;
2518
2519         psz = strchr(psz_parser, ',' );
2520         if( psz ) *psz++ = '\0';
2521
2522         if( !strcmp( psz_parser, "any" ) )
2523         {
2524             TAB_APPEND( i_psz, ppsz, strdup("any") );
2525         }
2526         else
2527         {
2528             psz_code = LanguageGetCode( psz_parser );
2529             if( strcmp( psz_code, "??" ) )
2530             {
2531                 TAB_APPEND( i_psz, ppsz, psz_code );
2532             }
2533             else
2534             {
2535                 free( psz_code );
2536             }
2537         }
2538
2539         psz_parser = psz;
2540     }
2541
2542     if( i_psz )
2543     {
2544         TAB_APPEND( i_psz, ppsz, NULL );
2545     }
2546
2547     free( psz_dup );
2548     return ppsz;
2549 }
2550
2551 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )
2552 {
2553     int i;
2554
2555     if( !ppsz_langs || !psz_lang ) return -1;
2556
2557     for( i = 0; ppsz_langs[i]; i++ )
2558     {
2559         if( !strcasecmp( ppsz_langs[i], psz_lang ) ||
2560             !strcasecmp( ppsz_langs[i], "any" ) )
2561         {
2562             return i;
2563         }
2564     }
2565
2566     return -1;
2567 }
2568
2569 /****************************************************************************
2570  * EsOutUpdateInfo:
2571  * - add meta info to the playlist item
2572  ****************************************************************************/
2573 static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t *fmt, const vlc_meta_t *p_meta )
2574 {
2575     es_out_sys_t   *p_sys = out->p_sys;
2576     input_thread_t *p_input = p_sys->p_input;
2577     const es_format_t *p_fmt_es = &es->fmt;
2578     char           *psz_cat;
2579     lldiv_t         div;
2580
2581     /* Create category name */
2582     if( asprintf( &psz_cat, _("Stream %d"), es->i_meta_id ) == -1 )
2583         return;
2584
2585     /* Remove previous information */
2586     input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL );
2587
2588     /* Add informations */
2589     const char *psz_type;
2590     switch( fmt->i_cat )
2591     {
2592     case AUDIO_ES:
2593         psz_type = _("Audio");
2594         break;
2595     case VIDEO_ES:
2596         psz_type = _("Video");
2597         break;
2598     case SPU_ES:
2599         psz_type = _("Subtitle");
2600         break;
2601     default:
2602         psz_type = NULL;
2603         break;
2604     }
2605
2606     if( psz_type )
2607         input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Type"), psz_type );
2608
2609     if( es->i_meta_id != es->i_id )
2610         input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Original ID"),
2611                        "%d", es->i_id );
2612
2613     input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),
2614                    "%.4s", (char*)&p_fmt_es->i_codec );
2615
2616     if( es->psz_language && *es->psz_language )
2617         input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),
2618                        "%s", es->psz_language );
2619     if( fmt->psz_description && *fmt->psz_description )
2620         input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Description"),
2621                        "%s", fmt->psz_description );
2622
2623     switch( fmt->i_cat )
2624     {
2625     case AUDIO_ES:
2626         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2627                        _("Type"), _("Audio") );
2628
2629         if( fmt->audio.i_physical_channels & AOUT_CHAN_PHYSMASK )
2630             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),
2631                            "%s", _( aout_FormatPrintChannels( &fmt->audio ) ) );
2632         else if( fmt->audio.i_channels > 0 )
2633             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),
2634                            "%u", fmt->audio.i_channels );
2635
2636         if( fmt->audio.i_rate > 0 )
2637         {
2638             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),
2639                            _("%u Hz"), fmt->audio.i_rate );
2640             /* FIXME that should be removed or improved ! (used by text/strings.c) */
2641             var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate );
2642         }
2643
2644         unsigned int i_bitspersample = fmt->audio.i_bitspersample;
2645         if( i_bitspersample <= 0 )
2646             i_bitspersample = aout_BitsPerSample( p_fmt_es->i_codec );
2647         if( i_bitspersample > 0 )
2648             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2649                            _("Bits per sample"), "%u",
2650                            i_bitspersample );
2651
2652         if( fmt->i_bitrate > 0 )
2653         {
2654             input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),
2655                            _("%u kb/s"), fmt->i_bitrate / 1000 );
2656             /* FIXME that should be removed or improved ! (used by text/strings.c) */
2657             var_SetInteger( p_input, "bit-rate", fmt->i_bitrate );
2658         }
2659         for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
2660         {
2661             const audio_replay_gain_t *p_rg = &fmt->audio_replay_gain;
2662             if( !p_rg->pb_gain[i] )
2663                 continue;
2664             const char *psz_name;
2665             if( i == AUDIO_REPLAY_GAIN_TRACK )
2666                 psz_name = _("Track replay gain");
2667             else
2668                 psz_name = _("Album replay gain");
2669             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2670                            psz_name, _("%.2f dB"), p_rg->pf_gain[i] );
2671         }
2672         break;
2673
2674     case VIDEO_ES:
2675         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2676                        _("Type"), _("Video") );
2677
2678         if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
2679             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2680                            _("Resolution"), "%ux%u",
2681                            fmt->video.i_width, fmt->video.i_height );
2682
2683         if( fmt->video.i_visible_width > 0 &&
2684             fmt->video.i_visible_height > 0 )
2685             input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2686                            _("Display resolution"), "%ux%u",
2687                            fmt->video.i_visible_width,
2688                            fmt->video.i_visible_height);
2689        if( fmt->video.i_frame_rate > 0 &&
2690            fmt->video.i_frame_rate_base > 0 )
2691        {
2692            div = lldiv( (float)fmt->video.i_frame_rate /
2693                                fmt->video.i_frame_rate_base * 1000000,
2694                                1000000 );
2695            if( div.rem > 0 )
2696                input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2697                               _("Frame rate"), "%"PRId64".%06u",
2698                               div.quot, (unsigned int )div.rem );
2699            else
2700                input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2701                               _("Frame rate"), "%"PRId64, div.quot );
2702        }
2703        break;
2704
2705     case SPU_ES:
2706         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2707                        _("Type"), _("Subtitle") );
2708         break;
2709
2710     default:
2711         break;
2712     }
2713
2714     /* Append generic meta */
2715     if( p_meta )
2716     {
2717         char **ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags );
2718         for( int i = 0; ppsz_all_keys && ppsz_all_keys[i]; i++ )
2719         {
2720             char *psz_key = ppsz_all_keys[i];
2721             char *psz_value = vlc_dictionary_value_for_key( &p_meta->extra_tags, psz_key );
2722
2723             if( psz_value )
2724                 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(psz_key), _(psz_value) );
2725             free( psz_key );
2726         }
2727         free( ppsz_all_keys );
2728     }
2729
2730     free( psz_cat );
2731 }