root/src/input/es_out.c

Revision 2734c1b4d4f9ddbeff77ad5929c60d486a3151bf, 64.8 kB (checked in by Rémi Denis-Courmont <rdenis@simphalempin.com>, 5 days ago)

memset outside of lock

  • Property mode set to 100644
Line 
1 /*****************************************************************************
2  * es_out.c: Es Out handler for input.
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <assert.h>
34 #include <vlc_common.h>
35
36 #include <vlc_input.h>
37 #include <vlc_es_out.h>
38 #include <vlc_block.h>
39 #include <vlc_aout.h>
40
41 #include "input_internal.h"
42
43 #include "../stream_output/stream_output.h"
44
45 #include <vlc_iso_lang.h>
46 /* FIXME we should find a better way than including that */
47 #include "../text/iso-639_def.h"
48
49 /*****************************************************************************
50  * Local prototypes
51  *****************************************************************************/
52 typedef struct
53 {
54     /* Program ID */
55     int i_id;
56
57     /* Number of es for this pgrm */
58     int i_es;
59
60     bool b_selected;
61
62     /* Clock for this program */
63     input_clock_t clock;
64
65     char    *psz_name;
66     char    *psz_now_playing;
67     char    *psz_publisher;
68
69     vlc_epg_t *p_epg;
70 } es_out_pgrm_t;
71
72 struct es_out_id_t
73 {
74     /* ES ID */
75     int       i_id;
76     es_out_pgrm_t *p_pgrm;
77
78     /* Misc. */
79     int64_t i_preroll_end;
80
81     /* Channel in the track type */
82     int         i_channel;
83     es_format_t fmt;
84     char        *psz_language;
85     char        *psz_language_code;
86
87     decoder_t   *p_dec;
88     decoder_t   *p_dec_record;
89
90     /* Fields for Video with CC */
91     bool  pb_cc_present[4];
92     es_out_id_t  *pp_cc_es[4];
93
94     /* Field for CC track from a master video */
95     es_out_id_t *p_master;
96 };
97
98 struct es_out_sys_t
99 {
100     input_thread_t *p_input;
101
102     /* all programs */
103     int           i_pgrm;
104     es_out_pgrm_t **pgrm;
105     es_out_pgrm_t **pp_selected_pgrm; /* --programs */
106     es_out_pgrm_t *p_pgrm;  /* Master program */
107
108     /* all es */
109     int         i_id;
110     int         i_es;
111     es_out_id_t **es;
112
113     /* mode gestion */
114     bool  b_active;
115     int         i_mode;
116
117     /* es count */
118     int         i_audio;
119     int         i_video;
120     int         i_sub;
121
122     /* es to select */
123     int         i_audio_last, i_audio_id;
124     int         i_sub_last, i_sub_id;
125     int         i_default_sub_id;   /* As specified in container; if applicable */
126     char        **ppsz_audio_language;
127     char        **ppsz_sub_language;
128
129     /* current main es */
130     es_out_id_t *p_es_audio;
131     es_out_id_t *p_es_video;
132     es_out_id_t *p_es_sub;
133
134     /* delay */
135     int64_t i_audio_delay;
136     int64_t i_spu_delay;
137
138     /* Rate used to rescale ES ts */
139     int         i_rate;
140
141     /* Record */
142     sout_instance_t *p_sout_record;
143 };
144
145 static es_out_id_t *EsOutAdd    ( es_out_t *, es_format_t * );
146 static int          EsOutSend   ( es_out_t *, es_out_id_t *, block_t * );
147 static void         EsOutDel    ( es_out_t *, es_out_id_t * );
148 static void         EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force );
149 static int          EsOutControl( es_out_t *, int i_query, va_list );
150
151 static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );
152
153 static bool EsIsSelected( es_out_id_t *es );
154 static void EsSelect( es_out_t *out, es_out_id_t *es );
155 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
156 static char *LanguageGetName( const char *psz_code );
157 static char *LanguageGetCode( const char *psz_lang );
158 static char **LanguageSplit( const char *psz_langs );
159 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );
160
161 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
162
163 static const vlc_fourcc_t EsOutFourccClosedCaptions[4] = {
164     VLC_FOURCC('c', 'c', '1', ' '),
165     VLC_FOURCC('c', 'c', '2', ' '),
166     VLC_FOURCC('c', 'c', '3', ' '),
167     VLC_FOURCC('c', 'c', '4', ' '),
168 };
169 static inline int EsOutGetClosedCaptionsChannel( vlc_fourcc_t fcc )
170 {
171     int i;
172     for( i = 0; i < 4; i++ )
173     {
174         if( fcc == EsOutFourccClosedCaptions[i] )
175             return i;
176     }
177     return -1;
178 }
179
180
181 /*****************************************************************************
182  * input_EsOutNew:
183  *****************************************************************************/
184 es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
185 {
186     vlc_value_t  val;
187     int i;
188
189     es_out_t     *out = malloc( sizeof( es_out_t ) );
190     if( !out ) return NULL;
191
192     es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );
193     if( !p_sys )
194     {
195         free( out );
196         return NULL;
197     }
198
199     out->pf_add     = EsOutAdd;
200     out->pf_send    = EsOutSend;
201     out->pf_del     = EsOutDel;
202     out->pf_control = EsOutControl;
203     out->p_sys      = p_sys;
204     out->b_sout     = p_input->p->p_sout != NULL;
205
206     p_sys->p_input = p_input;
207
208     p_sys->b_active = false;
209     p_sys->i_mode   = ES_OUT_MODE_AUTO;
210
211
212     TAB_INIT( p_sys->i_pgrm, p_sys->pgrm );
213     p_sys->p_pgrm   = NULL;
214
215     p_sys->i_id    = 0;
216
217     TAB_INIT( p_sys->i_es, p_sys->es );
218
219     p_sys->i_audio = 0;
220     p_sys->i_video = 0;
221     p_sys->i_sub   = 0;
222
223     /* */
224     var_Get( p_input, "audio-track", &val );
225     p_sys->i_audio_last = val.i_int;
226
227     var_Get( p_input, "sub-track", &val );
228     p_sys->i_sub_last = val.i_int;
229
230     p_sys->i_default_sub_id   = -1;
231
232     if( !p_input->b_preparsing )
233     {
234         var_Get( p_input, "audio-language", &val );
235         p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);
236         if( p_sys->ppsz_audio_language )
237         {
238             for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
239                 msg_Dbg( p_input, "selected audio language[%d] %s",
240                          i, p_sys->ppsz_audio_language[i] );
241         }
242         free( val.psz_string );
243
244         var_Get( p_input, "sub-language", &val );
245         p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);
246         if( p_sys->ppsz_sub_language )
247         {
248             for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
249                 msg_Dbg( p_input, "selected subtitle language[%d] %s",
250                          i, p_sys->ppsz_sub_language[i] );
251         }
252         free( val.psz_string );
253     }
254     else
255     {
256         p_sys->ppsz_sub_language = NULL;
257         p_sys->ppsz_audio_language = NULL;
258     }
259
260     var_Get( p_input, "audio-track-id", &val );
261     p_sys->i_audio_id = val.i_int;
262
263     var_Get( p_input, "sub-track-id", &val );
264     p_sys->i_sub_id = val.i_int;
265
266     p_sys->p_es_audio = NULL;
267     p_sys->p_es_video = NULL;
268     p_sys->p_es_sub   = NULL;
269
270     p_sys->i_audio_delay= 0;
271     p_sys->i_spu_delay  = 0;
272
273     p_sys->i_rate = i_rate;
274
275     p_sys->p_sout_record = NULL;
276
277     return out;
278 }
279
280 /*****************************************************************************
281  * input_EsOutDelete:
282  *****************************************************************************/
283 void input_EsOutDelete( es_out_t *out )
284 {
285     es_out_sys_t *p_sys = out->p_sys;
286     int i;
287
288     if( p_sys->p_sout_record )
289         input_EsOutSetRecord( out, false );
290
291     for( i = 0; i < p_sys->i_es; i++ )
292     {
293         if( p_sys->es[i]->p_dec )
294             input_DecoderDelete( p_sys->es[i]->p_dec );
295
296         free( p_sys->es[i]->psz_language );
297         free( p_sys->es[i]->psz_language_code );
298         es_format_Clean( &p_sys->es[i]->fmt );
299
300         free( p_sys->es[i] );
301     }
302     if( p_sys->ppsz_audio_language )
303     {
304         for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
305             free( p_sys->ppsz_audio_language[i] );
306         free( p_sys->ppsz_audio_language );
307     }
308     if( p_sys->ppsz_sub_language )
309     {
310         for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
311             free( p_sys->ppsz_sub_language[i] );
312         free( p_sys->ppsz_sub_language );
313     }
314     free( p_sys->es );
315
316     /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
317     for( i = 0; i < p_sys->i_pgrm; i++ )
318     {
319         es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
320         free( p_pgrm->psz_now_playing );
321         free( p_pgrm->psz_publisher );
322         free( p_pgrm->psz_name );
323         if( p_pgrm->p_epg )
324             vlc_epg_Delete( p_pgrm->p_epg );
325
326         free( p_pgrm );
327     }
328     TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
329
330     free( p_sys );
331     free( out );
332 }
333
334 es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )
335 {
336     int i;
337     if( i_id < 0 )
338     {
339         /* Special HACK, -i_id is tha cat of the stream */
340         return (es_out_id_t*)((uint8_t*)NULL-i_id);
341     }
342
343     for( i = 0; i < out->p_sys->i_es; i++ )
344     {
345         if( out->p_sys->es[i]->i_id == i_id )
346             return out->p_sys->es[i];
347     }
348     return NULL;
349 }
350
351 static void EsOutDiscontinuity( es_out_t *out, bool b_flush, bool b_audio )
352 {
353     es_out_sys_t      *p_sys = out->p_sys;
354     int i;
355
356     for( i = 0; i < p_sys->i_es; i++ )
357     {
358         es_out_id_t *es = p_sys->es[i];
359
360         /* Send a dummy block to let decoder know that
361          * there is a discontinuity */
362         if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )
363         {
364             input_DecoderDiscontinuity( es->p_dec, b_flush );
365             if( es->p_dec_record )
366                 input_DecoderDiscontinuity( es->p_dec_record, b_flush );
367         }
368     }
369 }
370 void input_EsOutChangeRate( es_out_t *out, int i_rate )
371 {
372     es_out_sys_t      *p_sys = out->p_sys;
373     int i;
374
375     p_sys->i_rate = i_rate;
376     EsOutDiscontinuity( out, false, false );
377
378     for( i = 0; i < p_sys->i_pgrm; i++ )
379         input_ClockSetRate( &p_sys->pgrm[i]->clock, i_rate );
380 }
381
382 int input_EsOutSetRecord(  es_out_t *out, bool b_record )
383 {
384     es_out_sys_t   *p_sys = out->p_sys;
385     input_thread_t *p_input = p_sys->p_input;
386
387     assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
388
389     if( b_record )
390     {
391         char *psz_path = var_CreateGetString( p_input, "input-record-path" );
392         if( !psz_path || *psz_path == '\0' )
393         {
394             free( psz_path );
395             psz_path = strdup( config_GetHomeDir() );
396         }
397
398         char *psz_sout = NULL;  // TODO conf
399
400         if( !psz_sout && psz_path )
401         {
402             char *psz_file = input_CreateFilename( VLC_OBJECT(p_input), psz_path, INPUT_RECORD_PREFIX, NULL );
403             if( psz_file )
404             {
405                 if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file ) < 0 )
406                     psz_sout = NULL;
407                 free( psz_file );
408             }
409         }
410         free( psz_path );
411
412         if( !psz_sout )
413             return VLC_EGENERIC;
414
415 #ifdef ENABLE_SOUT
416         p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
417 #endif
418         free( psz_sout );
419
420         if( !p_sys->p_sout_record )
421             return VLC_EGENERIC;
422
423         for( int i = 0; i < p_sys->i_es; i++ )
424         {
425             es_out_id_t *p_es = p_sys->es[i];
426
427             if( !p_es->p_dec || p_es->p_master )
428                 continue;
429
430             p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_sys->p_sout_record );
431         }
432     }
433     else
434     {
435         for( int i = 0; i < p_sys->i_es; i++ )
436         {
437             es_out_id_t *p_es = p_sys->es[i];
438
439             if( !p_es->p_dec_record )
440                 continue;
441
442             input_DecoderDelete( p_es->p_dec_record );
443             p_es->p_dec_record = NULL;
444         }
445 #ifdef ENABLE_SOUT
446         sout_DeleteInstance( p_sys->p_sout_record );
447 #endif
448         p_sys->p_sout_record = NULL;
449     }
450
451     return VLC_SUCCESS;
452 }
453 void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
454 {
455     es_out_sys_t *p_sys = out->p_sys;
456
457     if( i_cat == AUDIO_ES )
458         p_sys->i_audio_delay = i_delay;
459     else if( i_cat == SPU_ES )
460         p_sys->i_spu_delay = i_delay;
461 }
462 void input_EsOutChangeState( es_out_t *out )
463 {
464     es_out_sys_t *p_sys = out->p_sys;
465     input_thread_t *p_input = p_sys->p_input;
466
467     if( p_input->i_state  == PAUSE_S )
468     {
469         /* Send discontinuity to decoders (it will allow them to flush
470          *                  * if implemented */
471         EsOutDiscontinuity( out, false, false );
472     }
473     else
474     {
475         /* Out of pause, reset pcr */
476         es_out_Control( out, ES_OUT_RESET_PCR );
477     }
478 }
479 void input_EsOutChangePosition( es_out_t *out )
480 {
481     //es_out_sys_t *p_sys = out->p_sys;
482
483     es_out_Control( out, ES_OUT_RESET_PCR );
484     EsOutDiscontinuity( out, true, false );
485 }
486
487 bool input_EsOutDecodersEmpty( es_out_t *out )
488 {
489     es_out_sys_t      *p_sys = out->p_sys;
490     int i;
491
492     for( i = 0; i < p_sys->i_es; i++ )
493     {
494         es_out_id_t *es = p_sys->es[i];
495
496         if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )
497             return false;
498         if( es->p_dec_record && !input_DecoderEmpty( es->p_dec_record ) )
499             return false;
500     }
501     return true;
502 }
503
504 /*****************************************************************************
505  *
506  *****************************************************************************/
507 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
508                                      bool b_delete )
509 {
510     es_out_sys_t      *p_sys = out->p_sys;
511     input_thread_t    *p_input = p_sys->p_input;
512     const  bool b_teletext = fmt->i_cat == SPU_ES && fmt->i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' );
513     vlc_value_t       val, text;
514
515     const char *psz_var;
516
517     if( fmt->i_cat == AUDIO_ES )
518         psz_var = "audio-es";
519     else if( fmt->i_cat == VIDEO_ES )
520         psz_var = "video-es";
521     else if( fmt->i_cat == SPU_ES )
522         psz_var = "spu-es";
523     else
524         return;
525
526     if( b_delete )
527     {
528         if( b_teletext )
529             var_SetInteger( p_sys->p_input, "teletext-es", -1 );
530
531         val.i_int = i_id;
532         var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
533
534         var_SetBool( p_sys->p_input, "intf-change", true );
535         return;
536     }
537
538     /* Get the number of ES already added */
539     var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
540     if( val.i_int == 0 )
541     {
542         vlc_value_t val2;
543
544         /* First one, we need to add the "Disable" choice */
545         val2.i_int = -1; text.psz_string = _("Disable");
546         var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
547         val.i_int++;
548     }
549
550     /* Take care of the ES description */
551     if( fmt->psz_description && *fmt->psz_description )
552     {
553         if( psz_language && *psz_language )
554         {
555             text.psz_string = malloc( strlen( fmt->psz_description) +
556                                       strlen( psz_language ) + 10 );
557             sprintf( text.psz_string, "%s - [%s]", fmt->psz_description,
558                                                    psz_language );
559         }
560         else text.psz_string = strdup( fmt->psz_description );
561     }
562     else
563     {
564         if( psz_language && *psz_language )
565         {
566             if( asprintf( &text.psz_string, "%s %i - [%s]", _( "Track" ), val.i_int, psz_language ) == -1 )
567                 text.psz_string = NULL;
568         }
569         else
570         {
571             if( asprintf( &text.psz_string, "%s %i", _( "Track" ), val.i_int ) == -1 )
572                 text.psz_string = NULL;
573         }
574     }
575
576     val.i_int = i_id;
577     var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
578
579     free( text.psz_string );
580
581     if( b_teletext )
582         var_SetInteger( p_sys->p_input, "teletext-es", i_id );
583
584     var_SetBool( p_sys->p_input, "intf-change", true );
585 }
586
587 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
588                               bool b_delete )
589 {
590     EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete );
591 }
592
593 /* EsOutProgramSelect:
594  *  Select a program and update the object variable
595  */
596 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
597 {
598     es_out_sys_t      *p_sys = out->p_sys;
599     input_thread_t    *p_input = p_sys->p_input;
600     vlc_value_t       val;
601     int               i;
602
603     if( p_sys->p_pgrm == p_pgrm )
604         return; /* Nothing to do */
605
606     if( p_sys->p_pgrm )
607     {
608         es_out_pgrm_t *old = p_sys->p_pgrm;
609         msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
610
611         for( i = 0; i < p_sys->i_es; i++ )
612         {
613             if( p_sys->es[i]->p_pgrm == old && EsIsSelected( p_sys->es[i] ) &&
614                 p_sys->i_mode != ES_OUT_MODE_ALL )
615                 EsUnselect( out, p_sys->es[i], true );
616         }
617
618         p_sys->p_es_audio = NULL;
619         p_sys->p_es_sub = NULL;
620         p_sys->p_es_video = NULL;
621     }
622
623     msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
624
625     /* Mark it selected */
626     p_pgrm->b_selected = true;
627
628     /* Switch master stream */
629     if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )
630     {
631         p_sys->p_pgrm->clock.b_master = false;
632     }
633     p_pgrm->clock.b_master = true;
634     p_sys->p_pgrm = p_pgrm;
635
636     /* Update "program" */
637     val.i_int = p_pgrm->i_id;
638     var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
639
640     /* Update "es-*" */
641     var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
642     var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
643     var_Change( p_input, "spu-es",   VLC_VAR_CLEARCHOICES, NULL, NULL );
644     var_SetInteger( p_input, "teletext-es", -1 );
645     for( i = 0; i < p_sys->i_es; i++ )
646     {
647         if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
648             EsOutESVarUpdate( out, p_sys->es[i], false );
649         EsOutSelect( out, p_sys->es[i], false );
650     }
651
652     /* Update now playing */
653     input_item_SetNowPlaying( p_input->p->input.p_item,
654                               p_pgrm->psz_now_playing );
655     input_item_SetPublisher( p_input->p->input.p_item,
656                              p_pgrm->psz_publisher );
657
658     var_SetBool( p_sys->p_input, "intf-change", true );
659 }
660
661 /* EsOutAddProgram:
662  *  Add a program
663  */
664 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
665 {
666     es_out_sys_t      *p_sys = out->p_sys;
667     input_thread_t    *p_input = p_sys->p_input;
668     vlc_value_t       val;
669
670     es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
671     if( !p_pgrm ) return NULL;
672
673     /* Init */
674     p_pgrm->i_id = i_group;
675     p_pgrm->i_es = 0;
676     p_pgrm->b_selected = false;
677     p_pgrm->psz_name = NULL;
678     p_pgrm->psz_now_playing = NULL;
679     p_pgrm->psz_publisher = NULL;
680     p_pgrm->p_epg = NULL;
681     input_ClockInit( &p_pgrm->clock, false, p_input->p->input.i_cr_average, p_sys->i_rate );
682
683     /* Append it */
684     TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
685
686     /* Update "program" variable */
687     val.i_int = i_group;
688     var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
689
690     if( i_group == var_GetInteger( p_input, "program" ) )
691     {
692         EsOutProgramSelect( out, p_pgrm );
693     }
694     else
695     {
696         var_SetBool( p_sys->p_input, "intf-change", true );
697     }
698     return p_pgrm;
699 }
700
701 /* EsOutDelProgram:
702  *  Delete a program
703  */
704 static int EsOutProgramDel( es_out_t *out, int i_group )
705 {
706     es_out_sys_t      *p_sys = out->p_sys;
707     input_thread_t    *p_input = p_sys->p_input;
708     es_out_pgrm_t     *p_pgrm = NULL;
709     vlc_value_t       val;
710     int               i;
711
712     for( i = 0; i < p_sys->i_pgrm; i++ )
713     {
714         if( p_sys->pgrm[i]->i_id == i_group )
715         {
716             p_pgrm = p_sys->pgrm[i];
717             break;
718         }
719     }
720
721     if( p_pgrm == NULL )
722         return VLC_EGENERIC;
723
724     if( p_pgrm->i_es )
725     {
726         msg_Dbg( p_input, "can't delete program %d which still has %i ES",
727                  i_group, p_pgrm->i_es );
728         return VLC_EGENERIC;
729     }
730
731     TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
732
733     /* If program is selected we need to unselect it */
734     if( p_sys->p_pgrm == p_pgrm ) p_sys->p_pgrm = NULL;
735
736     free( p_pgrm->psz_name );
737     free( p_pgrm->psz_now_playing );
738     free( p_pgrm->psz_publisher );
739     if( p_pgrm->p_epg )
740         vlc_epg_Delete( p_pgrm->p_epg );
741     free( p_pgrm );
742
743     /* Update "program" variable */
744     val.i_int = i_group;
745     var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
746
747     var_SetBool( p_sys->p_input, "intf-change", true );
748
749     return VLC_SUCCESS;
750 }
751
752 /* EsOutProgramMeta:
753  */
754 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
755 {
756     char *psz = NULL;
757     if( p_pgrm->psz_name )
758     {
759         if( asprintf( &psz, _("%s [%s %d]"), p_pgrm->psz_name, _("Program"), p_pgrm->i_id ) == -1 )
760             return NULL;
761     }
762     else
763     {
764         if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
765             return NULL;
766     }
767     return psz;
768 }
769
770 static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
771 {
772     es_out_sys_t      *p_sys = out->p_sys;
773     es_out_pgrm_t     *p_pgrm = NULL;
774     input_thread_t    *p_input = p_sys->p_input;
775     char              *psz_cat;
776     const char        *psz_title = NULL;
777     const char        *psz_provider = NULL;
778     int i;
779
780     msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
781
782     /* Check against empty meta data (empty for what we handle) */
783     if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
784         !vlc_meta_Get( p_meta, vlc_meta_NowPlaying) &&
785         !vlc_meta_Get( p_meta, vlc_meta_Publisher) &&
786         vlc_dictionary_keys_count( &p_meta->extra_tags ) <= 0 )
787     {
788         return;
789     }
790     /* Find program */
791     for( i = 0; i < p_sys->i_pgrm; i++ )
792     {
793         if( p_sys->pgrm[i]->i_id == i_group )
794         {
795             p_pgrm = p_sys->pgrm[i];
796             break;
797         }
798     }
799     if( p_pgrm == NULL )
800         p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
801
802     /* */
803     psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
804     psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
805
806     /* Update the description text of the program */
807     if( psz_title && *psz_title )
808     {
809         vlc_value_t val;
810         vlc_value_t text;
811
812         if( !p_pgrm->psz_name || strcmp( p_pgrm->psz_name, psz_title ) )
813         {
814             char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
815
816             /* Remove old entries */
817             input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL );
818             /* TODO update epg name */
819             free( psz_cat );
820         }
821         free( p_pgrm->psz_name );
822         p_pgrm->psz_name = strdup( psz_title );
823
824         /* ugly but it works */
825         val.i_int = i_group;
826         var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
827
828         if( psz_provider && *psz_provider )
829         {
830             if( asprintf( &text.psz_string, "%s [%s]", psz_title, psz_provider ) != -1 )
831             {
832                 var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
833                 free( text.psz_string );
834             }
835         }
836         else
837         {
838             text.psz_string = (char *)psz_title;
839             var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
840         }
841     }
842
843     psz_cat = EsOutProgramGetMetaName( p_pgrm );
844     if( psz_provider )
845     {
846         if( p_sys->p_pgrm == p_pgrm )
847             input_item_SetPublisher( p_input->p->input.p_item, psz_provider );
848         input_Control( p_input, INPUT_ADD_INFO, psz_cat, input_MetaTypeToLocalizedString(vlc_meta_Publisher), psz_provider );
849     }
850     char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags );
851     for( i = 0; ppsz_all_keys[i]; i++ )
852     {
853         input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(ppsz_all_keys[i]),
854                        vlc_dictionary_value_for_key( &p_meta->extra_tags, ppsz_all_keys[i] ) );
855         free( ppsz_all_keys[i] );
856     }
857     free( ppsz_all_keys );
858
859     free( psz_cat );
860 }
861
862 static void vlc_epg_Merge( vlc_epg_t *p_dst, const vlc_epg_t *p_src )
863 {
864     int i;
865
866     /* Add new event */
867     for( i = 0; i < p_src->i_event; i++ )
868     {
869         vlc_epg_event_t *p_evt = p_src->pp_event[i];
870         bool b_add = true;
871         int j;
872
873         for( j = 0; j < p_dst->i_event; j++ )
874         {
875             if( p_dst->pp_event[j]->i_start == p_evt->i_start && p_dst->pp_event[j]->i_duration == p_evt->i_duration )
876             {
877                 b_add = false;
878                 break;
879             }
880             if( p_dst->pp_event[j]->i_start > p_evt->i_start )
881                 break;
882         }
883         if( b_add )
884         {
885             vlc_epg_event_t *p_copy = malloc( sizeof(vlc_epg_event_t) );
886             if( !p_copy )
887                 break;
888             memset( p_copy, 0, sizeof(vlc_epg_event_t) );
889             p_copy->i_start = p_evt->i_start;
890             p_copy->i_duration = p_evt->i_duration;
891             p_copy->psz_name = p_evt->psz_name ? strdup( p_evt->psz_name ) : NULL;
892             p_copy->psz_short_description = p_evt->psz_short_description ? strdup( p_evt->psz_short_description ) : NULL;
893             p_copy->psz_description = p_evt->psz_description ? strdup( p_evt->psz_description ) : NULL;
894             TAB_INSERT( p_dst->i_event, p_dst->pp_event, p_copy, j );
895         }
896     }
897     /* Update current */
898     vlc_epg_SetCurrent( p_dst, p_src->p_current ? p_src->p_current->i_start : -1 );
899
900     /* Keep only 1 old event  */
901     if( p_dst->p_current )
902     {
903         while( p_dst->i_event > 1 && p_dst->pp_event[0] != p_dst->p_current && p_dst->pp_event[1] != p_dst->p_current )
904             TAB_REMOVE( p_dst->i_event, p_dst->pp_event, p_dst->pp_event[0] );
905     }
906 }
907
908 static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg )
909 {
910     es_out_sys_t      *p_sys = out->p_sys;
911     input_thread_t    *p_input = p_sys->p_input;
912     es_out_pgrm_t     *p_pgrm = NULL;
913     char *psz_cat;
914     int i;
915
916     /* Find program */
917     for( i = 0; i < p_sys->i_pgrm; i++ )
918     {
919         if( p_sys->pgrm[i]->i_id == i_group )
920         {
921             p_pgrm = p_sys->pgrm[i];
922             break;
923         }
924     }
925     if( p_pgrm == NULL )
926         p_pgrm = EsOutProgramAdd( out, i_group );   /* Create it */
927
928     /* Merge EPG */
929     if( !p_pgrm->p_epg )
930         p_pgrm->p_epg = vlc_epg_New( p_pgrm->psz_name );
931     vlc_epg_Merge( p_pgrm->p_epg, p_epg );
932
933     /* Update info */
934     psz_cat = EsOutProgramGetMetaName( p_pgrm );
935 #ifdef HAVE_LOCALTIME_R
936     char *psz_epg;
937     if( asprintf( &psz_epg, "EPG %s", psz_cat ) == -1 )
938         psz_epg = NULL;
939     input_Control( p_input, INPUT_DEL_INFO, psz_epg, NULL );
940     msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, p_pgrm->p_epg->psz_name );
941     for( i = 0; i < p_pgrm->p_epg->i_event; i++ )
942     {
943         const vlc_epg_event_t *p_evt = p_pgrm->p_epg->pp_event[i];
944         time_t t_start = (time_t)p_evt->i_start;
945         struct tm tm_start;
946         char psz_start[128];
947
948         localtime_r( &t_start, &tm_start );
949
950         snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d:%2.2d", tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
951         if( p_evt->psz_short_description || p_evt->psz_description )
952             input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s",
953                            p_evt->psz_name,
954                            p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
955                            p_evt->psz_short_description ? p_evt->psz_short_description : p_evt->psz_description );
956         else
957             input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
958                            p_evt->psz_name,
959                            p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
960     }
961     free( psz_epg );
962 #endif
963     /* Update now playing */
964     free( p_pgrm->psz_now_playing );
965     p_pgrm->psz_now_playing = NULL;
966     if( p_epg->p_current && p_epg->p_current->psz_name && *p_epg->p_current->psz_name )
967         p_pgrm->psz_now_playing = strdup( p_epg->p_current->psz_name );
968
969     if( p_pgrm == p_sys->p_pgrm )
970         input_item_SetNowPlaying( p_input->p->input.p_item, p_pgrm->psz_now_playing );
971
972     if( p_pgrm->psz_now_playing )
973     {
974         input_Control( p_input, INPUT_ADD_INFO, psz_cat,
975             input_MetaType