es_out: rename p_clock to p_input_clock
[vlc.git] / src / input / es_out.c
blobe0c0594a278ca77dd45d73368aea54995439886b
1 /*****************************************************************************
2 * es_out.c: Es Out handler for input.
3 *****************************************************************************
4 * Copyright (C) 2003-2004 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <stdio.h>
33 #include <assert.h>
34 #include <vlc_common.h>
36 #include <vlc_input.h>
37 #include <vlc_es_out.h>
38 #include <vlc_block.h>
39 #include <vlc_aout.h>
40 #include <vlc_fourcc.h>
41 #include <vlc_meta.h>
43 #include "input_internal.h"
44 #include "../clock/input_clock.h"
45 #include "decoder.h"
46 #include "es_out.h"
47 #include "event.h"
48 #include "info.h"
49 #include "item.h"
51 #include "../stream_output/stream_output.h"
53 #include <vlc_iso_lang.h>
54 /* FIXME we should find a better way than including that */
55 #include "../text/iso-639_def.h"
57 /*****************************************************************************
58 * Local prototypes
59 *****************************************************************************/
60 typedef struct
62 /* Program ID */
63 int i_id;
65 /* Number of es for this pgrm */
66 int i_es;
68 bool b_selected;
69 bool b_scrambled;
71 /* Clock for this program */
72 input_clock_t *p_input_clock;
74 vlc_meta_t *p_meta;
75 } es_out_pgrm_t;
77 struct es_out_id_t
79 /* ES ID */
80 int i_id;
81 es_out_pgrm_t *p_pgrm;
83 /* */
84 bool b_scrambled;
86 /* Channel in the track type */
87 int i_channel;
88 es_format_t fmt;
89 char *psz_language;
90 char *psz_language_code;
92 decoder_t *p_dec;
93 decoder_t *p_dec_record;
95 /* Fields for Video with CC */
96 struct
98 vlc_fourcc_t type;
99 uint64_t i_bitmap; /* channels bitmap */
100 es_out_id_t *pp_es[64]; /* a max of 64 chans for CEA708 */
101 } cc;
103 /* Field for CC track from a master video */
104 es_out_id_t *p_master;
106 /* ID for the meta data */
107 int i_meta_id;
110 typedef struct
112 int i_count; /* es count */
113 es_out_id_t *p_main_es; /* current main es */
114 enum es_out_policy_e e_policy;
116 /* Parameters used for es selection */
117 bool b_autoselect; /* if we want to select an es when no user prefs */
118 int i_id; /* es id as set by es fmt.id */
119 int i_demux_id; /* same as previous, demuxer set default value */
120 int i_channel; /* es number in creation order */
121 char **ppsz_language;
122 } es_out_es_props_t;
124 typedef struct
126 input_thread_t *p_input;
128 /* */
129 vlc_mutex_t lock;
131 /* all programs */
132 int i_pgrm;
133 es_out_pgrm_t **pgrm;
134 es_out_pgrm_t *p_pgrm; /* Master program */
136 /* all es */
137 int i_id;
138 int i_es;
139 es_out_id_t **es;
141 /* mode gestion */
142 bool b_active;
143 int i_mode;
145 es_out_es_props_t video, audio, sub;
147 /* es/group to select */
148 int i_group_id;
150 /* delay */
151 int64_t i_audio_delay;
152 int64_t i_spu_delay;
154 /* Clock configuration */
155 mtime_t i_pts_delay;
156 mtime_t i_pts_jitter;
157 int i_cr_average;
158 int i_rate;
160 /* */
161 bool b_paused;
162 mtime_t i_pause_date;
164 /* Current preroll */
165 mtime_t i_preroll_end;
167 /* Used for buffering */
168 bool b_buffering;
169 mtime_t i_buffering_extra_initial;
170 mtime_t i_buffering_extra_stream;
171 mtime_t i_buffering_extra_system;
173 /* Record */
174 sout_instance_t *p_sout_record;
176 /* Used only to limit debugging output */
177 int i_prev_stream_level;
178 } es_out_sys_t;
180 static es_out_id_t *EsOutAdd ( es_out_t *, const es_format_t * );
181 static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * );
182 static void EsOutDel ( es_out_t *, es_out_id_t * );
183 static int EsOutControl( es_out_t *, int i_query, va_list );
184 static void EsOutDelete ( es_out_t * );
186 static void EsOutTerminate( es_out_t * );
187 static void EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force );
188 static void EsOutUpdateInfo( es_out_t *, es_out_id_t *es, const es_format_t *, const vlc_meta_t * );
189 static int EsOutSetRecord( es_out_t *, bool b_record );
191 static bool EsIsSelected( es_out_id_t *es );
192 static void EsSelect( es_out_t *out, es_out_id_t *es );
193 static void EsDeleteInfo( es_out_t *, es_out_id_t *es );
194 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
195 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
196 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
197 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
198 static void EsOutProgramsChangeRate( es_out_t *out );
199 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
200 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta );
201 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_progmeta );
203 static char *LanguageGetName( const char *psz_code );
204 static char *LanguageGetCode( const char *psz_lang );
205 static char **LanguageSplit( const char *psz_langs );
206 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang );
208 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
209 static char *EsInfoCategoryName( es_out_id_t* es );
211 static inline int EsOutGetClosedCaptionsChannel( const es_format_t *p_fmt )
213 int i_channel;
214 if( p_fmt->i_codec == VLC_CODEC_CEA608 && p_fmt->subs.cc.i_channel < 4 )
215 i_channel = p_fmt->subs.cc.i_channel;
216 else if( p_fmt->i_codec == VLC_CODEC_CEA708 && p_fmt->subs.cc.i_channel < 64 )
217 i_channel = p_fmt->subs.cc.i_channel;
218 else
219 i_channel = -1;
220 return i_channel;
222 static inline bool EsFmtIsTeletext( const es_format_t *p_fmt )
224 return p_fmt->i_cat == SPU_ES && p_fmt->i_codec == VLC_CODEC_TELETEXT;
227 /*****************************************************************************
228 * Es category specific structs
229 *****************************************************************************/
230 static es_out_es_props_t * GetPropsByCat( es_out_sys_t *p_sys, int i_cat )
232 switch( i_cat )
234 case AUDIO_ES:
235 return &p_sys->audio;
236 case SPU_ES:
237 return &p_sys->sub;
238 case VIDEO_ES:
239 return &p_sys->video;
241 return NULL;
244 static void EsOutPropsCleanup( es_out_es_props_t *p_props )
246 if( p_props->ppsz_language )
248 for( int i = 0; p_props->ppsz_language[i]; i++ )
249 free( p_props->ppsz_language[i] );
250 free( p_props->ppsz_language );
254 static void EsOutPropsInit( es_out_es_props_t *p_props,
255 bool autoselect,
256 input_thread_t *p_input,
257 enum es_out_policy_e e_default_policy,
258 const char *psz_trackidvar,
259 const char *psz_trackvar,
260 const char *psz_langvar,
261 const char *psz_debug )
263 p_props->e_policy = e_default_policy;
264 p_props->i_count = 0;
265 p_props->b_autoselect = autoselect;
266 p_props->i_id = (psz_trackidvar) ? var_GetInteger( p_input, psz_trackidvar ): -1;
267 p_props->i_channel = (psz_trackvar) ? var_GetInteger( p_input, psz_trackvar ): -1;
268 p_props->i_demux_id = -1;
269 p_props->p_main_es = NULL;
271 if( !input_priv(p_input)->b_preparsing && psz_langvar )
273 char *psz_string = var_GetString( p_input, psz_langvar );
274 p_props->ppsz_language = LanguageSplit( psz_string );
275 if( p_props->ppsz_language )
277 for( int i = 0; p_props->ppsz_language[i]; i++ )
278 msg_Dbg( p_input, "selected %s language[%d] %s",
279 psz_debug, i, p_props->ppsz_language[i] );
281 free( psz_string );
285 /*****************************************************************************
286 * input_EsOutNew:
287 *****************************************************************************/
288 es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
290 es_out_t *out = malloc( sizeof( *out ) );
291 if( !out )
292 return NULL;
294 es_out_sys_t *p_sys = calloc( 1, sizeof( *p_sys ) );
295 if( !p_sys )
297 free( out );
298 return NULL;
301 out->pf_add = EsOutAdd;
302 out->pf_send = EsOutSend;
303 out->pf_del = EsOutDel;
304 out->pf_control = EsOutControl;
305 out->pf_destroy = EsOutDelete;
306 out->p_sys = p_sys;
308 vlc_mutex_init_recursive( &p_sys->lock );
309 p_sys->p_input = p_input;
311 p_sys->b_active = false;
312 p_sys->i_mode = ES_OUT_MODE_NONE;
314 TAB_INIT( p_sys->i_pgrm, p_sys->pgrm );
316 TAB_INIT( p_sys->i_es, p_sys->es );
318 /* */
319 EsOutPropsInit( &p_sys->video, true, p_input, ES_OUT_ES_POLICY_SIMULTANEOUS,
320 NULL, NULL, NULL, NULL );
321 EsOutPropsInit( &p_sys->audio, true, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,
322 "audio-track-id", "audio-track", "audio-language", "audio" );
323 EsOutPropsInit( &p_sys->sub, false, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,
324 "sub-track-id", "sub-track", "sub-language", "sub" );
326 p_sys->i_group_id = var_GetInteger( p_input, "program" );
328 p_sys->i_pause_date = -1;
330 p_sys->i_rate = i_rate;
332 p_sys->b_buffering = true;
333 p_sys->i_preroll_end = -1;
334 p_sys->i_prev_stream_level = -1;
336 return out;
339 /*****************************************************************************
341 *****************************************************************************/
342 static void EsOutDelete( es_out_t *out )
344 es_out_sys_t *p_sys = out->p_sys;
346 assert( !p_sys->i_es && !p_sys->i_pgrm && !p_sys->p_pgrm );
347 EsOutPropsCleanup( &p_sys->audio );
348 EsOutPropsCleanup( &p_sys->sub );
350 vlc_mutex_destroy( &p_sys->lock );
352 free( p_sys );
353 free( out );
356 static void EsOutTerminate( es_out_t *out )
358 es_out_sys_t *p_sys = out->p_sys;
360 if( p_sys->p_sout_record )
361 EsOutSetRecord( out, false );
363 for( int i = 0; i < p_sys->i_es; i++ )
365 if( p_sys->es[i]->p_dec )
366 input_DecoderDelete( p_sys->es[i]->p_dec );
368 free( p_sys->es[i]->psz_language );
369 free( p_sys->es[i]->psz_language_code );
370 es_format_Clean( &p_sys->es[i]->fmt );
372 free( p_sys->es[i] );
374 TAB_CLEAN( p_sys->i_es, p_sys->es );
376 /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
377 for( int i = 0; i < p_sys->i_pgrm; i++ )
379 es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
380 input_clock_Delete( p_pgrm->p_input_clock );
381 if( p_pgrm->p_meta )
382 vlc_meta_Delete( p_pgrm->p_meta );
384 free( p_pgrm );
386 TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
388 p_sys->p_pgrm = NULL;
390 input_item_SetEpgOffline( input_priv(p_sys->p_input)->p_item );
391 input_SendEventMetaEpg( p_sys->p_input );
394 static mtime_t EsOutGetWakeup( es_out_t *out )
396 es_out_sys_t *p_sys = out->p_sys;
397 input_thread_t *p_input = p_sys->p_input;
399 if( !p_sys->p_pgrm )
400 return 0;
402 /* We do not have a wake up date if the input cannot have its speed
403 * controlled or sout is imposing its own or while buffering
405 * FIXME for !input_priv(p_input)->b_can_pace_control a wake-up time is still needed
406 * to avoid too heavy buffering */
407 if( !input_priv(p_input)->b_can_pace_control ||
408 input_priv(p_input)->b_out_pace_control ||
409 p_sys->b_buffering )
410 return 0;
412 return input_clock_GetWakeup( p_sys->p_pgrm->p_input_clock );
415 static es_out_id_t es_cat[DATA_ES];
417 static es_out_id_t *EsOutGetFromID( es_out_t *out, int i_id )
419 if( i_id < 0 )
421 /* Special HACK, -i_id is the cat of the stream */
422 return es_cat - i_id;
425 es_out_sys_t *p_sys = out->p_sys;
426 for( int i = 0; i < p_sys->i_es; i++ )
428 if( p_sys->es[i]->i_id == i_id )
429 return p_sys->es[i];
431 return NULL;
434 static bool EsOutDecodersIsEmpty( es_out_t *out )
436 es_out_sys_t *p_sys = out->p_sys;
438 if( p_sys->b_buffering && p_sys->p_pgrm )
440 EsOutDecodersStopBuffering( out, true );
441 if( p_sys->b_buffering )
442 return true;
445 for( int i = 0; i < p_sys->i_es; i++ )
447 es_out_id_t *es = p_sys->es[i];
449 if( es->p_dec && !input_DecoderIsEmpty( es->p_dec ) )
450 return false;
451 if( es->p_dec_record && !input_DecoderIsEmpty( es->p_dec_record ) )
452 return false;
454 return true;
457 static void EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
459 es_out_sys_t *p_sys = out->p_sys;
461 if( i_cat == AUDIO_ES )
462 p_sys->i_audio_delay = i_delay;
463 else if( i_cat == SPU_ES )
464 p_sys->i_spu_delay = i_delay;
466 for( int i = 0; i < p_sys->i_es; i++ )
467 EsOutDecoderChangeDelay( out, p_sys->es[i] );
470 static int EsOutSetRecord( es_out_t *out, bool b_record )
472 es_out_sys_t *p_sys = out->p_sys;
473 input_thread_t *p_input = p_sys->p_input;
475 assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
477 if( b_record )
479 char *psz_path = var_CreateGetNonEmptyString( p_input, "input-record-path" );
480 if( !psz_path )
482 if( var_CountChoices( p_input, "video-es" ) )
483 psz_path = config_GetUserDir( VLC_VIDEOS_DIR );
484 else if( var_CountChoices( p_input, "audio-es" ) )
485 psz_path = config_GetUserDir( VLC_MUSIC_DIR );
486 else
487 psz_path = config_GetUserDir( VLC_DOWNLOAD_DIR );
490 char *psz_sout = NULL; // TODO conf
492 if( !psz_sout && psz_path )
494 char *psz_file = input_CreateFilename( p_input, psz_path, INPUT_RECORD_PREFIX, NULL );
495 if( psz_file )
497 if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file ) < 0 )
498 psz_sout = NULL;
499 free( psz_file );
502 free( psz_path );
504 if( !psz_sout )
505 return VLC_EGENERIC;
507 #ifdef ENABLE_SOUT
508 p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
509 #endif
510 free( psz_sout );
512 if( !p_sys->p_sout_record )
513 return VLC_EGENERIC;
515 for( int i = 0; i < p_sys->i_es; i++ )
517 es_out_id_t *p_es = p_sys->es[i];
519 if( !p_es->p_dec || p_es->p_master )
520 continue;
522 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_input_clock, p_sys->p_sout_record );
523 if( p_es->p_dec_record && p_sys->b_buffering )
524 input_DecoderStartWait( p_es->p_dec_record );
527 else
529 for( int i = 0; i < p_sys->i_es; i++ )
531 es_out_id_t *p_es = p_sys->es[i];
533 if( !p_es->p_dec_record )
534 continue;
536 input_DecoderDelete( p_es->p_dec_record );
537 p_es->p_dec_record = NULL;
539 #ifdef ENABLE_SOUT
540 sout_DeleteInstance( p_sys->p_sout_record );
541 #endif
542 p_sys->p_sout_record = NULL;
545 return VLC_SUCCESS;
547 static void EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
549 es_out_sys_t *p_sys = out->p_sys;
551 /* XXX the order is important */
552 if( b_paused )
554 EsOutDecodersChangePause( out, true, i_date );
555 EsOutProgramChangePause( out, true, i_date );
557 else
559 if( p_sys->i_buffering_extra_initial > 0 )
561 mtime_t i_stream_start;
562 mtime_t i_system_start;
563 mtime_t i_stream_duration;
564 mtime_t i_system_duration;
565 int i_ret;
566 i_ret = input_clock_GetState( p_sys->p_pgrm->p_input_clock,
567 &i_stream_start, &i_system_start,
568 &i_stream_duration, &i_system_duration );
569 if( !i_ret )
571 /* FIXME pcr != exactly what wanted */
572 const mtime_t i_used = /*(i_stream_duration - input_priv(p_sys->p_input)->i_pts_delay)*/ p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
573 i_date -= i_used;
575 p_sys->i_buffering_extra_initial = 0;
576 p_sys->i_buffering_extra_stream = 0;
577 p_sys->i_buffering_extra_system = 0;
579 EsOutProgramChangePause( out, false, i_date );
580 EsOutDecodersChangePause( out, false, i_date );
582 EsOutProgramsChangeRate( out );
584 p_sys->b_paused = b_paused;
585 p_sys->i_pause_date = i_date;
588 static void EsOutChangeRate( es_out_t *out, int i_rate )
590 es_out_sys_t *p_sys = out->p_sys;
592 p_sys->i_rate = i_rate;
593 EsOutProgramsChangeRate( out );
596 static void EsOutChangePosition( es_out_t *out )
598 es_out_sys_t *p_sys = out->p_sys;
600 input_SendEventCache( p_sys->p_input, 0.0 );
602 for( int i = 0; i < p_sys->i_es; i++ )
604 es_out_id_t *p_es = p_sys->es[i];
606 if( p_es->p_dec != NULL )
608 input_DecoderFlush( p_es->p_dec );
609 if( !p_sys->b_buffering )
611 input_DecoderStartWait( p_es->p_dec );
612 if( p_es->p_dec_record != NULL )
613 input_DecoderStartWait( p_es->p_dec_record );
618 for( int i = 0; i < p_sys->i_pgrm; i++ )
619 input_clock_Reset( p_sys->pgrm[i]->p_input_clock );
621 p_sys->b_buffering = true;
622 p_sys->i_buffering_extra_initial = 0;
623 p_sys->i_buffering_extra_stream = 0;
624 p_sys->i_buffering_extra_system = 0;
625 p_sys->i_preroll_end = -1;
626 p_sys->i_prev_stream_level = -1;
631 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
633 es_out_sys_t *p_sys = out->p_sys;
635 mtime_t i_stream_start;
636 mtime_t i_system_start;
637 mtime_t i_stream_duration;
638 mtime_t i_system_duration;
639 if (input_clock_GetState( p_sys->p_pgrm->p_input_clock,
640 &i_stream_start, &i_system_start,
641 &i_stream_duration, &i_system_duration ))
642 return;
644 mtime_t i_preroll_duration = 0;
645 if( p_sys->i_preroll_end >= 0 )
646 i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
648 const mtime_t i_buffering_duration = p_sys->i_pts_delay +
649 i_preroll_duration +
650 p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
652 if( i_stream_duration <= i_buffering_duration && !b_forced )
654 double f_level;
655 if (i_buffering_duration == 0)
656 f_level = 0;
657 else
658 f_level = __MAX( (double)i_stream_duration / i_buffering_duration, 0 );
659 input_SendEventCache( p_sys->p_input, f_level );
661 int i_level = (int)(100 * f_level);
662 if( p_sys->i_prev_stream_level != i_level )
664 msg_Dbg( p_sys->p_input, "Buffering %d%%", i_level );
665 p_sys->i_prev_stream_level = i_level;
668 return;
670 input_SendEventCache( p_sys->p_input, 1.0 );
672 msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
673 (int)(i_stream_duration/1000), (int)(i_system_duration/1000) );
674 p_sys->b_buffering = false;
675 p_sys->i_preroll_end = -1;
676 p_sys->i_prev_stream_level = -1;
678 if( p_sys->i_buffering_extra_initial > 0 )
680 /* FIXME wrong ? */
681 return;
684 const mtime_t i_decoder_buffering_start = mdate();
685 for( int i = 0; i < p_sys->i_es; i++ )
687 es_out_id_t *p_es = p_sys->es[i];
689 if( !p_es->p_dec || p_es->fmt.i_cat == SPU_ES )
690 continue;
691 input_DecoderWait( p_es->p_dec );
692 if( p_es->p_dec_record )
693 input_DecoderWait( p_es->p_dec_record );
696 msg_Dbg( p_sys->p_input, "Decoder wait done in %d ms",
697 (int)(mdate() - i_decoder_buffering_start)/1000 );
699 /* Here is a good place to destroy unused vout with every demuxer */
700 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
702 /* */
703 const mtime_t i_wakeup_delay = 10*1000; /* FIXME CLEANUP thread wake up time*/
704 const mtime_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : mdate();
706 input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_input_clock, true,
707 i_current_date + i_wakeup_delay - i_buffering_duration );
709 for( int i = 0; i < p_sys->i_es; i++ )
711 es_out_id_t *p_es = p_sys->es[i];
713 if( !p_es->p_dec )
714 continue;
716 input_DecoderStopWait( p_es->p_dec );
717 if( p_es->p_dec_record )
718 input_DecoderStopWait( p_es->p_dec_record );
721 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
723 es_out_sys_t *p_sys = out->p_sys;
725 /* Pause decoders first */
726 for( int i = 0; i < p_sys->i_es; i++ )
728 es_out_id_t *es = p_sys->es[i];
730 if( es->p_dec )
732 input_DecoderChangePause( es->p_dec, b_paused, i_date );
733 if( es->p_dec_record )
734 input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
739 static bool EsOutIsExtraBufferingAllowed( es_out_t *out )
741 es_out_sys_t *p_sys = out->p_sys;
743 size_t i_size = 0;
744 for( int i = 0; i < p_sys->i_es; i++ )
746 es_out_id_t *p_es = p_sys->es[i];
748 if( p_es->p_dec )
749 i_size += input_DecoderGetFifoSize( p_es->p_dec );
750 if( p_es->p_dec_record )
751 i_size += input_DecoderGetFifoSize( p_es->p_dec_record );
753 //msg_Info( out, "----- EsOutIsExtraBufferingAllowed =% 5d KiB -- ", i_size / 1024 );
755 /* TODO maybe we want to be able to tune it ? */
756 #if defined(OPTIMIZE_MEMORY)
757 const size_t i_level_high = 512*1024; /* 0.5 MiB */
758 #else
759 const size_t i_level_high = 10*1024*1024; /* 10 MiB */
760 #endif
761 return i_size < i_level_high;
764 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
766 es_out_sys_t *p_sys = out->p_sys;
768 for( int i = 0; i < p_sys->i_pgrm; i++ )
769 input_clock_ChangePause( p_sys->pgrm[i]->p_input_clock, b_paused, i_date );
772 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
774 es_out_sys_t *p_sys = out->p_sys;
776 mtime_t i_delay = 0;
777 if( p_es->fmt.i_cat == AUDIO_ES )
778 i_delay = p_sys->i_audio_delay;
779 else if( p_es->fmt.i_cat == SPU_ES )
780 i_delay = p_sys->i_spu_delay;
781 else
782 return;
784 if( p_es->p_dec )
785 input_DecoderChangeDelay( p_es->p_dec, i_delay );
786 if( p_es->p_dec_record )
787 input_DecoderChangeDelay( p_es->p_dec_record, i_delay );
789 static void EsOutProgramsChangeRate( es_out_t *out )
791 es_out_sys_t *p_sys = out->p_sys;
793 for( int i = 0; i < p_sys->i_pgrm; i++ )
794 input_clock_ChangeRate( p_sys->pgrm[i]->p_input_clock, p_sys->i_rate );
797 static void EsOutFrameNext( es_out_t *out )
799 es_out_sys_t *p_sys = out->p_sys;
800 es_out_id_t *p_es_video = NULL;
802 if( p_sys->b_buffering )
804 msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" );
805 return;
808 assert( p_sys->b_paused );
810 for( int i = 0; i < p_sys->i_es; i++ )
812 es_out_id_t *p_es = p_sys->es[i];
814 if( p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
816 p_es_video = p_es;
817 break;
821 if( !p_es_video )
823 msg_Warn( p_sys->p_input, "No video track selected, ignoring 'frame next'" );
824 return;
827 mtime_t i_duration;
828 input_DecoderFrameNext( p_es_video->p_dec, &i_duration );
830 msg_Dbg( p_sys->p_input, "EsOutFrameNext consummed %d ms", (int)(i_duration/1000) );
832 if( i_duration <= 0 )
833 i_duration = 40*1000;
835 /* FIXME it is not a clean way ? */
836 if( p_sys->i_buffering_extra_initial <= 0 )
838 mtime_t i_stream_start;
839 mtime_t i_system_start;
840 mtime_t i_stream_duration;
841 mtime_t i_system_duration;
842 int i_ret;
844 i_ret = input_clock_GetState( p_sys->p_pgrm->p_input_clock,
845 &i_stream_start, &i_system_start,
846 &i_stream_duration, &i_system_duration );
847 if( i_ret )
848 return;
850 p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->i_pts_delay; /* FIXME < 0 ? */
851 p_sys->i_buffering_extra_system =
852 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
855 const int i_rate = input_clock_GetRate( p_sys->p_pgrm->p_input_clock );
857 p_sys->b_buffering = true;
858 p_sys->i_buffering_extra_system += i_duration;
859 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial +
860 ( p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial ) *
861 INPUT_RATE_DEFAULT / i_rate;
863 p_sys->i_preroll_end = -1;
864 p_sys->i_prev_stream_level = -1;
866 static mtime_t EsOutGetBuffering( es_out_t *out )
868 es_out_sys_t *p_sys = out->p_sys;
869 mtime_t i_stream_duration, i_system_start;
871 if( !p_sys->p_pgrm )
872 return 0;
873 else
875 mtime_t i_stream_start, i_system_duration;
877 if( input_clock_GetState( p_sys->p_pgrm->p_input_clock,
878 &i_stream_start, &i_system_start,
879 &i_stream_duration, &i_system_duration ) )
880 return 0;
883 mtime_t i_delay;
885 if( p_sys->b_buffering && p_sys->i_buffering_extra_initial <= 0 )
887 i_delay = i_stream_duration;
889 else
891 mtime_t i_system_duration;
893 if( p_sys->b_paused )
895 i_system_duration = p_sys->i_pause_date - i_system_start;
896 if( p_sys->i_buffering_extra_initial > 0 )
897 i_system_duration += p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
899 else
901 i_system_duration = mdate() - i_system_start;
904 const mtime_t i_consumed = i_system_duration * INPUT_RATE_DEFAULT / p_sys->i_rate - i_stream_duration;
905 i_delay = p_sys->i_pts_delay - i_consumed;
907 if( i_delay < 0 )
908 return 0;
909 return i_delay;
912 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id,
913 const es_format_t *fmt, const char *psz_language,
914 bool b_delete )
916 es_out_sys_t *p_sys = out->p_sys;
917 input_thread_t *p_input = p_sys->p_input;
918 vlc_value_t val, text;
920 if( b_delete )
922 if( EsFmtIsTeletext( fmt ) )
923 input_SendEventTeletextDel( p_sys->p_input, i_id );
925 input_SendEventEsDel( p_input, fmt->i_cat, i_id );
926 return;
929 /* Get the number of ES already added */
930 const char *psz_var;
931 if( fmt->i_cat == AUDIO_ES )
932 psz_var = "audio-es";
933 else if( fmt->i_cat == VIDEO_ES )
934 psz_var = "video-es";
935 else
936 psz_var = "spu-es";
938 var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
939 if( val.i_int == 0 )
941 vlc_value_t val2;
943 /* First one, we need to add the "Disable" choice */
944 val2.i_int = -1; text.psz_string = _("Disable");
945 var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
946 val.i_int++;
949 /* Take care of the ES description */
950 if( fmt->psz_description && *fmt->psz_description )
952 if( psz_language && *psz_language )
954 if( asprintf( &text.psz_string, "%s - [%s]", fmt->psz_description,
955 psz_language ) == -1 )
956 text.psz_string = NULL;
958 else text.psz_string = strdup( fmt->psz_description );
960 else
962 if( psz_language && *psz_language )
964 if( asprintf( &text.psz_string, "%s %"PRId64" - [%s]", _( "Track" ), val.i_int, psz_language ) == -1 )
965 text.psz_string = NULL;
967 else
969 if( asprintf( &text.psz_string, "%s %"PRId64, _( "Track" ), val.i_int ) == -1 )
970 text.psz_string = NULL;
974 input_SendEventEsAdd( p_input, fmt->i_cat, i_id, text.psz_string );
975 if( EsFmtIsTeletext( fmt ) )
977 char psz_page[3+1];
978 snprintf( psz_page, sizeof(psz_page), "%d%2.2x",
979 fmt->subs.teletext.i_magazine,
980 fmt->subs.teletext.i_page );
981 input_SendEventTeletextAdd( p_sys->p_input,
982 i_id, fmt->subs.teletext.i_magazine >= 0 ? psz_page : NULL );
985 free( text.psz_string );
988 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
989 bool b_delete )
991 EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete );
994 static bool EsOutIsProgramVisible( es_out_t *out, int i_group )
996 es_out_sys_t *p_sys = out->p_sys;
997 return p_sys->i_group_id == 0 || p_sys->i_group_id == i_group;
1000 /* EsOutProgramSelect:
1001 * Select a program and update the object variable
1003 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
1005 es_out_sys_t *p_sys = out->p_sys;
1006 input_thread_t *p_input = p_sys->p_input;
1007 int i;
1009 if( p_sys->p_pgrm == p_pgrm )
1010 return; /* Nothing to do */
1012 if( p_sys->p_pgrm )
1014 es_out_pgrm_t *old = p_sys->p_pgrm;
1015 msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
1017 for( i = 0; i < p_sys->i_es; i++ )
1019 if( p_sys->es[i]->p_pgrm == old && EsIsSelected( p_sys->es[i] ) &&
1020 p_sys->i_mode != ES_OUT_MODE_ALL )
1021 EsUnselect( out, p_sys->es[i], true );
1024 p_sys->audio.p_main_es = NULL;
1025 p_sys->video.p_main_es = NULL;
1026 p_sys->sub.p_main_es = NULL;
1029 msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
1031 /* Mark it selected */
1032 p_pgrm->b_selected = true;
1034 /* Switch master stream */
1035 p_sys->p_pgrm = p_pgrm;
1037 /* Update "program" */
1038 input_SendEventProgramSelect( p_input, p_pgrm->i_id );
1040 /* Update "es-*" */
1041 input_SendEventEsDel( p_input, AUDIO_ES, -1 );
1042 input_SendEventEsDel( p_input, VIDEO_ES, -1 );
1043 input_SendEventEsDel( p_input, SPU_ES, -1 );
1044 input_SendEventTeletextDel( p_input, -1 );
1045 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, p_pgrm->b_scrambled );
1047 /* TODO event */
1048 var_SetInteger( p_input, "teletext-es", -1 );
1050 for( i = 0; i < p_sys->i_es; i++ )
1052 if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
1054 EsOutESVarUpdate( out, p_sys->es[i], false );
1055 EsOutUpdateInfo( out, p_sys->es[i], &p_sys->es[i]->fmt, NULL );
1058 EsOutSelect( out, p_sys->es[i], false );
1061 /* Ensure the correct running EPG table is selected */
1062 input_item_ChangeEPGSource( input_priv(p_input)->p_item, p_pgrm->i_id );
1064 /* Update now playing */
1065 if( p_pgrm->p_meta )
1067 input_item_SetESNowPlaying( input_priv(p_input)->p_item,
1068 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) );
1069 input_item_SetPublisher( input_priv(p_input)->p_item,
1070 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Publisher ) );
1071 input_item_SetTitle( input_priv(p_input)->p_item,
1072 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1073 input_SendEventMeta( p_input );
1074 /* FIXME: we probably want to replace every input meta */
1078 /* EsOutAddProgram:
1079 * Add a program
1081 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
1083 es_out_sys_t *p_sys = out->p_sys;
1084 input_thread_t *p_input = p_sys->p_input;
1086 es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
1087 if( !p_pgrm )
1088 return NULL;
1090 /* Init */
1091 p_pgrm->i_id = i_group;
1092 p_pgrm->i_es = 0;
1093 p_pgrm->b_selected = false;
1094 p_pgrm->b_scrambled = false;
1095 p_pgrm->p_meta = NULL;
1096 p_pgrm->p_input_clock = input_clock_New( p_sys->i_rate );
1097 if( !p_pgrm->p_input_clock )
1099 free( p_pgrm );
1100 return NULL;
1102 if( p_sys->b_paused )
1103 input_clock_ChangePause( p_pgrm->p_input_clock, p_sys->b_paused, p_sys->i_pause_date );
1104 input_clock_SetJitter( p_pgrm->p_input_clock, p_sys->i_pts_delay, p_sys->i_cr_average );
1106 /* Append it */
1107 TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
1109 /* Update "program" variable */
1110 if( EsOutIsProgramVisible( out, i_group ) )
1111 input_SendEventProgramAdd( p_input, i_group, NULL );
1113 if( i_group == p_sys->i_group_id || ( !p_sys->p_pgrm && p_sys->i_group_id == 0 ) )
1114 EsOutProgramSelect( out, p_pgrm );
1116 return p_pgrm;
1119 /* EsOutDelProgram:
1120 * Delete a program
1122 static int EsOutProgramDel( es_out_t *out, int i_group )
1124 es_out_sys_t *p_sys = out->p_sys;
1125 input_thread_t *p_input = p_sys->p_input;
1126 es_out_pgrm_t *p_pgrm = NULL;
1127 int i;
1129 for( i = 0; i < p_sys->i_pgrm; i++ )
1131 if( p_sys->pgrm[i]->i_id == i_group )
1133 p_pgrm = p_sys->pgrm[i];
1134 break;
1138 if( p_pgrm == NULL )
1139 return VLC_EGENERIC;
1141 if( p_pgrm->i_es )
1143 msg_Dbg( p_input, "can't delete program %d which still has %i ES",
1144 i_group, p_pgrm->i_es );
1145 return VLC_EGENERIC;
1148 TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
1150 /* If program is selected we need to unselect it */
1151 if( p_sys->p_pgrm == p_pgrm )
1152 p_sys->p_pgrm = NULL;
1154 input_clock_Delete( p_pgrm->p_input_clock );
1156 if( p_pgrm->p_meta )
1157 vlc_meta_Delete( p_pgrm->p_meta );
1158 free( p_pgrm );
1160 /* Update "program" variable */
1161 input_SendEventProgramDel( p_input, i_group );
1163 return VLC_SUCCESS;
1166 /* EsOutProgramFind
1168 static es_out_pgrm_t *EsOutProgramFind( es_out_t *p_out, int i_group )
1170 es_out_sys_t *p_sys = p_out->p_sys;
1172 for( int i = 0; i < p_sys->i_pgrm; i++ )
1174 if( p_sys->pgrm[i]->i_id == i_group )
1175 return p_sys->pgrm[i];
1177 return EsOutProgramAdd( p_out, i_group );
1180 /* EsOutProgramMeta:
1182 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
1184 char *psz = NULL;
1185 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1187 if( asprintf( &psz, _("%s [%s %d]"), vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ),
1188 _("Program"), p_pgrm->i_id ) == -1 )
1189 return NULL;
1191 else
1193 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1194 return NULL;
1196 return psz;
1199 static char *EsOutProgramGetProgramName( es_out_pgrm_t *p_pgrm )
1201 char *psz = NULL;
1202 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1204 return strdup( vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1206 else
1208 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1209 return NULL;
1211 return psz;
1214 static char *EsInfoCategoryName( es_out_id_t* es )
1216 char *psz_category;
1218 if( asprintf( &psz_category, _("Stream %d"), es->i_meta_id ) == -1 )
1219 return NULL;
1221 return psz_category;
1224 static void EsOutProgramMeta( es_out_t *out, int i_group, const vlc_meta_t *p_meta )
1226 es_out_sys_t *p_sys = out->p_sys;
1227 es_out_pgrm_t *p_pgrm;
1228 input_thread_t *p_input = p_sys->p_input;
1229 const char *psz_title = NULL;
1230 const char *psz_provider = NULL;
1231 int i;
1233 msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
1235 /* Check against empty meta data (empty for what we handle) */
1236 if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
1237 !vlc_meta_Get( p_meta, vlc_meta_ESNowPlaying) &&
1238 !vlc_meta_Get( p_meta, vlc_meta_Publisher) )
1240 return;
1243 if( i_group < 0 )
1245 EsOutGlobalMeta( out, p_meta );
1246 return;
1249 /* Find program */
1250 if( !EsOutIsProgramVisible( out, i_group ) )
1251 return;
1252 p_pgrm = EsOutProgramFind( out, i_group );
1253 if( !p_pgrm )
1254 return;
1256 if( p_pgrm->p_meta )
1258 const char *psz_current_title = vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title );
1259 const char *psz_new_title = vlc_meta_Get( p_meta, vlc_meta_Title );
1260 if( (psz_current_title != NULL && psz_new_title != NULL)
1261 ? strcmp(psz_new_title, psz_current_title)
1262 : (psz_current_title != psz_new_title) )
1264 /* Remove old entries */
1265 char *psz_oldinfokey = EsOutProgramGetMetaName( p_pgrm );
1266 input_Control( p_input, INPUT_DEL_INFO, psz_oldinfokey, NULL );
1267 /* TODO update epg name ?
1268 * TODO update scrambled info name ? */
1269 free( psz_oldinfokey );
1271 vlc_meta_Delete( p_pgrm->p_meta );
1273 p_pgrm->p_meta = vlc_meta_New();
1274 if( p_pgrm->p_meta )
1275 vlc_meta_Merge( p_pgrm->p_meta, p_meta );
1277 if( p_sys->p_pgrm == p_pgrm )
1279 EsOutMeta( out, NULL, p_meta );
1281 /* */
1282 psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
1283 psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
1285 /* Update the description text of the program */
1286 if( psz_title && *psz_title )
1288 char *psz_text;
1289 if( psz_provider && *psz_provider )
1291 if( asprintf( &psz_text, "%s [%s]", psz_title, psz_provider ) < 0 )
1292 psz_text = NULL;
1294 else
1296 psz_text = strdup( psz_title );
1299 /* ugly but it works */
1300 if( psz_text )
1302 input_SendEventProgramDel( p_input, i_group );
1303 input_SendEventProgramAdd( p_input, i_group, psz_text );
1304 if( p_sys->p_pgrm == p_pgrm )
1305 input_SendEventProgramSelect( p_input, i_group );
1306 free( psz_text );
1310 /* */
1311 char **ppsz_all_keys = vlc_meta_CopyExtraNames(p_meta );
1313 info_category_t *p_cat = NULL;
1314 if( psz_provider || ( ppsz_all_keys[0] && *ppsz_all_keys[0] ) )
1316 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1317 if( psz_cat )
1318 p_cat = info_category_New( psz_cat );
1319 free( psz_cat );
1322 for( i = 0; ppsz_all_keys[i]; i++ )
1324 if( p_cat )
1325 info_category_AddInfo( p_cat, vlc_gettext(ppsz_all_keys[i]), "%s",
1326 vlc_meta_GetExtra( p_meta, ppsz_all_keys[i] ) );
1327 free( ppsz_all_keys[i] );
1329 free( ppsz_all_keys );
1331 if( psz_provider )
1333 if( p_sys->p_pgrm == p_pgrm )
1335 input_item_SetPublisher( input_priv(p_input)->p_item, psz_provider );
1336 input_SendEventMeta( p_input );
1338 if( p_cat )
1339 info_category_AddInfo( p_cat, vlc_meta_TypeToLocalizedString(vlc_meta_Publisher),
1340 "%s",psz_provider );
1342 if( p_cat )
1343 input_Control( p_input, INPUT_MERGE_INFOS, p_cat );
1346 static void EsOutProgramEpgEvent( es_out_t *out, int i_group, const vlc_epg_event_t *p_event )
1348 es_out_sys_t *p_sys = out->p_sys;
1349 input_thread_t *p_input = p_sys->p_input;
1350 input_item_t *p_item = input_priv(p_input)->p_item;
1351 es_out_pgrm_t *p_pgrm;
1353 /* Find program */
1354 if( !EsOutIsProgramVisible( out, i_group ) )
1355 return;
1356 p_pgrm = EsOutProgramFind( out, i_group );
1357 if( !p_pgrm )
1358 return;
1360 input_item_SetEpgEvent( p_item, p_event );
1363 static void EsOutProgramEpg( es_out_t *out, int i_group, const vlc_epg_t *p_epg )
1365 es_out_sys_t *p_sys = out->p_sys;
1366 input_thread_t *p_input = p_sys->p_input;
1367 input_item_t *p_item = input_priv(p_input)->p_item;
1368 es_out_pgrm_t *p_pgrm;
1369 char *psz_cat;
1371 /* Find program */
1372 if( !EsOutIsProgramVisible( out, i_group ) )
1373 return;
1374 p_pgrm = EsOutProgramFind( out, i_group );
1375 if( !p_pgrm )
1376 return;
1378 /* Update info */
1379 psz_cat = EsOutProgramGetMetaName( p_pgrm );
1380 msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, psz_cat );
1382 /* Merge EPG */
1383 vlc_epg_t epg;
1385 epg = *p_epg;
1386 epg.psz_name = EsOutProgramGetProgramName( p_pgrm );
1388 input_item_SetEpg( p_item, &epg, p_sys->p_pgrm && (p_epg->i_source_id == p_sys->p_pgrm->i_id) );
1389 input_SendEventMetaEpg( p_sys->p_input );
1391 free( epg.psz_name );
1393 /* Update now playing */
1394 if( p_epg->b_present && p_pgrm->p_meta &&
1395 ( p_epg->p_current || p_epg->i_event == 0 ) )
1397 vlc_meta_SetNowPlaying( p_pgrm->p_meta, NULL );
1400 vlc_mutex_lock( &p_item->lock );
1401 for( int i = 0; i < p_item->i_epg; i++ )
1403 const vlc_epg_t *p_tmp = p_item->pp_epg[i];
1405 if( p_tmp->b_present && p_tmp->i_source_id == p_pgrm->i_id )
1407 const char *psz_name = ( p_tmp->p_current ) ? p_tmp->p_current->psz_name : NULL;
1408 if( !p_pgrm->p_meta )
1409 p_pgrm->p_meta = vlc_meta_New();
1410 if( p_pgrm->p_meta )
1411 vlc_meta_Set( p_pgrm->p_meta, vlc_meta_ESNowPlaying, psz_name );
1412 break;
1415 vlc_mutex_unlock( &p_item->lock );
1417 /* Update selected program input info */
1418 if( p_pgrm == p_sys->p_pgrm )
1420 const char *psz_nowplaying = p_pgrm->p_meta ?
1421 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) : NULL;
1423 input_item_SetESNowPlaying( input_priv(p_input)->p_item, psz_nowplaying );
1424 input_SendEventMeta( p_input );
1426 if( psz_nowplaying )
1428 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1429 vlc_meta_TypeToLocalizedString(vlc_meta_ESNowPlaying), "%s", psz_nowplaying );
1431 else
1433 input_Control( p_input, INPUT_DEL_INFO, psz_cat,
1434 vlc_meta_TypeToLocalizedString(vlc_meta_ESNowPlaying) );
1438 free( psz_cat );
1441 static void EsOutEpgTime( es_out_t *out, int64_t time )
1443 es_out_sys_t *p_sys = out->p_sys;
1444 input_thread_t *p_input = p_sys->p_input;
1445 input_item_t *p_item = input_priv(p_input)->p_item;
1447 input_item_SetEpgTime( p_item, time );
1450 static void EsOutProgramUpdateScrambled( es_out_t *p_out, es_out_pgrm_t *p_pgrm )
1452 es_out_sys_t *p_sys = p_out->p_sys;
1453 input_thread_t *p_input = p_sys->p_input;
1454 bool b_scrambled = false;
1456 for( int i = 0; i < p_sys->i_es; i++ )
1458 if( p_sys->es[i]->p_pgrm == p_pgrm && p_sys->es[i]->b_scrambled )
1460 b_scrambled = true;
1461 break;
1464 if( !p_pgrm->b_scrambled == !b_scrambled )
1465 return;
1467 p_pgrm->b_scrambled = b_scrambled;
1468 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1470 if( b_scrambled )
1471 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Scrambled"), _("Yes") );
1472 else
1473 input_Control( p_input, INPUT_DEL_INFO, psz_cat, _("Scrambled") );
1474 free( psz_cat );
1476 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, b_scrambled );
1479 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_program_meta )
1481 es_out_sys_t *p_sys = p_out->p_sys;
1482 input_thread_t *p_input = p_sys->p_input;
1483 input_item_t *p_item = input_GetItem( p_input );
1485 vlc_mutex_lock( &p_item->lock );
1486 if( p_meta )
1487 vlc_meta_Merge( p_item->p_meta, p_meta );
1488 vlc_mutex_unlock( &p_item->lock );
1490 /* Check program meta to not override GROUP_META values */
1491 if( p_meta && (!p_program_meta || vlc_meta_Get( p_program_meta, vlc_meta_Title ) == NULL) &&
1492 vlc_meta_Get( p_meta, vlc_meta_Title ) != NULL )
1493 input_item_SetName( p_item, vlc_meta_Get( p_meta, vlc_meta_Title ) );
1495 const char *psz_arturl = NULL;
1496 char *psz_alloc = NULL;
1498 if( p_program_meta )
1499 psz_arturl = vlc_meta_Get( p_program_meta, vlc_meta_ArtworkURL );
1500 if( psz_arturl == NULL && p_meta )
1501 psz_arturl = vlc_meta_Get( p_meta, vlc_meta_ArtworkURL );
1503 if( psz_arturl == NULL ) /* restore/favor previously set item art URL */
1504 psz_arturl = psz_alloc = input_item_GetArtURL( p_item );
1506 if( psz_arturl != NULL )
1507 input_item_SetArtURL( p_item, psz_arturl );
1509 if( psz_arturl != NULL && !strncmp( psz_arturl, "attachment://", 13 ) )
1510 { /* Clear art cover if streaming out.
1511 * FIXME: Why? Remove this when sout gets meta data support. */
1512 if( input_priv(p_input)->p_sout != NULL )
1513 input_item_SetArtURL( p_item, NULL );
1514 else
1515 input_ExtractAttachmentAndCacheArt( p_input, psz_arturl + 13 );
1517 free( psz_alloc );
1519 input_item_SetPreparsed( p_item, true );
1521 input_SendEventMeta( p_input );
1522 /* TODO handle sout meta ? */
1525 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta )
1527 es_out_sys_t *p_sys = p_out->p_sys;
1528 EsOutMeta( p_out, p_meta,
1529 (p_sys->p_pgrm && p_sys->p_pgrm->p_meta) ? p_sys->p_pgrm->p_meta : NULL );
1532 static es_out_id_t *EsOutAddSlave( es_out_t *out, const es_format_t *fmt, es_out_id_t *p_master )
1534 es_out_sys_t *p_sys = out->p_sys;
1535 input_thread_t *p_input = p_sys->p_input;
1537 if( fmt->i_group < 0 )
1539 msg_Err( p_input, "invalid group number" );
1540 return NULL;
1543 es_out_id_t *es = malloc( sizeof( *es ) );
1544 es_out_pgrm_t *p_pgrm;
1545 int i;
1547 if( !es )
1548 return NULL;
1550 vlc_mutex_lock( &p_sys->lock );
1552 /* Search the program */
1553 p_pgrm = EsOutProgramFind( out, fmt->i_group );
1554 if( !p_pgrm )
1556 vlc_mutex_unlock( &p_sys->lock );
1557 free( es );
1558 return NULL;
1561 /* Increase ref count for program */
1562 p_pgrm->i_es++;
1564 /* Set up ES */
1565 es->p_pgrm = p_pgrm;
1566 es_format_Copy( &es->fmt, fmt );
1567 if( es->fmt.i_id < 0 )
1568 es->fmt.i_id = p_sys->i_id;
1569 if( !es->fmt.i_original_fourcc )
1570 es->fmt.i_original_fourcc = es->fmt.i_codec;
1572 es->i_id = es->fmt.i_id;
1573 es->i_meta_id = p_sys->i_id++; /* always incremented */
1574 es->b_scrambled = false;
1576 switch( es->fmt.i_cat )
1578 case AUDIO_ES:
1580 es->fmt.i_codec = vlc_fourcc_GetCodecAudio( es->fmt.i_codec,
1581 es->fmt.audio.i_bitspersample );
1582 es->i_channel = p_sys->audio.i_count++;
1584 audio_replay_gain_t rg;
1585 memset( &rg, 0, sizeof(rg) );
1586 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
1587 vlc_audio_replay_gain_MergeFromMeta( &rg, input_priv(p_input)->p_item->p_meta );
1588 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1590 for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
1592 if( !es->fmt.audio_replay_gain.pb_peak[i] )
1594 es->fmt.audio_replay_gain.pb_peak[i] = rg.pb_peak[i];
1595 es->fmt.audio_replay_gain.pf_peak[i] = rg.pf_peak[i];
1597 if( !es->fmt.audio_replay_gain.pb_gain[i] )
1599 es->fmt.audio_replay_gain.pb_gain[i] = rg.pb_gain[i];
1600 es->fmt.audio_replay_gain.pf_gain[i] = rg.pf_gain[i];
1603 break;
1606 case VIDEO_ES:
1607 es->fmt.i_codec = vlc_fourcc_GetCodec( es->fmt.i_cat, es->fmt.i_codec );
1608 es->i_channel = p_sys->video.i_count++;
1610 if( !es->fmt.video.i_visible_width || !es->fmt.video.i_visible_height )
1612 es->fmt.video.i_visible_width = es->fmt.video.i_width;
1613 es->fmt.video.i_visible_height = es->fmt.video.i_height;
1616 if( es->fmt.video.i_frame_rate && es->fmt.video.i_frame_rate_base )
1617 vlc_ureduce( &es->fmt.video.i_frame_rate,
1618 &es->fmt.video.i_frame_rate_base,
1619 es->fmt.video.i_frame_rate,
1620 es->fmt.video.i_frame_rate_base, 0 );
1621 break;
1623 case SPU_ES:
1624 es->fmt.i_codec = vlc_fourcc_GetCodec( es->fmt.i_cat, es->fmt.i_codec );
1625 es->i_channel = p_sys->sub.i_count++;
1626 break;
1628 default:
1629 es->i_channel = 0;
1630 break;
1632 es->psz_language = LanguageGetName( es->fmt.psz_language ); /* remember so we only need to do it once */
1633 es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
1634 es->p_dec = NULL;
1635 es->p_dec_record = NULL;
1636 es->cc.type = 0;
1637 es->cc.i_bitmap = 0;
1638 es->p_master = p_master;
1640 TAB_APPEND( p_sys->i_es, p_sys->es, es );
1642 if( es->p_pgrm == p_sys->p_pgrm )
1643 EsOutESVarUpdate( out, es, false );
1645 EsOutUpdateInfo( out, es, &es->fmt, NULL );
1646 EsOutSelect( out, es, false );
1648 if( es->b_scrambled )
1649 EsOutProgramUpdateScrambled( out, es->p_pgrm );
1651 vlc_mutex_unlock( &p_sys->lock );
1653 return es;
1656 /* EsOutAdd:
1657 * Add an es_out
1659 static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt )
1661 return EsOutAddSlave( out, fmt, NULL );
1664 static bool EsIsSelected( es_out_id_t *es )
1666 if( es->p_master )
1668 bool b_decode = false;
1669 if( es->p_master->p_dec )
1671 int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
1672 input_DecoderGetCcState( es->p_master->p_dec, es->fmt.i_codec,
1673 i_channel, &b_decode );
1675 return b_decode;
1677 else
1679 return es->p_dec != NULL;
1682 static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
1684 es_out_sys_t *p_sys = out->p_sys;
1685 input_thread_t *p_input = p_sys->p_input;
1687 p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_input_clock, input_priv(p_input)->p_sout );
1688 if( p_es->p_dec )
1690 if( p_sys->b_buffering )
1691 input_DecoderStartWait( p_es->p_dec );
1693 if( !p_es->p_master && p_sys->p_sout_record )
1695 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_input_clock, p_sys->p_sout_record );
1696 if( p_es->p_dec_record && p_sys->b_buffering )
1697 input_DecoderStartWait( p_es->p_dec_record );
1701 EsOutDecoderChangeDelay( out, p_es );
1703 static void EsDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
1705 VLC_UNUSED(out);
1707 if( !p_es->p_dec )
1708 return;
1710 input_DecoderDelete( p_es->p_dec );
1711 p_es->p_dec = NULL;
1713 if( p_es->p_dec_record )
1715 input_DecoderDelete( p_es->p_dec_record );
1716 p_es->p_dec_record = NULL;
1720 static void EsSelect( es_out_t *out, es_out_id_t *es )
1722 es_out_sys_t *p_sys = out->p_sys;
1723 input_thread_t *p_input = p_sys->p_input;
1725 if( EsIsSelected( es ) )
1727 msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
1728 return;
1731 if( es->p_master )
1733 int i_channel;
1734 if( !es->p_master->p_dec )
1735 return;
1737 i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
1739 if( i_channel == -1 ||
1740 input_DecoderSetCcState( es->p_master->p_dec, es->fmt.i_codec,
1741 i_channel, true ) )
1742 return;
1744 else
1746 const bool b_sout = input_priv(p_input)->p_sout != NULL;
1747 if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
1749 if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) )
1751 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
1752 es->i_id );
1753 return;
1756 else if( es->fmt.i_cat == AUDIO_ES )
1758 if( !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) )
1760 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
1761 es->i_id );
1762 return;
1765 if( es->fmt.i_cat == SPU_ES )
1767 if( !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) )
1769 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
1770 es->i_id );
1771 return;
1775 EsCreateDecoder( out, es );
1777 if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
1778 return;
1781 /* Mark it as selected */
1782 input_SendEventEsSelect( p_input, es->fmt.i_cat, es->i_id );
1783 input_SendEventTeletextSelect( p_input, EsFmtIsTeletext( &es->fmt ) ? es->i_id : -1 );
1786 static void EsDeleteCCChannels( es_out_t *out, es_out_id_t *parent )
1788 es_out_sys_t *p_sys = out->p_sys;
1789 input_thread_t *p_input = p_sys->p_input;
1791 if( parent->cc.type == 0 )
1792 return;
1794 const int i_spu_id = var_GetInteger( p_input, "spu-es");
1796 uint64_t i_bitmap = parent->cc.i_bitmap;
1797 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1 )
1799 if( (i_bitmap & 1) == 0 || !parent->cc.pp_es[i] )
1800 continue;
1802 if( i_spu_id == parent->cc.pp_es[i]->i_id )
1804 /* Force unselection of the CC */
1805 input_SendEventEsSelect( p_input, SPU_ES, -1 );
1807 EsOutDel( out, parent->cc.pp_es[i] );
1810 parent->cc.i_bitmap = 0;
1811 parent->cc.type = 0;
1814 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
1816 es_out_sys_t *p_sys = out->p_sys;
1817 input_thread_t *p_input = p_sys->p_input;
1819 if( !EsIsSelected( es ) )
1821 msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
1822 return;
1825 if( es->p_master )
1827 if( es->p_master->p_dec )
1829 int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
1830 if( i_channel != -1 )
1831 input_DecoderSetCcState( es->p_master->p_dec, es->fmt.i_codec,
1832 i_channel, false );
1835 else
1837 EsDeleteCCChannels( out, es );
1838 EsDestroyDecoder( out, es );
1841 if( !b_update )
1842 return;
1844 /* Mark it as unselected */
1845 input_SendEventEsSelect( p_input, es->fmt.i_cat, -1 );
1846 if( EsFmtIsTeletext( &es->fmt ) )
1847 input_SendEventTeletextSelect( p_input, -1 );
1851 * Select an ES given the current mode
1852 * XXX: you need to take a the lock before (stream.stream_lock)
1854 * \param out The es_out structure
1855 * \param es es_out_id structure
1856 * \param b_force ...
1857 * \return nothing
1859 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
1861 es_out_sys_t *p_sys = out->p_sys;
1862 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
1864 if( !p_sys->b_active ||
1865 ( !b_force && es->fmt.i_priority < ES_PRIORITY_SELECTABLE_MIN ) )
1867 return;
1870 bool b_auto_unselect = p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO &&
1871 p_esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE &&
1872 p_esprops->p_main_es && p_esprops->p_main_es != es;
1874 if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
1876 if( !EsIsSelected( es ) )
1878 if( b_auto_unselect )
1879 EsUnselect( out, p_esprops->p_main_es, false );
1881 EsSelect( out, es );
1884 else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
1886 char *prgms = var_GetNonEmptyString( p_sys->p_input, "programs" );
1887 if( prgms != NULL )
1889 char *buf;
1891 for ( const char *prgm = strtok_r( prgms, ",", &buf );
1892 prgm != NULL;
1893 prgm = strtok_r( NULL, ",", &buf ) )
1895 if( atoi( prgm ) == es->p_pgrm->i_id || b_force )
1897 if( !EsIsSelected( es ) )
1898 EsSelect( out, es );
1899 break;
1902 free( prgms );
1905 else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
1907 const es_out_id_t *wanted_es = NULL;
1909 if( es->p_pgrm != p_sys->p_pgrm || !p_esprops )
1910 return;
1912 /* user designated by ID ES have higher prio than everything */
1913 if ( p_esprops->i_id >= 0 )
1915 if( es->i_id == p_esprops->i_id )
1916 wanted_es = es;
1918 /* then per pos */
1919 else if( p_esprops->i_channel >= 0 )
1921 if( p_esprops->i_channel == es->i_channel )
1922 wanted_es = es;
1924 else if( p_esprops->ppsz_language )
1926 /* If not deactivated */
1927 const int i_stop_idx = LanguageArrayIndex( p_esprops->ppsz_language, "none" );
1929 int current_es_idx = ( p_esprops->p_main_es == NULL ) ? -1 :
1930 LanguageArrayIndex( p_esprops->ppsz_language,
1931 p_esprops->p_main_es->psz_language_code );
1932 int es_idx = LanguageArrayIndex( p_esprops->ppsz_language,
1933 es->psz_language_code );
1934 if( es_idx >= 0 && (i_stop_idx < 0 || i_stop_idx > es_idx) )
1936 /* Only select the language if it's in the list */
1937 if( p_esprops->p_main_es == NULL ||
1938 current_es_idx < 0 || /* current es was not selected by lang prefs */
1939 es_idx < current_es_idx || /* current es has lower lang prio */
1940 ( es_idx == current_es_idx && /* lang is same, but es has higher prio */
1941 p_esprops->p_main_es->fmt.i_priority < es->fmt.i_priority ) )
1943 wanted_es = es;
1946 /* We did not find a language matching our prefs */
1947 else if( i_stop_idx < 0 ) /* If not fallback disabled by 'none' */
1949 /* Select if asked by demuxer */
1950 if( current_es_idx < 0 ) /* No es is currently selected by lang pref */
1952 /* If demux has specified a track */
1953 if( p_esprops->i_demux_id >= 0 && es->i_id == p_esprops->i_demux_id )
1955 wanted_es = es;
1957 /* Otherwise, fallback by priority */
1958 else if( p_esprops->p_main_es == NULL ||
1959 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority )
1961 if( p_esprops->b_autoselect )
1962 wanted_es = es;
1969 /* If there is no user preference, select the default subtitle
1970 * or adapt by ES priority */
1971 else if( p_esprops->i_demux_id >= 0 && es->i_id == p_esprops->i_demux_id )
1973 wanted_es = es;
1975 else if( p_esprops->p_main_es == NULL ||
1976 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority )
1978 if( p_esprops->b_autoselect )
1979 wanted_es = es;
1982 if( wanted_es == es && !EsIsSelected( es ) )
1984 if( b_auto_unselect )
1985 EsUnselect( out, p_esprops->p_main_es, false );
1987 EsSelect( out, es );
1991 /* FIXME TODO handle priority here */
1992 if( p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO && EsIsSelected( es ) )
1993 p_esprops->p_main_es = es;
1996 static void EsOutCreateCCChannels( es_out_t *out, vlc_fourcc_t codec, uint64_t i_bitmap,
1997 const char *psz_descfmt, es_out_id_t *parent )
1999 es_out_sys_t *p_sys = out->p_sys;
2000 input_thread_t *p_input = p_sys->p_input;
2002 /* Only one type of captions is allowed ! */
2003 if( parent->cc.type && parent->cc.type != codec )
2004 return;
2006 uint64_t i_existingbitmap = parent->cc.i_bitmap;
2007 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1, i_existingbitmap >>= 1 )
2009 es_format_t fmt;
2011 if( (i_bitmap & 1) == 0 || (i_existingbitmap & 1) )
2012 continue;
2014 msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, parent->i_id );
2016 es_format_Init( &fmt, SPU_ES, codec );
2017 fmt.subs.cc.i_channel = i;
2018 fmt.i_group = parent->fmt.i_group;
2019 if( asprintf( &fmt.psz_description, psz_descfmt, 1 + i ) == -1 )
2020 fmt.psz_description = NULL;
2022 es_out_id_t **pp_es = &parent->cc.pp_es[i];
2023 *pp_es = EsOutAddSlave( out, &fmt, parent );
2024 es_format_Clean( &fmt );
2026 /* */
2027 parent->cc.i_bitmap |= (1ULL << i);
2028 parent->cc.type = codec;
2030 /* Enable if user specified on command line */
2031 if (p_sys->sub.i_channel == i)
2032 EsOutSelect(out, *pp_es, true);
2037 * Send a block for the given es_out
2039 * \param out the es_out to send from
2040 * \param es the es_out_id
2041 * \param p_block the data block to send
2043 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
2045 es_out_sys_t *p_sys = out->p_sys;
2046 input_thread_t *p_input = p_sys->p_input;
2048 assert( p_block->p_next == NULL );
2050 struct input_stats *stats = input_priv(p_input)->stats;
2051 if( stats != NULL )
2053 input_rate_Add( &stats->demux_bitrate, p_block->i_buffer );
2055 /* Update number of corrupted data packats */
2056 if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
2057 atomic_fetch_add_explicit(&stats->demux_corrupted, 1,
2058 memory_order_relaxed);
2060 /* Update number of discontinuities */
2061 if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )
2062 atomic_fetch_add_explicit(&stats->demux_discontinuity, 1,
2063 memory_order_relaxed);
2066 vlc_mutex_lock( &p_sys->lock );
2068 /* Mark preroll blocks */
2069 if( p_sys->i_preroll_end >= 0 )
2071 int64_t i_date = p_block->i_pts;
2072 if( p_block->i_pts == VLC_TS_INVALID )
2073 i_date = p_block->i_dts;
2075 if( i_date + p_block->i_length < p_sys->i_preroll_end )
2076 p_block->i_flags |= BLOCK_FLAG_PREROLL;
2079 if( !es->p_dec )
2081 block_Release( p_block );
2082 vlc_mutex_unlock( &p_sys->lock );
2083 return VLC_SUCCESS;
2086 /* Check for sout mode */
2087 if( input_priv(p_input)->p_sout )
2089 /* FIXME review this, proper lock may be missing */
2090 if( input_priv(p_input)->p_sout->i_out_pace_nocontrol > 0 &&
2091 input_priv(p_input)->b_out_pace_control )
2093 msg_Dbg( p_input, "switching to sync mode" );
2094 input_priv(p_input)->b_out_pace_control = false;
2096 else if( input_priv(p_input)->p_sout->i_out_pace_nocontrol <= 0 &&
2097 !input_priv(p_input)->b_out_pace_control )
2099 msg_Dbg( p_input, "switching to async mode" );
2100 input_priv(p_input)->b_out_pace_control = true;
2104 /* Decode */
2105 if( es->p_dec_record )
2107 block_t *p_dup = block_Duplicate( p_block );
2108 if( p_dup )
2109 input_DecoderDecode( es->p_dec_record, p_dup,
2110 input_priv(p_input)->b_out_pace_control );
2112 input_DecoderDecode( es->p_dec, p_block,
2113 input_priv(p_input)->b_out_pace_control );
2115 es_format_t fmt_dsc;
2116 vlc_meta_t *p_meta_dsc;
2117 if( input_DecoderHasFormatChanged( es->p_dec, &fmt_dsc, &p_meta_dsc ) )
2119 EsOutUpdateInfo( out, es, &fmt_dsc, p_meta_dsc );
2121 es_format_Clean( &fmt_dsc );
2122 if( p_meta_dsc )
2123 vlc_meta_Delete( p_meta_dsc );
2126 /* Check CC status */
2127 decoder_cc_desc_t desc;
2129 input_DecoderGetCcDesc( es->p_dec, &desc );
2130 if( var_InheritInteger( p_input, "captions" ) == 708 )
2131 EsOutCreateCCChannels( out, VLC_CODEC_CEA708, desc.i_708_channels,
2132 _("DTVCC Closed captions %u"), es );
2133 EsOutCreateCCChannels( out, VLC_CODEC_CEA608, desc.i_608_channels,
2134 _("Closed captions %u"), es );
2136 vlc_mutex_unlock( &p_sys->lock );
2138 return VLC_SUCCESS;
2141 /*****************************************************************************
2142 * EsOutDel:
2143 *****************************************************************************/
2144 static void EsOutDel( es_out_t *out, es_out_id_t *es )
2146 es_out_sys_t *p_sys = out->p_sys;
2147 bool b_reselect = false;
2148 int i;
2150 vlc_mutex_lock( &p_sys->lock );
2152 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
2154 /* We don't try to reselect */
2155 if( es->p_dec )
2156 { /* FIXME: This might hold the ES output caller (i.e. the demux), and
2157 * the corresponding thread (typically the input thread), for a little
2158 * bit too long if the ES is deleted in the middle of a stream. */
2159 input_DecoderDrain( es->p_dec );
2160 while( !input_Stopped(p_sys->p_input) && !p_sys->b_buffering )
2162 if( input_DecoderIsEmpty( es->p_dec ) &&
2163 ( !es->p_dec_record || input_DecoderIsEmpty( es->p_dec_record ) ))
2164 break;
2165 /* FIXME there should be a way to have auto deleted es, but there will be
2166 * a problem when another codec of the same type is created (mainly video) */
2167 msleep( 20*1000 );
2169 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
2172 if( es->p_pgrm == p_sys->p_pgrm )
2173 EsOutESVarUpdate( out, es, true );
2175 EsDeleteInfo( out, es );
2177 TAB_REMOVE( p_sys->i_es, p_sys->es, es );
2179 /* Update program */
2180 es->p_pgrm->i_es--;
2181 if( es->p_pgrm->i_es == 0 )
2182 msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" );
2184 if( es->b_scrambled )
2185 EsOutProgramUpdateScrambled( out, es->p_pgrm );
2187 /* */
2188 if( p_esprops )
2190 if( p_esprops->p_main_es == es )
2192 b_reselect = true;
2193 p_esprops->p_main_es = NULL;
2195 p_esprops->i_count--;
2198 /* Re-select another track when needed */
2199 if( b_reselect )
2201 for( i = 0; i < p_sys->i_es; i++ )
2203 if( es->fmt.i_cat == p_sys->es[i]->fmt.i_cat )
2205 if( EsIsSelected(p_sys->es[i]) )
2207 input_SendEventEsSelect( p_sys->p_input, es->fmt.i_cat, p_sys->es[i]->i_id );
2208 if( p_esprops->p_main_es == NULL )
2209 p_esprops->p_main_es = p_sys->es[i];
2211 else
2212 EsOutSelect( out, p_sys->es[i], false );
2217 free( es->psz_language );
2218 free( es->psz_language_code );
2220 es_format_Clean( &es->fmt );
2222 vlc_mutex_unlock( &p_sys->lock );
2224 free( es );
2228 * Control query handler
2230 * \param out the es_out to control
2231 * \param i_query A es_out query as defined in include/ninput.h
2232 * \param args a variable list of arguments for the query
2233 * \return VLC_SUCCESS or an error code
2235 static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
2237 es_out_sys_t *p_sys = out->p_sys;
2239 switch( i_query )
2241 case ES_OUT_SET_ES_STATE:
2243 es_out_id_t *es = va_arg( args, es_out_id_t * );
2244 bool b = va_arg( args, int );
2245 if( b && !EsIsSelected( es ) )
2247 EsSelect( out, es );
2248 return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC;
2250 else if( !b && EsIsSelected( es ) )
2252 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
2253 return VLC_SUCCESS;
2255 return VLC_SUCCESS;
2258 case ES_OUT_GET_ES_STATE:
2260 es_out_id_t *es = va_arg( args, es_out_id_t * );
2261 bool *pb = va_arg( args, bool * );
2263 *pb = EsIsSelected( es );
2264 return VLC_SUCCESS;
2267 case ES_OUT_SET_ES_CAT_POLICY:
2269 enum es_format_category_e i_cat = va_arg( args, enum es_format_category_e );
2270 enum es_out_policy_e i_pol = va_arg( args, enum es_out_policy_e );
2271 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, i_cat );
2272 if( p_esprops == NULL )
2273 return VLC_EGENERIC;
2274 p_esprops->e_policy = i_pol;
2275 return VLC_SUCCESS;
2278 case ES_OUT_GET_GROUP_FORCED:
2280 int *pi_group = va_arg( args, int * );
2281 *pi_group = p_sys->i_group_id;
2282 return VLC_SUCCESS;
2285 case ES_OUT_SET_MODE:
2287 const int i_mode = va_arg( args, int );
2288 assert( i_mode == ES_OUT_MODE_NONE || i_mode == ES_OUT_MODE_ALL ||
2289 i_mode == ES_OUT_MODE_AUTO || i_mode == ES_OUT_MODE_PARTIAL ||
2290 i_mode == ES_OUT_MODE_END );
2292 if( i_mode != ES_OUT_MODE_NONE && !p_sys->b_active && p_sys->i_es > 0 )
2294 /* XXX Terminate vout if there are tracks but no video one.
2295 * This one is not mandatory but is he earliest place where it
2296 * can be done */
2297 int i;
2298 for( i = 0; i < p_sys->i_es; i++ )
2300 es_out_id_t *p_es = p_sys->es[i];
2301 if( p_es->fmt.i_cat == VIDEO_ES )
2302 break;
2304 if( i >= p_sys->i_es )
2305 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
2307 p_sys->b_active = i_mode != ES_OUT_MODE_NONE;
2308 p_sys->i_mode = i_mode;
2310 /* Reapply policy mode */
2311 for( int i = 0; i < p_sys->i_es; i++ )
2313 if( EsIsSelected( p_sys->es[i] ) )
2314 EsUnselect( out, p_sys->es[i],
2315 p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2317 for( int i = 0; i < p_sys->i_es; i++ )
2318 EsOutSelect( out, p_sys->es[i], false );
2319 if( i_mode == ES_OUT_MODE_END )
2320 EsOutTerminate( out );
2321 return VLC_SUCCESS;
2324 case ES_OUT_SET_ES:
2325 case ES_OUT_RESTART_ES:
2327 #define IGNORE_ES DATA_ES
2328 es_out_id_t *es = va_arg( args, es_out_id_t * );
2330 enum es_format_category_e i_cat;
2331 if( es == NULL )
2332 i_cat = UNKNOWN_ES;
2333 else if( es == es_cat + AUDIO_ES )
2334 i_cat = AUDIO_ES;
2335 else if( es == es_cat + VIDEO_ES )
2336 i_cat = VIDEO_ES;
2337 else if( es == es_cat + SPU_ES )
2338 i_cat = SPU_ES;
2339 else
2340 i_cat = IGNORE_ES;
2342 for( int i = 0; i < p_sys->i_es; i++ )
2344 if( i_cat == IGNORE_ES )
2346 if( es == p_sys->es[i] )
2348 if( i_query == ES_OUT_RESTART_ES && p_sys->es[i]->p_dec )
2350 EsDestroyDecoder( out, p_sys->es[i] );
2351 EsCreateDecoder( out, p_sys->es[i] );
2353 else if( i_query == ES_OUT_SET_ES )
2355 EsOutSelect( out, es, true );
2357 break;
2360 else
2362 if( i_cat == UNKNOWN_ES || p_sys->es[i]->fmt.i_cat == i_cat )
2364 if( EsIsSelected( p_sys->es[i] ) )
2366 if( i_query == ES_OUT_RESTART_ES )
2368 if( p_sys->es[i]->p_dec )
2370 EsDestroyDecoder( out, p_sys->es[i] );
2371 EsCreateDecoder( out, p_sys->es[i] );
2374 else
2376 EsUnselect( out, p_sys->es[i],
2377 p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2383 return VLC_SUCCESS;
2385 case ES_OUT_STOP_ALL_ES:
2387 int *selected_es = vlc_alloc(p_sys->i_es + 1, sizeof(int));
2388 if (!selected_es)
2389 return VLC_ENOMEM;
2390 selected_es[0] = p_sys->i_es;
2391 for( int i = 0; i < p_sys->i_es; i++ )
2393 if( EsIsSelected( p_sys->es[i] ) )
2395 EsDestroyDecoder( out, p_sys->es[i] );
2396 selected_es[i + 1] = p_sys->es[i]->i_id;
2398 else
2399 selected_es[i + 1] = -1;
2401 *va_arg( args, void **) = selected_es;
2402 return VLC_SUCCESS;
2404 case ES_OUT_START_ALL_ES:
2406 int *selected_es = va_arg( args, void * );
2407 int count = selected_es[0];
2408 for( int i = 0; i < count; ++i )
2410 int i_id = selected_es[i + 1];
2411 if( i_id != -1 )
2413 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2414 EsCreateDecoder( out, p_es );
2417 free(selected_es);
2418 return VLC_SUCCESS;
2421 case ES_OUT_SET_ES_DEFAULT:
2423 es_out_id_t *es = va_arg( args, es_out_id_t * );
2425 if( es == NULL )
2427 /*p_sys->i_default_video_id = -1;*/
2428 /*p_sys->i_default_audio_id = -1;*/
2429 p_sys->sub.i_demux_id = -1;
2431 else if( es == es_cat + AUDIO_ES )
2433 /*p_sys->i_default_video_id = -1;*/
2435 else if( es == es_cat + VIDEO_ES )
2437 /*p_sys->i_default_audio_id = -1;*/
2439 else if( es == es_cat + SPU_ES )
2441 p_sys->sub.i_demux_id = -1;
2443 else
2445 /*if( es->fmt.i_cat == VIDEO_ES )
2446 p_sys->i_default_video_id = es->i_id;
2447 else
2448 if( es->fmt.i_cat == AUDIO_ES )
2449 p_sys->i_default_audio_id = es->i_id;
2450 else*/
2451 if( es->fmt.i_cat == SPU_ES )
2452 p_sys->sub.i_demux_id = es->i_id;
2454 return VLC_SUCCESS;
2457 case ES_OUT_SET_PCR:
2458 case ES_OUT_SET_GROUP_PCR:
2460 es_out_pgrm_t *p_pgrm = NULL;
2461 int i_group = 0;
2462 int64_t i_pcr;
2464 /* Search program */
2465 if( i_query == ES_OUT_SET_PCR )
2467 p_pgrm = p_sys->p_pgrm;
2468 if( !p_pgrm )
2469 p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
2471 else
2473 i_group = va_arg( args, int );
2474 p_pgrm = EsOutProgramFind( out, i_group );
2476 if( !p_pgrm )
2477 return VLC_EGENERIC;
2479 i_pcr = va_arg( args, int64_t );
2480 if( i_pcr == VLC_TS_INVALID )
2482 msg_Err( p_sys->p_input, "Invalid PCR value in ES_OUT_SET_(GROUP_)PCR !" );
2483 return VLC_EGENERIC;
2486 /* TODO do not use mdate() but proper stream acquisition date */
2487 bool b_late;
2488 input_clock_Update( p_pgrm->p_input_clock, VLC_OBJECT(p_sys->p_input),
2489 &b_late,
2490 input_priv(p_sys->p_input)->b_can_pace_control || p_sys->b_buffering,
2491 EsOutIsExtraBufferingAllowed( out ),
2492 i_pcr, mdate() );
2494 if( !p_sys->p_pgrm )
2495 return VLC_SUCCESS;
2497 if( p_sys->b_buffering )
2499 /* Check buffering state on master clock update */
2500 EsOutDecodersStopBuffering( out, false );
2502 else if( p_pgrm == p_sys->p_pgrm )
2504 if( b_late && ( !input_priv(p_sys->p_input)->p_sout ||
2505 !input_priv(p_sys->p_input)->b_out_pace_control ) )
2507 const mtime_t i_pts_delay_base = p_sys->i_pts_delay - p_sys->i_pts_jitter;
2508 mtime_t i_pts_delay = input_clock_GetJitter( p_pgrm->p_input_clock );
2510 /* Avoid dangerously high value */
2511 const mtime_t i_jitter_max = INT64_C(1000) * var_InheritInteger( p_sys->p_input, "clock-jitter" );
2512 if( i_pts_delay > __MIN( i_pts_delay_base + i_jitter_max, INPUT_PTS_DELAY_MAX ) )
2514 msg_Err( p_sys->p_input,
2515 "ES_OUT_SET_(GROUP_)PCR is called too late (jitter of %d ms ignored)",
2516 (int)(i_pts_delay - i_pts_delay_base) / 1000 );
2517 i_pts_delay = p_sys->i_pts_delay;
2519 /* reset clock */
2520 for( int i = 0; i < p_sys->i_pgrm; i++ )
2521 input_clock_Reset( p_sys->pgrm[i]->p_input_clock );
2523 else
2525 msg_Err( p_sys->p_input,
2526 "ES_OUT_SET_(GROUP_)PCR is called too late (pts_delay increased to %d ms)",
2527 (int)(i_pts_delay/1000) );
2529 /* Force a rebufferization when we are too late */
2531 /* It is not really good, as we throw away already buffered data
2532 * TODO have a mean to correctly reenter bufferization */
2533 es_out_Control( out, ES_OUT_RESET_PCR );
2536 es_out_SetJitter( out, i_pts_delay_base, i_pts_delay - i_pts_delay_base, p_sys->i_cr_average );
2539 return VLC_SUCCESS;
2542 case ES_OUT_RESET_PCR:
2543 msg_Dbg( p_sys->p_input, "ES_OUT_RESET_PCR called" );
2544 EsOutChangePosition( out );
2545 return VLC_SUCCESS;
2547 case ES_OUT_SET_GROUP:
2549 int i = va_arg( args, int );
2550 for( int j = 0; j < p_sys->i_pgrm; j++ )
2552 es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
2553 if( p_pgrm->i_id == i )
2555 EsOutProgramSelect( out, p_pgrm );
2556 return VLC_SUCCESS;
2559 return VLC_EGENERIC;
2562 case ES_OUT_SET_ES_FMT:
2564 /* This ain't pretty but is need by some demuxers (eg. Ogg )
2565 * to update the p_extra data */
2566 es_out_id_t *es = va_arg( args, es_out_id_t * );
2567 es_format_t *p_fmt = va_arg( args, es_format_t * );
2568 if( es == NULL )
2569 return VLC_EGENERIC;
2571 es_format_Clean( &es->fmt );
2572 es_format_Copy( &es->fmt, p_fmt );
2574 if( es->p_dec )
2576 EsDestroyDecoder( out, es );
2577 EsCreateDecoder( out, es );
2580 return VLC_SUCCESS;
2583 case ES_OUT_SET_ES_SCRAMBLED_STATE:
2585 es_out_id_t *es = va_arg( args, es_out_id_t * );
2586 bool b_scrambled = (bool)va_arg( args, int );
2588 if( !es->b_scrambled != !b_scrambled )
2590 es->b_scrambled = b_scrambled;
2591 EsOutProgramUpdateScrambled( out, es->p_pgrm );
2593 return VLC_SUCCESS;
2596 case ES_OUT_SET_NEXT_DISPLAY_TIME:
2598 const int64_t i_date = va_arg( args, int64_t );
2600 if( i_date < 0 )
2601 return VLC_EGENERIC;
2603 p_sys->i_preroll_end = i_date;
2605 return VLC_SUCCESS;
2607 case ES_OUT_SET_GROUP_META:
2609 int i_group = va_arg( args, int );
2610 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
2612 EsOutProgramMeta( out, i_group, p_meta );
2613 return VLC_SUCCESS;
2615 case ES_OUT_SET_GROUP_EPG:
2617 int i_group = va_arg( args, int );
2618 const vlc_epg_t *p_epg = va_arg( args, const vlc_epg_t * );
2620 EsOutProgramEpg( out, i_group, p_epg );
2621 return VLC_SUCCESS;
2623 case ES_OUT_SET_GROUP_EPG_EVENT:
2625 int i_group = va_arg( args, int );
2626 const vlc_epg_event_t *p_evt = va_arg( args, const vlc_epg_event_t * );
2628 EsOutProgramEpgEvent( out, i_group, p_evt );
2629 return VLC_SUCCESS;
2631 case ES_OUT_SET_EPG_TIME:
2633 int64_t i64 = va_arg( args, int64_t );
2635 EsOutEpgTime( out, i64 );
2636 return VLC_SUCCESS;
2639 case ES_OUT_DEL_GROUP:
2641 int i_group = va_arg( args, int );
2643 return EsOutProgramDel( out, i_group );
2646 case ES_OUT_SET_META:
2648 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
2650 EsOutGlobalMeta( out, p_meta );
2651 return VLC_SUCCESS;
2654 case ES_OUT_GET_WAKE_UP:
2656 mtime_t *pi_wakeup = va_arg( args, mtime_t* );
2657 *pi_wakeup = EsOutGetWakeup( out );
2658 return VLC_SUCCESS;
2661 case ES_OUT_SET_ES_BY_ID:
2662 case ES_OUT_RESTART_ES_BY_ID:
2663 case ES_OUT_SET_ES_DEFAULT_BY_ID:
2665 const int i_id = va_arg( args, int );
2666 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2667 int i_new_query = 0;
2669 switch( i_query )
2671 case ES_OUT_SET_ES_BY_ID: i_new_query = ES_OUT_SET_ES; break;
2672 case ES_OUT_RESTART_ES_BY_ID: i_new_query = ES_OUT_RESTART_ES; break;
2673 case ES_OUT_SET_ES_DEFAULT_BY_ID: i_new_query = ES_OUT_SET_ES_DEFAULT; break;
2674 default:
2675 vlc_assert_unreachable();
2677 /* TODO if the lock is made non recursive it should be changed */
2678 int i_ret = es_out_Control( out, i_new_query, p_es );
2680 /* Clean up vout after user action (in active mode only).
2681 * FIXME it does not work well with multiple video windows */
2682 if( p_sys->b_active )
2683 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
2684 return i_ret;
2687 case ES_OUT_GET_ES_OBJECTS_BY_ID:
2689 const int i_id = va_arg( args, int );
2690 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2691 if( !p_es )
2692 return VLC_EGENERIC;
2694 vlc_object_t **pp_decoder = va_arg( args, vlc_object_t ** );
2695 vout_thread_t **pp_vout = va_arg( args, vout_thread_t ** );
2696 audio_output_t **pp_aout = va_arg( args, audio_output_t ** );
2697 if( p_es->p_dec )
2699 if( pp_decoder )
2700 *pp_decoder = vlc_object_hold( p_es->p_dec );
2701 input_DecoderGetObjects( p_es->p_dec, pp_vout, pp_aout );
2703 else
2705 if( pp_decoder )
2706 *pp_decoder = NULL;
2707 if( pp_vout )
2708 *pp_vout = NULL;
2709 if( pp_aout )
2710 *pp_aout = NULL;
2712 return VLC_SUCCESS;
2715 case ES_OUT_GET_BUFFERING:
2717 bool *pb = va_arg( args, bool* );
2718 *pb = p_sys->b_buffering;
2719 return VLC_SUCCESS;
2722 case ES_OUT_GET_EMPTY:
2724 bool *pb = va_arg( args, bool* );
2725 *pb = EsOutDecodersIsEmpty( out );
2726 return VLC_SUCCESS;
2729 case ES_OUT_SET_DELAY:
2731 const int i_cat = va_arg( args, int );
2732 const mtime_t i_delay = va_arg( args, mtime_t );
2733 EsOutSetDelay( out, i_cat, i_delay );
2734 return VLC_SUCCESS;
2737 case ES_OUT_SET_RECORD_STATE:
2739 bool b = va_arg( args, int );
2740 return EsOutSetRecord( out, b );
2743 case ES_OUT_SET_PAUSE_STATE:
2745 const bool b_source_paused = (bool)va_arg( args, int );
2746 const bool b_paused = (bool)va_arg( args, int );
2747 const mtime_t i_date = va_arg( args, mtime_t );
2749 assert( !b_source_paused == !b_paused );
2750 EsOutChangePause( out, b_paused, i_date );
2752 return VLC_SUCCESS;
2755 case ES_OUT_SET_RATE:
2757 const int i_src_rate = va_arg( args, int );
2758 const int i_rate = va_arg( args, int );
2760 assert( i_src_rate == i_rate );
2761 EsOutChangeRate( out, i_rate );
2763 return VLC_SUCCESS;
2766 case ES_OUT_SET_TIME:
2768 const mtime_t i_date = va_arg( args, mtime_t );
2770 assert( i_date == -1 );
2771 EsOutChangePosition( out );
2773 return VLC_SUCCESS;
2776 case ES_OUT_SET_FRAME_NEXT:
2777 EsOutFrameNext( out );
2778 return VLC_SUCCESS;
2780 case ES_OUT_SET_TIMES:
2782 double f_position = va_arg( args, double );
2783 mtime_t i_time = va_arg( args, mtime_t );
2784 mtime_t i_length = va_arg( args, mtime_t );
2786 input_SendEventLength( p_sys->p_input, i_length );
2788 if( !p_sys->b_buffering )
2790 mtime_t i_delay;
2792 /* Fix for buffering delay */
2793 if( !input_priv(p_sys->p_input)->p_sout ||
2794 !input_priv(p_sys->p_input)->b_out_pace_control )
2795 i_delay = EsOutGetBuffering( out );
2796 else
2797 i_delay = 0;
2799 i_time -= i_delay;
2800 if( i_time < 0 )
2801 i_time = 0;
2803 if( i_length > 0 )
2804 f_position -= (double)i_delay / i_length;
2805 if( f_position < 0 )
2806 f_position = 0;
2808 input_SendEventPosition( p_sys->p_input, f_position, i_time );
2810 return VLC_SUCCESS;
2812 case ES_OUT_SET_JITTER:
2814 mtime_t i_pts_delay = va_arg( args, mtime_t );
2815 mtime_t i_pts_jitter = va_arg( args, mtime_t );
2816 int i_cr_average = va_arg( args, int );
2818 bool b_change_clock =
2819 i_pts_delay + i_pts_jitter != p_sys->i_pts_delay ||
2820 i_cr_average != p_sys->i_cr_average;
2822 assert( i_pts_jitter >= 0 );
2823 p_sys->i_pts_delay = i_pts_delay + i_pts_jitter;
2824 p_sys->i_pts_jitter = i_pts_jitter;
2825 p_sys->i_cr_average = i_cr_average;
2827 for( int i = 0; i < p_sys->i_pgrm && b_change_clock; i++ )
2828 input_clock_SetJitter( p_sys->pgrm[i]->p_input_clock,
2829 i_pts_delay + i_pts_jitter, i_cr_average );
2830 return VLC_SUCCESS;
2833 case ES_OUT_GET_PCR_SYSTEM:
2835 if( p_sys->b_buffering )
2836 return VLC_EGENERIC;
2838 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
2839 if( !p_pgrm )
2840 return VLC_EGENERIC;
2842 mtime_t *pi_system = va_arg( args, mtime_t *);
2843 mtime_t *pi_delay = va_arg( args, mtime_t *);
2844 input_clock_GetSystemOrigin( p_pgrm->p_input_clock, pi_system, pi_delay );
2845 return VLC_SUCCESS;
2848 case ES_OUT_MODIFY_PCR_SYSTEM:
2850 if( p_sys->b_buffering )
2851 return VLC_EGENERIC;
2853 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
2854 if( !p_pgrm )
2855 return VLC_EGENERIC;
2857 const bool b_absolute = va_arg( args, int );
2858 const mtime_t i_system = va_arg( args, mtime_t );
2859 input_clock_ChangeSystemOrigin( p_pgrm->p_input_clock, b_absolute, i_system );
2860 return VLC_SUCCESS;
2862 case ES_OUT_SET_EOS:
2864 for (int i = 0; i < p_sys->i_es; i++) {
2865 es_out_id_t *id = p_sys->es[i];
2866 if (id->p_dec != NULL)
2867 input_DecoderDrain(id->p_dec);
2869 return VLC_SUCCESS;
2872 case ES_OUT_POST_SUBNODE:
2874 input_item_node_t *node = va_arg(args, input_item_node_t *);
2875 input_item_node_PostAndDelete(node);
2876 return VLC_SUCCESS;
2879 default:
2880 msg_Err( p_sys->p_input, "unknown query 0x%x in %s", i_query,
2881 __func__ );
2882 return VLC_EGENERIC;
2885 static int EsOutControl( es_out_t *out, int i_query, va_list args )
2887 es_out_sys_t *p_sys = out->p_sys;
2888 int i_ret;
2890 vlc_mutex_lock( &p_sys->lock );
2891 i_ret = EsOutControlLocked( out, i_query, args );
2892 vlc_mutex_unlock( &p_sys->lock );
2894 return i_ret;
2897 /****************************************************************************
2898 * LanguageGetName: try to expend iso639 into plain name
2899 ****************************************************************************/
2900 static char *LanguageGetName( const char *psz_code )
2902 const iso639_lang_t *pl;
2904 if( psz_code == NULL || !strcmp( psz_code, "und" ) )
2906 return strdup( "" );
2909 if( strlen( psz_code ) == 2 )
2911 pl = GetLang_1( psz_code );
2913 else if( strlen( psz_code ) == 3 )
2915 pl = GetLang_2B( psz_code );
2916 if( !strcmp( pl->psz_iso639_1, "??" ) )
2918 pl = GetLang_2T( psz_code );
2921 else
2923 char *lang = LanguageGetCode( psz_code );
2924 pl = GetLang_1( lang );
2925 free( lang );
2928 if( !strcmp( pl->psz_iso639_1, "??" ) )
2930 return strdup( psz_code );
2932 else
2934 return strdup( vlc_gettext(pl->psz_eng_name) );
2938 /* Get a 2 char code */
2939 static char *LanguageGetCode( const char *psz_lang )
2941 const iso639_lang_t *pl;
2943 if( psz_lang == NULL || *psz_lang == '\0' )
2944 return strdup("??");
2946 for( pl = p_languages; pl->psz_eng_name != NULL; pl++ )
2948 if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
2949 !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
2950 !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
2951 !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
2952 return strdup( pl->psz_iso639_1 );
2955 return strdup("??");
2958 static char **LanguageSplit( const char *psz_langs )
2960 char *psz_dup;
2961 char *psz_parser;
2962 char **ppsz = NULL;
2963 int i_psz = 0;
2965 if( psz_langs == NULL ) return NULL;
2967 psz_parser = psz_dup = strdup(psz_langs);
2969 while( psz_parser && *psz_parser )
2971 char *psz;
2972 char *psz_code;
2974 psz = strchr(psz_parser, ',' );
2975 if( psz ) *psz++ = '\0';
2977 if( !strcmp( psz_parser, "any" ) )
2979 TAB_APPEND( i_psz, ppsz, strdup("any") );
2981 else if( !strcmp( psz_parser, "none" ) )
2983 TAB_APPEND( i_psz, ppsz, strdup("none") );
2985 else
2987 psz_code = LanguageGetCode( psz_parser );
2988 if( strcmp( psz_code, "??" ) )
2990 TAB_APPEND( i_psz, ppsz, psz_code );
2992 else
2994 free( psz_code );
2998 psz_parser = psz;
3001 if( i_psz )
3003 TAB_APPEND( i_psz, ppsz, NULL );
3006 free( psz_dup );
3007 return ppsz;
3010 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang )
3012 if( !ppsz_langs || !psz_lang )
3013 return -1;
3015 for( int i = 0; ppsz_langs[i]; i++ )
3017 if( !strcasecmp( ppsz_langs[i], psz_lang ) ||
3018 ( !strcasecmp( ppsz_langs[i], "any" ) && strcasecmp( psz_lang, "none") ) )
3019 return i;
3020 if( !strcasecmp( ppsz_langs[i], "none" ) )
3021 break;
3024 return -1;
3027 /****************************************************************************
3028 * EsOutUpdateInfo:
3029 * - add meta info to the playlist item
3030 ****************************************************************************/
3031 static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t *fmt, const vlc_meta_t *p_meta )
3033 es_out_sys_t *p_sys = out->p_sys;
3034 input_thread_t *p_input = p_sys->p_input;
3035 const es_format_t *p_fmt_es = &es->fmt;
3037 if( es->fmt.i_cat == fmt->i_cat )
3039 es_format_t update = *fmt;
3040 update.i_id = es->i_meta_id;
3041 update.i_codec = es->fmt.i_codec;
3042 update.i_original_fourcc = es->fmt.i_original_fourcc;
3044 /* Update infos that could have been lost by the decoder (no need to
3045 * dup them since input_item_UpdateTracksInfo() will do it). */
3046 if (update.psz_language == NULL)
3047 update.psz_language = es->fmt.psz_language;
3048 if (update.psz_description == NULL)
3049 update.psz_description = es->fmt.psz_description;
3050 if (update.i_cat == SPU_ES)
3052 if (update.subs.psz_encoding == NULL)
3053 update.subs.psz_encoding = es->fmt.subs.psz_encoding;
3055 if (update.i_extra_languages == 0)
3057 assert(update.p_extra_languages == NULL);
3058 update.i_extra_languages = es->fmt.i_extra_languages;
3059 update.p_extra_languages = es->fmt.p_extra_languages;
3062 /* No need to update codec specific data */
3063 update.i_extra = 0;
3064 update.p_extra = NULL;
3066 input_item_UpdateTracksInfo(input_GetItem(p_input), &update);
3069 /* Create category */
3070 char* psz_cat = EsInfoCategoryName( es );
3072 if( unlikely( !psz_cat ) )
3073 return;
3075 info_category_t* p_cat = info_category_New( psz_cat );
3077 free( psz_cat );
3079 if( unlikely( !p_cat ) )
3080 return;
3082 /* Add information */
3083 if( es->i_meta_id != es->i_id )
3084 info_category_AddInfo( p_cat, _("Original ID"),
3085 "%d", es->i_id );
3087 const vlc_fourcc_t i_codec_fourcc = ( p_fmt_es->i_original_fourcc )?
3088 p_fmt_es->i_original_fourcc : p_fmt_es->i_codec;
3089 const char *psz_codec_description =
3090 vlc_fourcc_GetDescription( p_fmt_es->i_cat, i_codec_fourcc );
3091 if( psz_codec_description && *psz_codec_description )
3092 info_category_AddInfo( p_cat, _("Codec"), "%s (%.4s)",
3093 psz_codec_description, (char*)&i_codec_fourcc );
3094 else if ( i_codec_fourcc != VLC_FOURCC(0,0,0,0) )
3095 info_category_AddInfo( p_cat, _("Codec"), "%.4s",
3096 (char*)&i_codec_fourcc );
3098 if( es->psz_language && *es->psz_language )
3099 info_category_AddInfo( p_cat, _("Language"), "%s",
3100 es->psz_language );
3101 if( fmt->psz_description && *fmt->psz_description )
3102 info_category_AddInfo( p_cat, _("Description"), "%s",
3103 fmt->psz_description );
3105 switch( fmt->i_cat )
3107 case AUDIO_ES:
3108 info_category_AddInfo( p_cat, _("Type"), _("Audio") );
3110 if( fmt->audio.i_physical_channels )
3111 info_category_AddInfo( p_cat, _("Channels"), "%s",
3112 vlc_gettext( aout_FormatPrintChannels( &fmt->audio ) ) );
3114 if( fmt->audio.i_rate != 0 )
3116 info_category_AddInfo( p_cat, _("Sample rate"), _("%u Hz"),
3117 fmt->audio.i_rate );
3118 /* FIXME that should be removed or improved ! (used by text/strings.c) */
3119 var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate );
3122 unsigned int i_bitspersample = fmt->audio.i_bitspersample;
3123 if( i_bitspersample == 0 )
3124 i_bitspersample = aout_BitsPerSample( p_fmt_es->i_codec );
3125 if( i_bitspersample != 0 )
3126 info_category_AddInfo( p_cat, _("Bits per sample"), "%u",
3127 i_bitspersample );
3129 if( fmt->i_bitrate != 0 )
3131 info_category_AddInfo( p_cat, _("Bitrate"), _("%u kb/s"),
3132 fmt->i_bitrate / 1000 );
3133 /* FIXME that should be removed or improved ! (used by text/strings.c) */
3134 var_SetInteger( p_input, "bit-rate", fmt->i_bitrate );
3136 for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
3138 const audio_replay_gain_t *p_rg = &fmt->audio_replay_gain;
3139 if( !p_rg->pb_gain[i] )
3140 continue;
3141 const char *psz_name;
3142 if( i == AUDIO_REPLAY_GAIN_TRACK )
3143 psz_name = _("Track replay gain");
3144 else
3145 psz_name = _("Album replay gain");
3146 info_category_AddInfo( p_cat, psz_name, _("%.2f dB"),
3147 p_rg->pf_gain[i] );
3149 break;
3151 case VIDEO_ES:
3152 info_category_AddInfo( p_cat, _("Type"), _("Video") );
3154 if( fmt->video.i_visible_width > 0 &&
3155 fmt->video.i_visible_height > 0 )
3156 info_category_AddInfo( p_cat, _("Video resolution"), "%ux%u",
3157 fmt->video.i_visible_width,
3158 fmt->video.i_visible_height);
3160 if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
3161 info_category_AddInfo( p_cat, _("Buffer dimensions"), "%ux%u",
3162 fmt->video.i_width, fmt->video.i_height );
3164 if( fmt->video.i_frame_rate > 0 &&
3165 fmt->video.i_frame_rate_base > 0 )
3167 if( fmt->video.i_frame_rate_base == 1 )
3168 info_category_AddInfo( p_cat, _("Frame rate"), "%u",
3169 fmt->video.i_frame_rate );
3170 else
3171 info_category_AddInfo( p_cat, _("Frame rate"), "%.6f",
3172 (double)fmt->video.i_frame_rate
3173 / (double)fmt->video.i_frame_rate_base );
3175 if( fmt->i_codec != p_fmt_es->i_codec )
3177 const char *psz_chroma_description =
3178 vlc_fourcc_GetDescription( VIDEO_ES, fmt->i_codec );
3179 if( psz_chroma_description )
3180 info_category_AddInfo( p_cat, _("Decoded format"), "%s",
3181 psz_chroma_description );
3184 static const char orient_names[][13] = {
3185 N_("Top left"), N_("Left top"),
3186 N_("Right bottom"), N_("Top right"),
3187 N_("Bottom left"), N_("Bottom right"),
3188 N_("Left bottom"), N_("Right top"),
3190 info_category_AddInfo( p_cat, _("Orientation"), "%s",
3191 vlc_gettext(orient_names[fmt->video.orientation]) );
3193 if( fmt->video.primaries != COLOR_PRIMARIES_UNDEF )
3195 static const char primaries_names[][32] = {
3196 [COLOR_PRIMARIES_UNDEF] = N_("Undefined"),
3197 [COLOR_PRIMARIES_BT601_525] =
3198 N_("ITU-R BT.601 (525 lines, 60 Hz)"),
3199 [COLOR_PRIMARIES_BT601_625] =
3200 N_("ITU-R BT.601 (625 lines, 50 Hz)"),
3201 [COLOR_PRIMARIES_BT709] = "ITU-R BT.709",
3202 [COLOR_PRIMARIES_BT2020] = "ITU-R BT.2020",
3203 [COLOR_PRIMARIES_DCI_P3] = "DCI/P3 D65",
3204 [COLOR_PRIMARIES_BT470_M] = "ITU-R BT.470 M",
3206 static_assert(ARRAY_SIZE(primaries_names) == COLOR_PRIMARIES_MAX+1,
3207 "Color primiaries table mismatch");
3208 info_category_AddInfo( p_cat, _("Color primaries"), "%s",
3209 vlc_gettext(primaries_names[fmt->video.primaries]) );
3211 if( fmt->video.transfer != TRANSFER_FUNC_UNDEF )
3213 static const char func_names[][20] = {
3214 [TRANSFER_FUNC_UNDEF] = N_("Undefined"),
3215 [TRANSFER_FUNC_LINEAR] = N_("Linear"),
3216 [TRANSFER_FUNC_SRGB] = "sRGB",
3217 [TRANSFER_FUNC_BT470_BG] = "ITU-R BT.470 BG",
3218 [TRANSFER_FUNC_BT470_M] = "ITU-R BT.470 M",
3219 [TRANSFER_FUNC_BT709] = "ITU-R BT.709",
3220 [TRANSFER_FUNC_SMPTE_ST2084] = "SMPTE ST2084",
3221 [TRANSFER_FUNC_SMPTE_240] = "SMPTE 240M",
3222 [TRANSFER_FUNC_HLG] = N_("Hybrid Log-Gamma"),
3224 static_assert(ARRAY_SIZE(func_names) == TRANSFER_FUNC_MAX+1,
3225 "Transfer functions table mismatch");
3226 info_category_AddInfo( p_cat, _("Color transfer function"), "%s",
3227 vlc_gettext(func_names[fmt->video.transfer]) );
3229 if( fmt->video.space != COLOR_SPACE_UNDEF )
3231 static const char space_names[][16] = {
3232 [COLOR_SPACE_UNDEF] = N_("Undefined"),
3233 [COLOR_SPACE_BT601] = "ITU-R BT.601",
3234 [COLOR_SPACE_BT709] = "ITU-R BT.709",
3235 [COLOR_SPACE_BT2020] = "ITU-R BT.2020",
3237 static_assert(ARRAY_SIZE(space_names) == COLOR_SPACE_MAX+1,
3238 "Color space table mismatch");
3239 info_category_AddInfo( p_cat, _("Color space"), _("%s %s Range"),
3240 vlc_gettext(space_names[fmt->video.space]),
3241 vlc_gettext(fmt->video.b_color_range_full
3242 ? N_("Full") : N_("Limited")) );
3244 if( fmt->video.chroma_location != CHROMA_LOCATION_UNDEF )
3246 static const char c_loc_names[][16] = {
3247 [CHROMA_LOCATION_UNDEF] = N_("Undefined"),
3248 [CHROMA_LOCATION_LEFT] = N_("Left"),
3249 [CHROMA_LOCATION_CENTER] = N_("Center"),
3250 [CHROMA_LOCATION_TOP_LEFT] = N_("Top Left"),
3251 [CHROMA_LOCATION_TOP_CENTER] = N_("Top Center"),
3252 [CHROMA_LOCATION_BOTTOM_LEFT] =N_("Bottom Left"),
3253 [CHROMA_LOCATION_BOTTOM_CENTER] = N_("Bottom Center"),
3255 static_assert(ARRAY_SIZE(c_loc_names) == CHROMA_LOCATION_MAX+1,
3256 "Chroma location table mismatch");
3257 info_category_AddInfo( p_cat, _("Chroma location"), "%s",
3258 vlc_gettext(c_loc_names[fmt->video.chroma_location]) );
3260 if( fmt->video.projection_mode != PROJECTION_MODE_RECTANGULAR )
3262 const char *psz_loc_name = NULL;
3263 switch (fmt->video.projection_mode)
3265 case PROJECTION_MODE_RECTANGULAR:
3266 psz_loc_name = N_("Rectangular");
3267 break;
3268 case PROJECTION_MODE_EQUIRECTANGULAR:
3269 psz_loc_name = N_("Equirectangular");
3270 break;
3271 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
3272 psz_loc_name = N_("Cubemap");
3273 break;
3274 default:
3275 vlc_assert_unreachable();
3276 break;
3278 info_category_AddInfo( p_cat, _("Projection"), "%s",
3279 vlc_gettext(psz_loc_name) );
3281 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Yaw"),
3282 "%.2f", fmt->video.pose.yaw );
3283 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Pitch"),
3284 "%.2f", fmt->video.pose.pitch );
3285 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Roll"),
3286 "%.2f", fmt->video.pose.roll );
3287 info_category_AddInfo( p_cat,
3288 vlc_pgettext("ViewPoint", "Field of view"),
3289 "%.2f", fmt->video.pose.fov );
3291 if ( fmt->video.mastering.max_luminance )
3293 info_category_AddInfo( p_cat, _("Max. luminance"), "%.4f cd/m²",
3294 fmt->video.mastering.max_luminance / 10000.f );
3296 if ( fmt->video.mastering.min_luminance )
3298 info_category_AddInfo( p_cat, _("Min. luminance"), "%.4f cd/m²",
3299 fmt->video.mastering.min_luminance / 10000.f );
3301 if ( fmt->video.mastering.primaries[4] &&
3302 fmt->video.mastering.primaries[5] )
3304 float x = (float)fmt->video.mastering.primaries[4] / 50000.f;
3305 float y = (float)fmt->video.mastering.primaries[5] / 50000.f;
3306 info_category_AddInfo( p_cat, _("Primary R"), "x=%.4f y=%.4f", x, y );
3308 if ( fmt->video.mastering.primaries[0] &&
3309 fmt->video.mastering.primaries[1] )
3311 float x = (float)fmt->video.mastering.primaries[0] / 50000.f;
3312 float y = (float)fmt->video.mastering.primaries[1] / 50000.f;
3313 info_category_AddInfo( p_cat, _("Primary G"), "x=%.4f y=%.4f", x, y );
3315 if ( fmt->video.mastering.primaries[2] &&
3316 fmt->video.mastering.primaries[3] )
3318 float x = (float)fmt->video.mastering.primaries[2] / 50000.f;
3319 float y = (float)fmt->video.mastering.primaries[3] / 50000.f;
3320 info_category_AddInfo( p_cat, _("Primary B"), "x=%.4f y=%.4f", x, y );
3322 if ( fmt->video.mastering.white_point[0] &&
3323 fmt->video.mastering.white_point[1] )
3325 float x = (float)fmt->video.mastering.white_point[0] / 50000.f;
3326 float y = (float)fmt->video.mastering.white_point[1] / 50000.f;
3327 info_category_AddInfo( p_cat, _("White point"), "x=%.4f y=%.4f", x, y );
3329 if ( fmt->video.lighting.MaxCLL )
3331 info_category_AddInfo( p_cat, "MaxCLL", "%d cd/m²",
3332 fmt->video.lighting.MaxCLL );
3334 if ( fmt->video.lighting.MaxFALL )
3336 info_category_AddInfo( p_cat, "MaxFALL", "%d cd/m²",
3337 fmt->video.lighting.MaxFALL );
3339 break;
3341 case SPU_ES:
3342 info_category_AddInfo( p_cat, _("Type"), _("Subtitle") );
3343 break;
3345 default:
3346 break;
3349 /* Append generic meta */
3350 if( p_meta )
3352 char **ppsz_all_keys = vlc_meta_CopyExtraNames( p_meta );
3353 for( int i = 0; ppsz_all_keys && ppsz_all_keys[i]; i++ )
3355 char *psz_key = ppsz_all_keys[i];
3356 const char *psz_value = vlc_meta_GetExtra( p_meta, psz_key );
3358 if( psz_value )
3359 info_category_AddInfo( p_cat, vlc_gettext(psz_key), "%s",
3360 vlc_gettext(psz_value) );
3361 free( psz_key );
3363 free( ppsz_all_keys );
3365 /* */
3366 input_Control( p_input, INPUT_REPLACE_INFOS, p_cat );
3369 static void EsDeleteInfo( es_out_t *out, es_out_id_t *es )
3371 char* psz_info_category;
3373 if( likely( psz_info_category = EsInfoCategoryName( es ) ) )
3375 es_out_sys_t *p_sys = out->p_sys;
3376 input_Control( p_sys->p_input, INPUT_DEL_INFO,
3377 psz_info_category, NULL );
3379 free( psz_info_category );