demux: mp4: set bitmap mask when possible
[vlc.git] / src / input / es_out.c
blob22033131af2790cc989148cac582eefa7f5df638
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>
42 #include <vlc_list.h>
44 #include "input_internal.h"
45 #include "../clock/input_clock.h"
46 #include "decoder.h"
47 #include "es_out.h"
48 #include "event.h"
49 #include "info.h"
50 #include "item.h"
52 #include "../stream_output/stream_output.h"
54 #include <vlc_iso_lang.h>
55 /* FIXME we should find a better way than including that */
56 #include "../text/iso-639_def.h"
58 /*****************************************************************************
59 * Local prototypes
60 *****************************************************************************/
61 typedef struct
63 /* Program ID */
64 int i_id;
66 /* Number of es for this pgrm */
67 int i_es;
69 bool b_selected;
70 bool b_scrambled;
72 /* Clock for this program */
73 input_clock_t *p_input_clock;
75 vlc_meta_t *p_meta;
76 struct vlc_list node;
77 } es_out_pgrm_t;
80 /**
81 * Opaque structure representing an ES (Elementary Stream) track.
83 * This structure is propagated via the vlc_input_event_es event
85 struct vlc_es_id_t
87 int i_id;
88 enum es_format_category_e i_cat;
91 struct es_out_id_t
93 vlc_es_id_t id;
95 /* ES ID */
96 es_out_pgrm_t *p_pgrm;
98 /* */
99 bool b_scrambled;
100 bool b_forced; /* if true, bypass variables when selecting this track */
102 /* Channel in the track type */
103 int i_channel;
105 vlc_atomic_rc_t rc;
107 size_t i_pos; /* position, used to get the title of the track */
108 es_format_t fmt; /* input fmt from the demuxer */
109 es_format_t fmt_out; /* updated fmt (by the decoder) */
110 char *psz_language;
111 char *psz_language_code;
112 char *psz_title;
113 bool b_terminated;
115 decoder_t *p_dec;
116 decoder_t *p_dec_record;
118 /* Fields for Video with CC */
119 struct
121 vlc_fourcc_t type;
122 uint64_t i_bitmap; /* channels bitmap */
123 es_out_id_t *pp_es[64]; /* a max of 64 chans for CEA708 */
124 } cc;
126 /* Field for CC track from a master video */
127 es_out_id_t *p_master;
129 /* ID for the meta data */
130 int i_meta_id;
132 struct vlc_list node;
134 vlc_mouse_event mouse_event_cb;
135 void* mouse_event_userdata;
138 typedef struct
140 int i_count; /* es count */
141 es_out_id_t *p_main_es; /* current main es */
142 enum es_out_policy_e e_policy;
144 /* Parameters used for es selection */
145 bool b_autoselect; /* if we want to select an es when no user prefs */
146 int i_id; /* es id as set by es fmt.id */
147 int i_demux_id; /* same as previous, demuxer set default value */
148 int i_channel; /* es number in creation order */
149 char **ppsz_language;
150 } es_out_es_props_t;
152 typedef struct
154 input_thread_t *p_input;
156 /* */
157 vlc_mutex_t lock;
159 /* all programs */
160 struct vlc_list programs;
161 es_out_pgrm_t *p_pgrm; /* Master program */
163 /* all es */
164 int i_id;
165 struct vlc_list es;
166 struct vlc_list es_slaves; /* Dynamically created es on regular es selection */
168 /* mode gestion */
169 bool b_active;
170 int i_mode;
172 es_out_es_props_t video, audio, sub;
174 /* es/group to select */
175 int i_group_id;
177 /* delay */
178 vlc_tick_t i_audio_delay;
179 vlc_tick_t i_spu_delay;
181 /* Clock configuration */
182 vlc_tick_t i_pts_delay;
183 vlc_tick_t i_pts_jitter;
184 int i_cr_average;
185 int i_rate;
187 /* */
188 bool b_paused;
189 vlc_tick_t i_pause_date;
191 /* Current preroll */
192 vlc_tick_t i_preroll_end;
194 /* Used for buffering */
195 bool b_buffering;
196 vlc_tick_t i_buffering_extra_initial;
197 vlc_tick_t i_buffering_extra_stream;
198 vlc_tick_t i_buffering_extra_system;
200 /* Record */
201 sout_instance_t *p_sout_record;
203 /* Used only to limit debugging output */
204 int i_prev_stream_level;
206 es_out_t out;
207 } es_out_sys_t;
209 static void EsOutDelLocked( es_out_t *, es_out_id_t * );
210 static void EsOutDel ( es_out_t *, es_out_id_t * );
212 static void EsOutTerminate( es_out_t * );
213 static void EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force );
214 static void EsOutUpdateInfo( es_out_t *, es_out_id_t *es, const vlc_meta_t * );
215 static int EsOutSetRecord( es_out_t *, bool b_record );
217 static bool EsIsSelected( es_out_id_t *es );
218 static void EsOutSelectEs( es_out_t *out, es_out_id_t *es );
219 static void EsOutDeleteInfoEs( es_out_t *, es_out_id_t *es );
220 static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update );
221 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
222 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date );
223 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date );
224 static void EsOutProgramsChangeRate( es_out_t *out );
225 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
226 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta );
227 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_progmeta );
228 static int EsOutEsUpdateFmt(es_out_t *out, es_out_id_t *es, const es_format_t *fmt);
230 static char *LanguageGetName( const char *psz_code );
231 static char *LanguageGetCode( const char *psz_lang );
232 static char **LanguageSplit( const char *psz_langs );
233 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang );
235 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
236 static char *EsInfoCategoryName( es_out_id_t* es );
238 static inline int EsOutGetClosedCaptionsChannel( const es_format_t *p_fmt )
240 int i_channel;
241 if( p_fmt->i_codec == VLC_CODEC_CEA608 && p_fmt->subs.cc.i_channel < 4 )
242 i_channel = p_fmt->subs.cc.i_channel;
243 else if( p_fmt->i_codec == VLC_CODEC_CEA708 && p_fmt->subs.cc.i_channel < 64 )
244 i_channel = p_fmt->subs.cc.i_channel;
245 else
246 i_channel = -1;
247 return i_channel;
250 #define foreach_es_then_es_slaves( pos ) \
251 for( int fetes_i=0; fetes_i<2; fetes_i++ ) \
252 vlc_list_foreach( pos, (!fetes_i ? &p_sys->es : &p_sys->es_slaves), node )
255 /*****************************************************************************
256 * Es category specific structs
257 *****************************************************************************/
258 static es_out_es_props_t * GetPropsByCat( es_out_sys_t *p_sys, int i_cat )
260 switch( i_cat )
262 case AUDIO_ES:
263 return &p_sys->audio;
264 case SPU_ES:
265 return &p_sys->sub;
266 case VIDEO_ES:
267 return &p_sys->video;
269 return NULL;
272 static void EsOutPropsCleanup( es_out_es_props_t *p_props )
274 if( p_props->ppsz_language )
276 for( int i = 0; p_props->ppsz_language[i]; i++ )
277 free( p_props->ppsz_language[i] );
278 free( p_props->ppsz_language );
282 static void EsOutPropsInit( es_out_es_props_t *p_props,
283 bool autoselect,
284 input_thread_t *p_input,
285 enum es_out_policy_e e_default_policy,
286 const char *psz_trackidvar,
287 const char *psz_trackvar,
288 const char *psz_langvar,
289 const char *psz_debug )
291 p_props->e_policy = e_default_policy;
292 p_props->i_count = 0;
293 p_props->b_autoselect = autoselect;
294 p_props->i_id = (psz_trackidvar) ? var_GetInteger( p_input, psz_trackidvar ): -1;
295 p_props->i_channel = (psz_trackvar) ? var_GetInteger( p_input, psz_trackvar ): -1;
296 p_props->i_demux_id = -1;
297 p_props->p_main_es = NULL;
299 if( !input_priv(p_input)->b_preparsing && psz_langvar )
301 char *psz_string = var_GetString( p_input, psz_langvar );
302 p_props->ppsz_language = LanguageSplit( psz_string );
303 if( p_props->ppsz_language )
305 for( int i = 0; p_props->ppsz_language[i]; i++ )
306 msg_Dbg( p_input, "selected %s language[%d] %s",
307 psz_debug, i, p_props->ppsz_language[i] );
309 free( psz_string );
313 static const struct es_out_callbacks es_out_cbs;
315 /*****************************************************************************
316 * input_EsOutNew:
317 *****************************************************************************/
318 es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
320 es_out_sys_t *p_sys = calloc( 1, sizeof( *p_sys ) );
321 if( !p_sys )
322 return NULL;
324 p_sys->out.cbs = &es_out_cbs;
326 vlc_mutex_init( &p_sys->lock );
327 p_sys->p_input = p_input;
329 p_sys->b_active = false;
330 p_sys->i_mode = ES_OUT_MODE_NONE;
332 vlc_list_init(&p_sys->programs);
333 vlc_list_init(&p_sys->es);
334 vlc_list_init(&p_sys->es_slaves);
336 /* */
337 EsOutPropsInit( &p_sys->video, true, p_input, ES_OUT_ES_POLICY_SIMULTANEOUS,
338 "video-track-id", "video-track", NULL, NULL );
339 EsOutPropsInit( &p_sys->audio, true, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,
340 "audio-track-id", "audio-track", "audio-language", "audio" );
341 EsOutPropsInit( &p_sys->sub, false, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,
342 "sub-track-id", "sub-track", "sub-language", "sub" );
344 p_sys->i_group_id = var_GetInteger( p_input, "program" );
346 p_sys->i_pause_date = -1;
348 p_sys->i_rate = i_rate;
350 p_sys->b_buffering = true;
351 p_sys->i_preroll_end = -1;
352 p_sys->i_prev_stream_level = -1;
354 return &p_sys->out;
357 /*****************************************************************************
359 *****************************************************************************/
360 static void EsTerminate(es_out_id_t *es)
362 vlc_list_remove(&es->node);
364 es->b_terminated = true;
367 static char *EsGetTitle( es_out_id_t *es )
369 const es_format_t *fmt = &es->fmt;
370 char *title;
372 /* Take care of the ES description */
373 if( fmt->psz_description && *fmt->psz_description )
375 if( es->psz_language && *es->psz_language )
377 if( asprintf( &title, "%s - [%s]", fmt->psz_description,
378 es->psz_language ) == -1 )
379 title = NULL;
381 else
382 title = strdup( fmt->psz_description );
384 else
386 if( es->psz_language && *es->psz_language )
388 if( asprintf( &title, "%s %zu - [%s]", _("Track"),
389 es->i_pos, es->psz_language ) == -1 )
390 title = NULL;
392 else
394 if( asprintf( &title, "%s %zu", _("Track"), es->i_pos ) == -1 )
395 title = NULL;
399 return title;
402 static void EsRelease(es_out_id_t *es)
404 if (vlc_atomic_rc_dec(&es->rc))
406 free(es->psz_title);
407 free(es->psz_language);
408 free(es->psz_language_code);
409 es_format_Clean(&es->fmt);
410 free(es);
414 static void EsHold(es_out_id_t *es)
416 vlc_atomic_rc_inc(&es->rc);
419 static void EsOutDelete( es_out_t *out )
421 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
423 assert(vlc_list_is_empty(&p_sys->es));
424 assert(vlc_list_is_empty(&p_sys->es_slaves));
425 assert(vlc_list_is_empty(&p_sys->programs));
426 assert(p_sys->p_pgrm == NULL);
427 EsOutPropsCleanup( &p_sys->audio );
428 EsOutPropsCleanup( &p_sys->sub );
430 vlc_mutex_destroy( &p_sys->lock );
432 free( p_sys );
435 static void EsOutTerminate( es_out_t *out )
437 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
438 es_out_id_t *es;
440 if( p_sys->p_sout_record )
441 EsOutSetRecord( out, false );
443 foreach_es_then_es_slaves(es)
445 if (es->p_dec != NULL)
446 input_DecoderDelete(es->p_dec);
448 EsTerminate(es);
449 EsRelease(es);
452 /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
453 es_out_pgrm_t *p_pgrm;
454 vlc_list_foreach(p_pgrm, &p_sys->programs, node)
456 input_clock_Delete( p_pgrm->p_input_clock );
457 if( p_pgrm->p_meta )
458 vlc_meta_Delete( p_pgrm->p_meta );
460 vlc_list_remove(&p_pgrm->node);
461 input_SendEventProgramDel( p_sys->p_input, p_pgrm->i_id );
462 free( p_pgrm );
465 p_sys->p_pgrm = NULL;
467 input_item_SetEpgOffline( input_priv(p_sys->p_input)->p_item );
468 input_SendEventMetaEpg( p_sys->p_input );
471 static vlc_tick_t EsOutGetWakeup( es_out_t *out )
473 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
474 input_thread_t *p_input = p_sys->p_input;
476 if( !p_sys->p_pgrm )
477 return 0;
479 /* We do not have a wake up date if the input cannot have its speed
480 * controlled or sout is imposing its own or while buffering
482 * FIXME for !input_priv(p_input)->b_can_pace_control a wake-up time is still needed
483 * to avoid too heavy buffering */
484 if( !input_priv(p_input)->b_can_pace_control ||
485 input_priv(p_input)->b_out_pace_control ||
486 p_sys->b_buffering )
487 return 0;
489 return input_clock_GetWakeup( p_sys->p_pgrm->p_input_clock );
492 static es_out_id_t es_cat[DATA_ES];
494 static es_out_id_t *EsOutGetFromID( es_out_t *out, int i_id )
496 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
497 es_out_id_t *es;
499 if( i_id < 0 )
501 /* Special HACK, -i_id is the cat of the stream */
502 return es_cat - i_id;
505 foreach_es_then_es_slaves(es)
506 if (es->fmt.i_id == i_id)
507 return es;
508 return NULL;
511 static es_out_id_t *EsOutGetSelectedCat( es_out_t *out,
512 enum es_format_category_e cat )
514 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
515 es_out_id_t *es;
517 foreach_es_then_es_slaves( es )
518 if( es->fmt.i_cat == cat && EsIsSelected( es ) )
519 return es;
520 return NULL;
523 static bool EsOutDecodersIsEmpty( es_out_t *out )
525 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
526 es_out_id_t *es;
528 if( p_sys->b_buffering && p_sys->p_pgrm )
530 EsOutDecodersStopBuffering( out, true );
531 if( p_sys->b_buffering )
532 return true;
535 foreach_es_then_es_slaves(es)
537 if( es->p_dec && !input_DecoderIsEmpty( es->p_dec ) )
538 return false;
539 if( es->p_dec_record && !input_DecoderIsEmpty( es->p_dec_record ) )
540 return false;
542 return true;
545 static void EsOutSetDelay( es_out_t *out, int i_cat, vlc_tick_t i_delay )
547 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
548 es_out_id_t *es;
550 if( i_cat == AUDIO_ES )
551 p_sys->i_audio_delay = i_delay;
552 else if( i_cat == SPU_ES )
553 p_sys->i_spu_delay = i_delay;
555 foreach_es_then_es_slaves(es)
556 EsOutDecoderChangeDelay(out, es);
559 static int EsOutSetRecord( es_out_t *out, bool b_record )
561 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
562 input_thread_t *p_input = p_sys->p_input;
563 es_out_id_t *p_es;
565 assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
567 if( b_record )
569 char *psz_path = var_CreateGetNonEmptyString( p_input, "input-record-path" );
570 if( !psz_path )
572 if( var_CountChoices( p_input, "video-es" ) )
573 psz_path = config_GetUserDir( VLC_VIDEOS_DIR );
574 else if( var_CountChoices( p_input, "audio-es" ) )
575 psz_path = config_GetUserDir( VLC_MUSIC_DIR );
576 else
577 psz_path = config_GetUserDir( VLC_DOWNLOAD_DIR );
580 char *psz_sout = NULL; // TODO conf
582 if( !psz_sout && psz_path )
584 char *psz_file = input_CreateFilename( p_input, NULL, psz_path,
585 INPUT_RECORD_PREFIX, NULL );
586 if( psz_file )
588 char* psz_file_esc = config_StringEscape( psz_file );
589 if ( psz_file_esc )
591 if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file_esc ) < 0 )
592 psz_sout = NULL;
593 free( psz_file_esc );
595 free( psz_file );
598 free( psz_path );
600 if( !psz_sout )
601 return VLC_EGENERIC;
603 #ifdef ENABLE_SOUT
604 p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
605 #endif
606 free( psz_sout );
608 if( !p_sys->p_sout_record )
609 return VLC_EGENERIC;
611 vlc_list_foreach( p_es, &p_sys->es, node ) /* Only master es */
613 if( !p_es->p_dec )
614 continue;
616 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_input_clock, p_sys->p_sout_record );
617 if( p_es->p_dec_record && p_sys->b_buffering )
618 input_DecoderStartWait( p_es->p_dec_record );
621 else
623 vlc_list_foreach( p_es, &p_sys->es, node ) /* Only master es */
625 if( !p_es->p_dec_record )
626 continue;
628 input_DecoderDelete( p_es->p_dec_record );
629 p_es->p_dec_record = NULL;
631 #ifdef ENABLE_SOUT
632 sout_DeleteInstance( p_sys->p_sout_record );
633 #endif
634 p_sys->p_sout_record = NULL;
637 return VLC_SUCCESS;
639 static void EsOutChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date )
641 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
643 /* XXX the order is important */
644 if( b_paused )
646 EsOutDecodersChangePause( out, true, i_date );
647 EsOutProgramChangePause( out, true, i_date );
649 else
651 if( p_sys->i_buffering_extra_initial > 0 )
653 vlc_tick_t i_stream_start;
654 vlc_tick_t i_system_start;
655 vlc_tick_t i_stream_duration;
656 vlc_tick_t i_system_duration;
657 int i_ret;
658 i_ret = input_clock_GetState( p_sys->p_pgrm->p_input_clock,
659 &i_stream_start, &i_system_start,
660 &i_stream_duration, &i_system_duration );
661 if( !i_ret )
663 /* FIXME pcr != exactly what wanted */
664 const vlc_tick_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;
665 i_date -= i_used;
667 p_sys->i_buffering_extra_initial = 0;
668 p_sys->i_buffering_extra_stream = 0;
669 p_sys->i_buffering_extra_system = 0;
671 EsOutProgramChangePause( out, false, i_date );
672 EsOutDecodersChangePause( out, false, i_date );
674 EsOutProgramsChangeRate( out );
676 p_sys->b_paused = b_paused;
677 p_sys->i_pause_date = i_date;
680 static void EsOutChangeRate( es_out_t *out, int i_rate )
682 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
683 float rate = (float)i_rate / (float)INPUT_RATE_DEFAULT;
684 es_out_id_t *es;
686 p_sys->i_rate = i_rate;
687 EsOutProgramsChangeRate( out );
689 foreach_es_then_es_slaves(es)
690 if( es->p_dec != NULL )
691 input_DecoderChangeRate( es->p_dec, rate );
694 static void EsOutChangePosition( es_out_t *out )
696 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
697 es_out_id_t *p_es;
699 input_SendEventCache( p_sys->p_input, 0.0 );
701 foreach_es_then_es_slaves(p_es)
702 if( p_es->p_dec != NULL )
704 input_DecoderFlush( p_es->p_dec );
705 if( !p_sys->b_buffering )
707 input_DecoderStartWait( p_es->p_dec );
708 if( p_es->p_dec_record != NULL )
709 input_DecoderStartWait( p_es->p_dec_record );
713 es_out_pgrm_t *pgrm;
714 vlc_list_foreach(pgrm, &p_sys->programs, node)
715 input_clock_Reset(pgrm->p_input_clock);
717 p_sys->b_buffering = true;
718 p_sys->i_buffering_extra_initial = 0;
719 p_sys->i_buffering_extra_stream = 0;
720 p_sys->i_buffering_extra_system = 0;
721 p_sys->i_preroll_end = -1;
722 p_sys->i_prev_stream_level = -1;
727 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
729 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
730 es_out_id_t *p_es;
732 vlc_tick_t i_stream_start;
733 vlc_tick_t i_system_start;
734 vlc_tick_t i_stream_duration;
735 vlc_tick_t i_system_duration;
736 if (input_clock_GetState( p_sys->p_pgrm->p_input_clock,
737 &i_stream_start, &i_system_start,
738 &i_stream_duration, &i_system_duration ))
739 return;
741 vlc_tick_t i_preroll_duration = 0;
742 if( p_sys->i_preroll_end >= 0 )
743 i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
745 const vlc_tick_t i_buffering_duration = p_sys->i_pts_delay +
746 i_preroll_duration +
747 p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
749 if( i_stream_duration <= i_buffering_duration && !b_forced )
751 double f_level;
752 if (i_buffering_duration == 0)
753 f_level = 0;
754 else
755 f_level = __MAX( (double)i_stream_duration / i_buffering_duration, 0 );
756 input_SendEventCache( p_sys->p_input, f_level );
758 int i_level = (int)(100 * f_level);
759 if( p_sys->i_prev_stream_level != i_level )
761 msg_Dbg( p_sys->p_input, "Buffering %d%%", i_level );
762 p_sys->i_prev_stream_level = i_level;
765 return;
767 input_SendEventCache( p_sys->p_input, 1.0 );
769 msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
770 (int)MS_FROM_VLC_TICK(i_stream_duration), (int)MS_FROM_VLC_TICK(i_system_duration) );
771 p_sys->b_buffering = false;
772 p_sys->i_preroll_end = -1;
773 p_sys->i_prev_stream_level = -1;
775 if( p_sys->i_buffering_extra_initial > 0 )
777 /* FIXME wrong ? */
778 return;
781 const vlc_tick_t i_decoder_buffering_start = vlc_tick_now();
782 foreach_es_then_es_slaves(p_es)
784 if( !p_es->p_dec || p_es->fmt.i_cat == SPU_ES )
785 continue;
786 input_DecoderWait( p_es->p_dec );
787 if( p_es->p_dec_record )
788 input_DecoderWait( p_es->p_dec_record );
791 msg_Dbg( p_sys->p_input, "Decoder wait done in %d ms",
792 (int)MS_FROM_VLC_TICK(vlc_tick_now() - i_decoder_buffering_start) );
794 /* Here is a good place to destroy unused vout with every demuxer */
795 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
797 /* */
798 const vlc_tick_t i_wakeup_delay = VLC_TICK_FROM_MS(10); /* FIXME CLEANUP thread wake up time*/
799 const vlc_tick_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : vlc_tick_now();
801 input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_input_clock, true,
802 i_current_date + i_wakeup_delay - i_buffering_duration );
804 foreach_es_then_es_slaves(p_es)
806 if( !p_es->p_dec )
807 continue;
809 input_DecoderStopWait( p_es->p_dec );
810 if( p_es->p_dec_record )
811 input_DecoderStopWait( p_es->p_dec_record );
814 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date )
816 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
817 es_out_id_t *es;
819 /* Pause decoders first */
820 foreach_es_then_es_slaves(es)
821 if( es->p_dec )
823 input_DecoderChangePause( es->p_dec, b_paused, i_date );
824 if( es->p_dec_record )
825 input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
829 static bool EsOutIsExtraBufferingAllowed( es_out_t *out )
831 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
832 es_out_id_t *p_es;
834 size_t i_size = 0;
835 foreach_es_then_es_slaves(p_es)
837 if( p_es->p_dec )
838 i_size += input_DecoderGetFifoSize( p_es->p_dec );
839 if( p_es->p_dec_record )
840 i_size += input_DecoderGetFifoSize( p_es->p_dec_record );
842 //msg_Info( out, "----- EsOutIsExtraBufferingAllowed =% 5d KiB -- ", i_size / 1024 );
844 /* TODO maybe we want to be able to tune it ? */
845 #if defined(OPTIMIZE_MEMORY)
846 const size_t i_level_high = 512*1024; /* 0.5 MiB */
847 #else
848 const size_t i_level_high = 10*1024*1024; /* 10 MiB */
849 #endif
850 return i_size < i_level_high;
853 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date )
855 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
856 es_out_pgrm_t *pgrm;
858 vlc_list_foreach(pgrm, &p_sys->programs, node)
859 input_clock_ChangePause(pgrm->p_input_clock, b_paused, i_date);
862 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
864 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
866 vlc_tick_t i_delay;
867 if( p_es->fmt.i_cat == AUDIO_ES )
868 i_delay = p_sys->i_audio_delay;
869 else if( p_es->fmt.i_cat == SPU_ES )
870 i_delay = p_sys->i_spu_delay;
871 else
872 return;
874 if( p_es->p_dec )
875 input_DecoderChangeDelay( p_es->p_dec, i_delay );
876 if( p_es->p_dec_record )
877 input_DecoderChangeDelay( p_es->p_dec_record, i_delay );
879 static void EsOutProgramsChangeRate( es_out_t *out )
881 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
882 es_out_pgrm_t *pgrm;
884 vlc_list_foreach(pgrm, &p_sys->programs, node)
885 input_clock_ChangeRate(pgrm->p_input_clock, p_sys->i_rate);
888 static void EsOutFrameNext( es_out_t *out )
890 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
891 es_out_id_t *p_es_video = NULL, *p_es;
893 if( p_sys->b_buffering )
895 msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" );
896 return;
899 assert( p_sys->b_paused );
901 foreach_es_then_es_slaves(p_es)
902 if( p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec && !p_es_video /* nested loop */ )
904 p_es_video = p_es;
905 break;
908 if( !p_es_video )
910 msg_Warn( p_sys->p_input, "No video track selected, ignoring 'frame next'" );
911 return;
914 vlc_tick_t i_duration;
915 input_DecoderFrameNext( p_es_video->p_dec, &i_duration );
917 msg_Dbg( p_sys->p_input, "EsOutFrameNext consummed %d ms", (int)MS_FROM_VLC_TICK(i_duration) );
919 if( i_duration <= 0 )
920 i_duration = VLC_TICK_FROM_MS(40);
922 /* FIXME it is not a clean way ? */
923 if( p_sys->i_buffering_extra_initial <= 0 )
925 vlc_tick_t i_stream_start;
926 vlc_tick_t i_system_start;
927 vlc_tick_t i_stream_duration;
928 vlc_tick_t i_system_duration;
929 int i_ret;
931 i_ret = input_clock_GetState( p_sys->p_pgrm->p_input_clock,
932 &i_stream_start, &i_system_start,
933 &i_stream_duration, &i_system_duration );
934 if( i_ret )
935 return;
937 p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->i_pts_delay; /* FIXME < 0 ? */
938 p_sys->i_buffering_extra_system =
939 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
942 const int i_rate = input_clock_GetRate( p_sys->p_pgrm->p_input_clock );
944 p_sys->b_buffering = true;
945 p_sys->i_buffering_extra_system += i_duration;
946 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial +
947 ( p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial ) *
948 INPUT_RATE_DEFAULT / i_rate;
950 p_sys->i_preroll_end = -1;
951 p_sys->i_prev_stream_level = -1;
953 static vlc_tick_t EsOutGetBuffering( es_out_t *out )
955 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
956 vlc_tick_t i_stream_duration, i_system_start;
958 if( !p_sys->p_pgrm )
959 return 0;
960 else
962 vlc_tick_t i_stream_start, i_system_duration;
964 if( input_clock_GetState( p_sys->p_pgrm->p_input_clock,
965 &i_stream_start, &i_system_start,
966 &i_stream_duration, &i_system_duration ) )
967 return 0;
970 vlc_tick_t i_delay;
972 if( p_sys->b_buffering && p_sys->i_buffering_extra_initial <= 0 )
974 i_delay = i_stream_duration;
976 else
978 vlc_tick_t i_system_duration;
980 if( p_sys->b_paused )
982 i_system_duration = p_sys->i_pause_date - i_system_start;
983 if( p_sys->i_buffering_extra_initial > 0 )
984 i_system_duration += p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
986 else
988 i_system_duration = vlc_tick_now() - i_system_start;
991 const vlc_tick_t i_consumed = i_system_duration * INPUT_RATE_DEFAULT / p_sys->i_rate - i_stream_duration;
992 i_delay = p_sys->i_pts_delay - i_consumed;
994 if( i_delay < 0 )
995 return 0;
996 return i_delay;
999 static void EsOutSendEsEvent(es_out_t *out, es_out_id_t *es, int action)
1001 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1002 input_thread_t *p_input = p_sys->p_input;
1004 if (action == VLC_INPUT_ES_ADDED)
1006 input_thread_private_t *priv = input_priv(p_input);
1007 /*FIXME: see input_SlaveSourceAdd */
1008 priv->i_last_es_id = es->fmt.i_id;
1009 priv->i_last_es_cat = es->fmt.i_cat;
1011 input_SendEventEs(p_input, &(struct vlc_input_event_es) {
1012 .action = action,
1013 .id = &es->id,
1014 .title = es->psz_title ? es->psz_title : "",
1015 .fmt = es->fmt_out.i_cat != UNKNOWN_ES ? &es->fmt_out : &es->fmt,
1019 static bool EsOutIsProgramVisible( es_out_t *out, int i_group )
1021 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1022 return p_sys->i_group_id == 0 || p_sys->i_group_id == i_group;
1025 /* EsOutProgramSelect:
1026 * Select a program and update the object variable
1028 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
1030 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1031 input_thread_t *p_input = p_sys->p_input;
1032 es_out_id_t *es;
1034 if( p_sys->p_pgrm == p_pgrm )
1035 return; /* Nothing to do */
1037 if( p_sys->p_pgrm )
1039 es_out_pgrm_t *old = p_sys->p_pgrm;
1041 msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
1043 foreach_es_then_es_slaves(es)
1045 if (es->p_pgrm == old && EsIsSelected(es)
1046 && p_sys->i_mode != ES_OUT_MODE_ALL)
1047 EsOutUnselectEs(out, es, true);
1048 if (es->p_pgrm == old)
1049 EsOutSendEsEvent( out, es, VLC_INPUT_ES_DELETED );
1052 p_sys->audio.p_main_es = NULL;
1053 p_sys->video.p_main_es = NULL;
1054 p_sys->sub.p_main_es = NULL;
1057 msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
1059 /* Mark it selected */
1060 p_pgrm->b_selected = true;
1062 /* Switch master stream */
1063 p_sys->p_pgrm = p_pgrm;
1065 /* Update "program" */
1066 input_SendEventProgramSelect( p_input, p_pgrm->i_id );
1068 /* Update "es-*" */
1069 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, p_pgrm->b_scrambled );
1071 foreach_es_then_es_slaves(es)
1073 if (es->p_pgrm == p_sys->p_pgrm)
1075 EsOutSendEsEvent(out, es, VLC_INPUT_ES_ADDED);
1076 EsOutUpdateInfo(out, es, NULL);
1079 EsOutSelect(out, es, false);
1082 /* Ensure the correct running EPG table is selected */
1083 input_item_ChangeEPGSource( input_priv(p_input)->p_item, p_pgrm->i_id );
1085 /* Update now playing */
1086 if( p_pgrm->p_meta )
1088 input_item_SetESNowPlaying( input_priv(p_input)->p_item,
1089 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) );
1090 input_item_SetPublisher( input_priv(p_input)->p_item,
1091 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Publisher ) );
1092 input_item_SetTitle( input_priv(p_input)->p_item,
1093 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1094 input_SendEventMeta( p_input );
1095 /* FIXME: we probably want to replace every input meta */
1099 /* EsOutAddProgram:
1100 * Add a program
1102 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
1104 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1105 input_thread_t *p_input = p_sys->p_input;
1107 es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
1108 if( !p_pgrm )
1109 return NULL;
1111 /* Init */
1112 p_pgrm->i_id = i_group;
1113 p_pgrm->i_es = 0;
1114 p_pgrm->b_selected = false;
1115 p_pgrm->b_scrambled = false;
1116 p_pgrm->p_meta = NULL;
1117 p_pgrm->p_input_clock = input_clock_New( p_sys->i_rate );
1118 if( !p_pgrm->p_input_clock )
1120 free( p_pgrm );
1121 return NULL;
1123 if( p_sys->b_paused )
1124 input_clock_ChangePause( p_pgrm->p_input_clock, p_sys->b_paused, p_sys->i_pause_date );
1125 input_clock_SetJitter( p_pgrm->p_input_clock, p_sys->i_pts_delay, p_sys->i_cr_average );
1127 /* Append it */
1128 vlc_list_append(&p_pgrm->node, &p_sys->programs);
1130 /* Update "program" variable */
1131 if( EsOutIsProgramVisible( out, i_group ) )
1132 input_SendEventProgramAdd( p_input, i_group, NULL );
1134 if( i_group == p_sys->i_group_id || ( !p_sys->p_pgrm && p_sys->i_group_id == 0 ) )
1135 EsOutProgramSelect( out, p_pgrm );
1137 return p_pgrm;
1140 /* EsOutDelProgram:
1141 * Delete a program
1143 static int EsOutProgramDel( es_out_t *out, int i_group )
1145 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1146 input_thread_t *p_input = p_sys->p_input;
1147 es_out_pgrm_t *p_pgrm = NULL, *pgrm;
1149 vlc_list_foreach(pgrm, &p_sys->programs, node)
1150 if (pgrm->i_id == i_group)
1152 p_pgrm = pgrm;
1153 break;
1156 if( p_pgrm == NULL )
1157 return VLC_EGENERIC;
1159 if( p_pgrm->i_es )
1161 msg_Dbg( p_input, "can't delete program %d which still has %i ES",
1162 i_group, p_pgrm->i_es );
1163 return VLC_EGENERIC;
1166 vlc_list_remove(&p_pgrm->node);
1168 /* If program is selected we need to unselect it */
1169 if( p_sys->p_pgrm == p_pgrm )
1170 p_sys->p_pgrm = NULL;
1172 input_clock_Delete( p_pgrm->p_input_clock );
1174 if( p_pgrm->p_meta )
1175 vlc_meta_Delete( p_pgrm->p_meta );
1176 free( p_pgrm );
1178 /* Update "program" variable */
1179 input_SendEventProgramDel( p_input, i_group );
1181 return VLC_SUCCESS;
1184 /* EsOutProgramFind
1186 static es_out_pgrm_t *EsOutProgramFind( es_out_t *p_out, int i_group )
1188 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1189 es_out_pgrm_t *pgrm;
1191 vlc_list_foreach(pgrm, &p_sys->programs, node)
1192 if (pgrm->i_id == i_group)
1193 return pgrm;
1195 return EsOutProgramAdd( p_out, i_group );
1198 /* EsOutProgramMeta:
1200 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
1202 char *psz = NULL;
1203 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1205 if( asprintf( &psz, _("%s [%s %d]"), vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ),
1206 _("Program"), p_pgrm->i_id ) == -1 )
1207 return NULL;
1209 else
1211 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1212 return NULL;
1214 return psz;
1217 static char *EsOutProgramGetProgramName( es_out_pgrm_t *p_pgrm )
1219 char *psz = NULL;
1220 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1222 return strdup( vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1224 else
1226 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1227 return NULL;
1229 return psz;
1232 static char *EsInfoCategoryName( es_out_id_t* es )
1234 char *psz_category;
1236 if( asprintf( &psz_category, _("Stream %d"), es->i_meta_id ) == -1 )
1237 return NULL;
1239 return psz_category;
1242 static void EsOutProgramMeta( es_out_t *out, int i_group, const vlc_meta_t *p_meta )
1244 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1245 es_out_pgrm_t *p_pgrm;
1246 input_thread_t *p_input = p_sys->p_input;
1247 input_item_t *p_item = input_priv(p_input)->p_item;
1248 const char *psz_title = NULL;
1249 const char *psz_provider = NULL;
1250 int i;
1251 bool b_has_new_infos = false;
1253 msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
1255 /* Check against empty meta data (empty for what we handle) */
1256 if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
1257 !vlc_meta_Get( p_meta, vlc_meta_ESNowPlaying) &&
1258 !vlc_meta_Get( p_meta, vlc_meta_Publisher) )
1260 return;
1263 if( i_group < 0 )
1265 EsOutGlobalMeta( out, p_meta );
1266 return;
1269 /* Find program */
1270 if( !EsOutIsProgramVisible( out, i_group ) )
1271 return;
1272 p_pgrm = EsOutProgramFind( out, i_group );
1273 if( !p_pgrm )
1274 return;
1276 if( p_pgrm->p_meta )
1278 const char *psz_current_title = vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title );
1279 const char *psz_new_title = vlc_meta_Get( p_meta, vlc_meta_Title );
1280 if( (psz_current_title != NULL && psz_new_title != NULL)
1281 ? strcmp(psz_new_title, psz_current_title)
1282 : (psz_current_title != psz_new_title) )
1284 /* Remove old entries */
1285 char *psz_oldinfokey = EsOutProgramGetMetaName( p_pgrm );
1287 if( !input_item_DelInfo( p_item, psz_oldinfokey, NULL ) )
1288 b_has_new_infos = true;
1289 /* TODO update epg name ?
1290 * TODO update scrambled info name ? */
1291 free( psz_oldinfokey );
1293 vlc_meta_Delete( p_pgrm->p_meta );
1295 p_pgrm->p_meta = vlc_meta_New();
1296 if( p_pgrm->p_meta )
1297 vlc_meta_Merge( p_pgrm->p_meta, p_meta );
1299 if( p_sys->p_pgrm == p_pgrm )
1301 EsOutMeta( out, NULL, p_meta );
1303 /* */
1304 psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
1305 psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
1307 /* Update the description text of the program */
1308 if( psz_title && *psz_title )
1310 char *psz_text;
1311 if( psz_provider && *psz_provider )
1313 if( asprintf( &psz_text, "%s [%s]", psz_title, psz_provider ) < 0 )
1314 psz_text = NULL;
1316 else
1318 psz_text = strdup( psz_title );
1321 if( psz_text )
1323 input_SendEventProgramUpdated( p_input, i_group, psz_text );
1324 if( p_sys->p_pgrm == p_pgrm )
1325 input_SendEventProgramSelect( p_input, i_group );
1326 free( psz_text );
1330 /* */
1331 char **ppsz_all_keys = vlc_meta_CopyExtraNames(p_meta );
1333 info_category_t *p_cat = NULL;
1334 if( psz_provider || ( ppsz_all_keys[0] && *ppsz_all_keys[0] ) )
1336 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1337 if( psz_cat )
1338 p_cat = info_category_New( psz_cat );
1339 free( psz_cat );
1342 for( i = 0; ppsz_all_keys[i]; i++ )
1344 if( p_cat )
1345 info_category_AddInfo( p_cat, vlc_gettext(ppsz_all_keys[i]), "%s",
1346 vlc_meta_GetExtra( p_meta, ppsz_all_keys[i] ) );
1347 free( ppsz_all_keys[i] );
1349 free( ppsz_all_keys );
1351 if( psz_provider )
1353 if( p_sys->p_pgrm == p_pgrm )
1355 input_item_SetPublisher( input_priv(p_input)->p_item, psz_provider );
1356 input_SendEventMeta( p_input );
1358 if( p_cat )
1359 info_category_AddInfo( p_cat, vlc_meta_TypeToLocalizedString(vlc_meta_Publisher),
1360 "%s",psz_provider );
1362 if( p_cat )
1364 input_item_MergeInfos( p_item, p_cat );
1365 b_has_new_infos = true;
1367 if( !input_priv(p_input)->b_preparsing && b_has_new_infos )
1368 input_SendEventMetaInfo( p_input );
1371 static void EsOutProgramEpgEvent( es_out_t *out, int i_group, const vlc_epg_event_t *p_event )
1373 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1374 input_thread_t *p_input = p_sys->p_input;
1375 input_item_t *p_item = input_priv(p_input)->p_item;
1376 es_out_pgrm_t *p_pgrm;
1378 /* Find program */
1379 if( !EsOutIsProgramVisible( out, i_group ) )
1380 return;
1381 p_pgrm = EsOutProgramFind( out, i_group );
1382 if( !p_pgrm )
1383 return;
1385 input_item_SetEpgEvent( p_item, p_event );
1388 static void EsOutProgramEpg( es_out_t *out, int i_group, const vlc_epg_t *p_epg )
1390 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1391 input_thread_t *p_input = p_sys->p_input;
1392 input_item_t *p_item = input_priv(p_input)->p_item;
1393 es_out_pgrm_t *p_pgrm;
1394 char *psz_cat;
1396 /* Find program */
1397 if( !EsOutIsProgramVisible( out, i_group ) )
1398 return;
1399 p_pgrm = EsOutProgramFind( out, i_group );
1400 if( !p_pgrm )
1401 return;
1403 /* Update info */
1404 psz_cat = EsOutProgramGetMetaName( p_pgrm );
1405 msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, psz_cat );
1407 /* Merge EPG */
1408 vlc_epg_t epg;
1410 epg = *p_epg;
1411 epg.psz_name = EsOutProgramGetProgramName( p_pgrm );
1413 input_item_SetEpg( p_item, &epg, p_sys->p_pgrm && (p_epg->i_source_id == p_sys->p_pgrm->i_id) );
1414 input_SendEventMetaEpg( p_sys->p_input );
1416 free( epg.psz_name );
1418 /* Update now playing */
1419 if( p_epg->b_present && p_pgrm->p_meta &&
1420 ( p_epg->p_current || p_epg->i_event == 0 ) )
1422 vlc_meta_SetNowPlaying( p_pgrm->p_meta, NULL );
1425 vlc_mutex_lock( &p_item->lock );
1426 for( int i = 0; i < p_item->i_epg; i++ )
1428 const vlc_epg_t *p_tmp = p_item->pp_epg[i];
1430 if( p_tmp->b_present && p_tmp->i_source_id == p_pgrm->i_id )
1432 const char *psz_name = ( p_tmp->p_current ) ? p_tmp->p_current->psz_name : NULL;
1433 if( !p_pgrm->p_meta )
1434 p_pgrm->p_meta = vlc_meta_New();
1435 if( p_pgrm->p_meta )
1436 vlc_meta_Set( p_pgrm->p_meta, vlc_meta_ESNowPlaying, psz_name );
1437 break;
1440 vlc_mutex_unlock( &p_item->lock );
1442 /* Update selected program input info */
1443 if( p_pgrm == p_sys->p_pgrm )
1445 const char *psz_nowplaying = p_pgrm->p_meta ?
1446 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) : NULL;
1448 input_item_SetESNowPlaying( input_priv(p_input)->p_item, psz_nowplaying );
1449 input_SendEventMeta( p_input );
1451 const char *now_playing_tr =
1452 vlc_meta_TypeToLocalizedString(vlc_meta_ESNowPlaying);
1453 int ret;
1454 if( psz_nowplaying )
1455 ret = input_item_AddInfo( p_item, psz_cat, now_playing_tr,
1456 "%s", psz_nowplaying );
1457 else
1458 ret = input_item_DelInfo( p_item, psz_cat, now_playing_tr );
1460 if( ret == VLC_SUCCESS && !input_priv(p_input)->b_preparsing )
1461 input_SendEventMetaInfo( p_input );
1464 free( psz_cat );
1467 static void EsOutEpgTime( es_out_t *out, int64_t time )
1469 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1470 input_thread_t *p_input = p_sys->p_input;
1471 input_item_t *p_item = input_priv(p_input)->p_item;
1473 input_item_SetEpgTime( p_item, time );
1476 static void EsOutProgramUpdateScrambled( es_out_t *p_out, es_out_pgrm_t *p_pgrm )
1478 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1479 input_thread_t *p_input = p_sys->p_input;
1480 input_item_t *p_item = input_priv(p_input)->p_item;
1481 es_out_id_t *es;
1482 bool b_scrambled = false;
1484 vlc_list_foreach( es, &p_sys->es, node ) /* Only master es */
1485 if (es->p_pgrm == p_pgrm && es->b_scrambled)
1487 b_scrambled = true;
1488 break;
1491 if( !p_pgrm->b_scrambled == !b_scrambled )
1492 return;
1494 p_pgrm->b_scrambled = b_scrambled;
1495 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1497 int ret;
1498 if( b_scrambled )
1499 ret = input_item_AddInfo( p_item, psz_cat, _("Scrambled"), _("Yes") );
1500 else
1501 ret = input_item_DelInfo( p_item, psz_cat, _("Scrambled") );
1502 free( psz_cat );
1504 if( ret == VLC_SUCCESS && !input_priv(p_input)->b_preparsing )
1505 input_SendEventMetaInfo( p_input );
1506 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, b_scrambled );
1509 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_program_meta )
1511 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1512 input_thread_t *p_input = p_sys->p_input;
1513 input_item_t *p_item = input_GetItem( p_input );
1515 vlc_mutex_lock( &p_item->lock );
1516 if( p_meta )
1517 vlc_meta_Merge( p_item->p_meta, p_meta );
1518 vlc_mutex_unlock( &p_item->lock );
1520 /* Check program meta to not override GROUP_META values */
1521 if( p_meta && (!p_program_meta || vlc_meta_Get( p_program_meta, vlc_meta_Title ) == NULL) &&
1522 vlc_meta_Get( p_meta, vlc_meta_Title ) != NULL )
1523 input_item_SetName( p_item, vlc_meta_Get( p_meta, vlc_meta_Title ) );
1525 const char *psz_arturl = NULL;
1526 char *psz_alloc = NULL;
1528 if( p_program_meta )
1529 psz_arturl = vlc_meta_Get( p_program_meta, vlc_meta_ArtworkURL );
1530 if( psz_arturl == NULL && p_meta )
1531 psz_arturl = vlc_meta_Get( p_meta, vlc_meta_ArtworkURL );
1533 if( psz_arturl == NULL ) /* restore/favor previously set item art URL */
1534 psz_arturl = psz_alloc = input_item_GetArtURL( p_item );
1536 if( psz_arturl != NULL )
1537 input_item_SetArtURL( p_item, psz_arturl );
1539 if( psz_arturl != NULL && !strncmp( psz_arturl, "attachment://", 13 ) )
1540 { /* Clear art cover if streaming out.
1541 * FIXME: Why? Remove this when sout gets meta data support. */
1542 if( input_priv(p_input)->p_sout != NULL )
1543 input_item_SetArtURL( p_item, NULL );
1544 else
1545 input_ExtractAttachmentAndCacheArt( p_input, psz_arturl + 13 );
1547 free( psz_alloc );
1549 input_item_SetPreparsed( p_item, true );
1551 input_SendEventMeta( p_input );
1552 /* TODO handle sout meta ? */
1555 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta )
1557 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1558 EsOutMeta( p_out, p_meta,
1559 (p_sys->p_pgrm && p_sys->p_pgrm->p_meta) ? p_sys->p_pgrm->p_meta : NULL );
1562 static void EsOutUpdateEsLanguageTitle(es_out_id_t *es,
1563 const es_format_t *fmt)
1565 free( es->psz_title );
1566 free( es->psz_language );
1567 free( es->psz_language_code );
1569 es->psz_language = LanguageGetName( fmt->psz_language );
1570 es->psz_language_code = LanguageGetCode( fmt->psz_language );
1572 es->psz_title = EsGetTitle(es);
1575 static void EsOutFillEsFmt(es_out_t *out, es_format_t *fmt)
1577 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1578 input_thread_t *p_input = p_sys->p_input;
1580 switch( fmt->i_cat )
1582 case AUDIO_ES:
1584 fmt->i_codec = vlc_fourcc_GetCodecAudio( fmt->i_codec,
1585 fmt->audio.i_bitspersample );
1586 audio_replay_gain_t rg;
1587 memset( &rg, 0, sizeof(rg) );
1588 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
1589 vlc_audio_replay_gain_MergeFromMeta( &rg, input_priv(p_input)->p_item->p_meta );
1590 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1592 for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
1594 if( !fmt->audio_replay_gain.pb_peak[i] )
1596 fmt->audio_replay_gain.pb_peak[i] = rg.pb_peak[i];
1597 fmt->audio_replay_gain.pf_peak[i] = rg.pf_peak[i];
1599 if( !fmt->audio_replay_gain.pb_gain[i] )
1601 fmt->audio_replay_gain.pb_gain[i] = rg.pb_gain[i];
1602 fmt->audio_replay_gain.pf_gain[i] = rg.pf_gain[i];
1605 break;
1608 case VIDEO_ES:
1609 fmt->i_codec = vlc_fourcc_GetCodec( fmt->i_cat, fmt->i_codec );
1611 if( !fmt->video.i_visible_width || !fmt->video.i_visible_height )
1613 fmt->video.i_visible_width = fmt->video.i_width;
1614 fmt->video.i_visible_height = fmt->video.i_height;
1617 if( fmt->video.i_frame_rate && fmt->video.i_frame_rate_base )
1618 vlc_ureduce( &fmt->video.i_frame_rate,
1619 &fmt->video.i_frame_rate_base,
1620 fmt->video.i_frame_rate,
1621 fmt->video.i_frame_rate_base, 0 );
1622 break;
1624 case SPU_ES:
1625 fmt->i_codec = vlc_fourcc_GetCodec( fmt->i_cat, fmt->i_codec );
1626 break;
1628 default:
1629 break;
1633 static es_out_id_t *EsOutAddSlaveLocked( es_out_t *out, const es_format_t *fmt,
1634 es_out_id_t *p_master )
1636 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1637 input_thread_t *p_input = p_sys->p_input;
1639 if( fmt->i_group < 0 )
1641 msg_Err( p_input, "invalid group number" );
1642 return NULL;
1645 es_out_id_t *es = malloc( sizeof( *es ) );
1646 es_out_pgrm_t *p_pgrm;
1648 if( !es )
1649 return NULL;
1651 if( es_format_Copy( &es->fmt, fmt ) != VLC_SUCCESS )
1653 free( es );
1654 return NULL;
1656 if( es->fmt.i_id < 0 )
1657 es->fmt.i_id = p_sys->i_id;
1658 if( !es->fmt.i_original_fourcc )
1659 es->fmt.i_original_fourcc = es->fmt.i_codec;
1661 /* Search the program */
1662 p_pgrm = EsOutProgramFind( out, fmt->i_group );
1663 if( !p_pgrm )
1665 es_format_Clean( &es->fmt );
1666 free( es );
1667 return NULL;
1670 /* Get the number of ES already added in order to get the position of the es */
1671 es->i_pos = 0;
1672 es_out_id_t *it;
1673 foreach_es_then_es_slaves(it)
1674 if( it->fmt.i_cat == fmt->i_cat && it->fmt.i_group == fmt->i_group )
1675 es->i_pos++;
1677 /* Increase ref count for program */
1678 p_pgrm->i_es++;
1680 /* Set up ES */
1681 es->p_pgrm = p_pgrm;
1683 es->id.i_id = es->fmt.i_id;
1684 es->id.i_cat = es->fmt.i_cat;
1686 es_format_Init( &es->fmt_out, UNKNOWN_ES, 0 );
1688 es->i_meta_id = p_sys->i_id++; /* always incremented */
1689 es->b_scrambled = false;
1690 es->b_forced = false;
1691 es->b_terminated = false;
1693 switch( es->fmt.i_cat )
1695 case AUDIO_ES:
1696 es->i_channel = p_sys->audio.i_count++;
1697 break;
1699 case VIDEO_ES:
1700 es->i_channel = p_sys->video.i_count++;
1701 break;
1703 case SPU_ES:
1704 es->i_channel = p_sys->sub.i_count++;
1705 break;
1707 default:
1708 es->i_channel = 0;
1709 break;
1711 EsOutFillEsFmt( out, &es->fmt );
1712 es->psz_language = LanguageGetName( es->fmt.psz_language ); /* remember so we only need to do it once */
1713 es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
1714 es->psz_title = EsGetTitle(es);
1715 es->p_dec = NULL;
1716 es->p_dec_record = NULL;
1717 es->cc.type = 0;
1718 es->cc.i_bitmap = 0;
1719 es->p_master = p_master;
1720 es->mouse_event_cb = NULL;
1721 es->mouse_event_userdata = NULL;
1723 vlc_list_append(&es->node, es->p_master ? &p_sys->es_slaves : &p_sys->es);
1725 vlc_atomic_rc_init(&es->rc);
1727 if( es->p_pgrm == p_sys->p_pgrm )
1728 EsOutSendEsEvent( out, es, VLC_INPUT_ES_ADDED );
1730 EsOutUpdateInfo( out, es, NULL );
1731 EsOutSelect( out, es, false );
1733 if( es->b_scrambled )
1734 EsOutProgramUpdateScrambled( out, es->p_pgrm );
1736 return es;
1739 /* EsOutAdd:
1740 * Add an es_out
1742 static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt )
1744 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1745 vlc_mutex_lock( &p_sys->lock );
1746 es_out_id_t *es = EsOutAddSlaveLocked( out, fmt, NULL );
1747 vlc_mutex_unlock( &p_sys->lock );
1748 return es;
1751 static bool EsIsSelected( es_out_id_t *es )
1753 if( es->p_master )
1755 bool b_decode = false;
1756 if( es->p_master->p_dec )
1758 int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
1759 input_DecoderGetCcState( es->p_master->p_dec, es->fmt.i_codec,
1760 i_channel, &b_decode );
1762 return b_decode;
1764 else
1766 return es->p_dec != NULL;
1769 static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )
1771 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1772 input_thread_t *p_input = p_sys->p_input;
1773 decoder_t *dec;
1775 dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_input_clock,
1776 input_priv(p_input)->p_sout );
1777 if( dec != NULL )
1779 float rate = (float)p_sys->i_rate / (float)INPUT_RATE_DEFAULT;
1781 input_DecoderChangeRate( dec, rate );
1783 if( p_sys->b_buffering )
1784 input_DecoderStartWait( dec );
1786 if( !p_es->p_master && p_sys->p_sout_record )
1788 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_input_clock, p_sys->p_sout_record );
1789 if( p_es->p_dec_record && p_sys->b_buffering )
1790 input_DecoderStartWait( p_es->p_dec_record );
1793 if( p_es->mouse_event_cb && p_es->fmt.i_cat == VIDEO_ES )
1794 input_DecoderSetVoutMouseEvent( dec, p_es->mouse_event_cb,
1795 p_es->mouse_event_userdata );
1797 p_es->p_dec = dec;
1799 EsOutDecoderChangeDelay( out, p_es );
1801 static void EsOutDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
1803 VLC_UNUSED(out);
1805 if( !p_es->p_dec )
1806 return;
1808 input_DecoderDelete( p_es->p_dec );
1809 p_es->p_dec = NULL;
1811 if( p_es->p_dec_record )
1813 input_DecoderDelete( p_es->p_dec_record );
1814 p_es->p_dec_record = NULL;
1817 es_format_Clean( &p_es->fmt_out );
1820 static void EsOutSelectEs( es_out_t *out, es_out_id_t *es )
1822 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1823 input_thread_t *p_input = p_sys->p_input;
1824 bool b_thumbnailing = input_priv(p_input)->b_thumbnailing;
1826 if( EsIsSelected( es ) )
1828 msg_Warn( p_input, "ES 0x%x is already selected", es->fmt.i_id );
1829 return;
1832 if( es->p_master )
1834 int i_channel;
1835 if( !es->p_master->p_dec )
1836 return;
1838 i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
1840 if( i_channel == -1 ||
1841 input_DecoderSetCcState( es->p_master->p_dec, es->fmt.i_codec,
1842 i_channel, true ) )
1843 return;
1845 else
1847 const bool b_sout = input_priv(p_input)->p_sout != NULL;
1848 /* If b_forced, the ES is specifically requested by the user, so bypass
1849 * the following vars check. */
1850 if( !es->b_forced )
1852 if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
1854 if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) )
1856 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
1857 es->fmt.i_id );
1858 return;
1861 else if( es->fmt.i_cat == AUDIO_ES )
1863 if( !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) || b_thumbnailing )
1865 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
1866 es->fmt.i_id );
1867 return;
1870 if( es->fmt.i_cat == SPU_ES )
1872 if( !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) || b_thumbnailing )
1874 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
1875 es->fmt.i_id );
1876 return;
1881 EsOutCreateDecoder( out, es );
1883 if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
1884 return;
1887 /* Mark it as selected */
1888 EsOutSendEsEvent(out, es, VLC_INPUT_ES_SELECTED);
1890 /* Special case of the zvbi decoder for teletext: send the initial selected
1891 * page and transparency */
1892 if( !es->p_master && es->fmt.i_cat == SPU_ES
1893 && es->fmt.i_codec == VLC_CODEC_TELETEXT
1894 && var_Type( es->p_dec, "vbi-page" ) == VLC_VAR_INTEGER )
1896 input_SendEventVbiPage( p_input,
1897 var_GetInteger( es->p_dec, "vbi-page" ) );
1898 input_SendEventVbiTransparency( p_input,
1899 var_GetBool( es->p_dec, "vbi-opaque" ) );
1903 static void EsOutDrainCCChannels( es_out_id_t *parent )
1905 /* Drain captions sub ES as well */
1906 uint64_t i_bitmap = parent->cc.i_bitmap;
1907 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1 )
1909 if( (i_bitmap & 1) == 0 || !parent->cc.pp_es[i] ||
1910 !parent->cc.pp_es[i]->p_dec )
1911 continue;
1912 input_DecoderDrain( parent->cc.pp_es[i]->p_dec );
1916 static void EsDeleteCCChannels( es_out_t *out, es_out_id_t *parent )
1918 if( parent->cc.type == 0 )
1919 return;
1921 es_out_id_t *spu_es = EsOutGetSelectedCat( out, SPU_ES );
1922 const int i_spu_id = spu_es ? spu_es->fmt.i_id : -1;
1924 uint64_t i_bitmap = parent->cc.i_bitmap;
1925 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1 )
1927 if( (i_bitmap & 1) == 0 || !parent->cc.pp_es[i] )
1928 continue;
1930 if( i_spu_id == parent->cc.pp_es[i]->fmt.i_id )
1932 /* Force unselection of the CC */
1933 EsOutSendEsEvent(out, parent->cc.pp_es[i], VLC_INPUT_ES_UNSELECTED);
1935 EsOutDelLocked( out, parent->cc.pp_es[i] );
1938 parent->cc.i_bitmap = 0;
1939 parent->cc.type = 0;
1942 static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update )
1944 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1945 input_thread_t *p_input = p_sys->p_input;
1947 if( !EsIsSelected( es ) )
1949 msg_Warn( p_input, "ES 0x%x is already unselected", es->fmt.i_id );
1950 return;
1953 if( es->p_master )
1955 if( es->p_master->p_dec )
1957 int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
1958 if( i_channel != -1 )
1959 input_DecoderSetCcState( es->p_master->p_dec, es->fmt.i_codec,
1960 i_channel, false );
1963 else
1965 EsDeleteCCChannels( out, es );
1966 EsOutDestroyDecoder( out, es );
1969 if( !b_update )
1970 return;
1972 /* Mark it as unselected */
1973 EsOutSendEsEvent(out, es, VLC_INPUT_ES_UNSELECTED);
1977 * Select an ES given the current mode
1978 * XXX: you need to take a the lock before (stream.stream_lock)
1980 * \param out The es_out structure
1981 * \param es es_out_id structure
1982 * \param b_force ...
1983 * \return nothing
1985 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
1987 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1988 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
1990 if( !p_sys->b_active ||
1991 ( !b_force && es->fmt.i_priority < ES_PRIORITY_SELECTABLE_MIN ) )
1993 return;
1996 bool b_auto_unselect = p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO &&
1997 p_esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE &&
1998 p_esprops->p_main_es && p_esprops->p_main_es != es;
2000 if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
2002 if( !EsIsSelected( es ) )
2004 if( b_auto_unselect )
2005 EsOutUnselectEs( out, p_esprops->p_main_es, true );
2007 EsOutSelectEs( out, es );
2010 else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
2012 char *prgms = var_GetNonEmptyString( p_sys->p_input, "programs" );
2013 if( prgms != NULL )
2015 char *buf;
2017 for ( const char *prgm = strtok_r( prgms, ",", &buf );
2018 prgm != NULL;
2019 prgm = strtok_r( NULL, ",", &buf ) )
2021 if( atoi( prgm ) == es->p_pgrm->i_id || b_force )
2023 if( !EsIsSelected( es ) )
2024 EsOutSelectEs( out, es );
2025 break;
2028 free( prgms );
2031 else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
2033 const es_out_id_t *wanted_es = NULL;
2035 if( es->p_pgrm != p_sys->p_pgrm || !p_esprops )
2036 return;
2038 /* user designated by ID ES have higher prio than everything */
2039 if ( p_esprops->i_id >= 0 )
2041 if( es->fmt.i_id == p_esprops->i_id )
2042 wanted_es = es;
2044 /* then per pos */
2045 else if( p_esprops->i_channel >= 0 )
2047 if( p_esprops->i_channel == es->i_channel )
2048 wanted_es = es;
2050 else if( p_esprops->ppsz_language )
2052 /* If not deactivated */
2053 const int i_stop_idx = LanguageArrayIndex( p_esprops->ppsz_language, "none" );
2055 int current_es_idx = ( p_esprops->p_main_es == NULL ) ? -1 :
2056 LanguageArrayIndex( p_esprops->ppsz_language,
2057 p_esprops->p_main_es->psz_language_code );
2058 int es_idx = LanguageArrayIndex( p_esprops->ppsz_language,
2059 es->psz_language_code );
2060 if( es_idx >= 0 && (i_stop_idx < 0 || i_stop_idx > es_idx) )
2062 /* Only select the language if it's in the list */
2063 if( p_esprops->p_main_es == NULL ||
2064 current_es_idx < 0 || /* current es was not selected by lang prefs */
2065 es_idx < current_es_idx || /* current es has lower lang prio */
2066 ( es_idx == current_es_idx && /* lang is same, but es has higher prio */
2067 p_esprops->p_main_es->fmt.i_priority < es->fmt.i_priority ) )
2069 wanted_es = es;
2072 /* We did not find a language matching our prefs */
2073 else if( i_stop_idx < 0 ) /* If not fallback disabled by 'none' */
2075 /* Select if asked by demuxer */
2076 if( current_es_idx < 0 ) /* No es is currently selected by lang pref */
2078 /* If demux has specified a track */
2079 if( p_esprops->i_demux_id >= 0 && es->fmt.i_id == p_esprops->i_demux_id )
2081 wanted_es = es;
2083 /* Otherwise, fallback by priority */
2084 else if( p_esprops->p_main_es == NULL ||
2085 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority )
2087 if( p_esprops->b_autoselect )
2088 wanted_es = es;
2095 /* If there is no user preference, select the default subtitle
2096 * or adapt by ES priority */
2097 else if( p_esprops->i_demux_id >= 0 && es->fmt.i_id == p_esprops->i_demux_id )
2099 wanted_es = es;
2101 else if( p_esprops->p_main_es == NULL ||
2102 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority )
2104 if( p_esprops->b_autoselect )
2105 wanted_es = es;
2108 if( wanted_es == es && !EsIsSelected( es ) )
2110 if( b_auto_unselect )
2111 EsOutUnselectEs( out, p_esprops->p_main_es, true );
2113 EsOutSelectEs( out, es );
2117 /* FIXME TODO handle priority here */
2118 if( p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO && EsIsSelected( es ) )
2119 p_esprops->p_main_es = es;
2122 static void EsOutCreateCCChannels( es_out_t *out, vlc_fourcc_t codec, uint64_t i_bitmap,
2123 const char *psz_descfmt, es_out_id_t *parent )
2125 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2126 input_thread_t *p_input = p_sys->p_input;
2128 /* Only one type of captions is allowed ! */
2129 if( parent->cc.type && parent->cc.type != codec )
2130 return;
2132 uint64_t i_existingbitmap = parent->cc.i_bitmap;
2133 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1, i_existingbitmap >>= 1 )
2135 es_format_t fmt;
2137 if( (i_bitmap & 1) == 0 || (i_existingbitmap & 1) )
2138 continue;
2140 msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, parent->fmt.i_id );
2142 es_format_Init( &fmt, SPU_ES, codec );
2143 fmt.subs.cc.i_channel = i;
2144 fmt.i_group = parent->fmt.i_group;
2145 if( asprintf( &fmt.psz_description, psz_descfmt, 1 + i ) == -1 )
2146 fmt.psz_description = NULL;
2148 es_out_id_t **pp_es = &parent->cc.pp_es[i];
2149 *pp_es = EsOutAddSlaveLocked( out, &fmt, parent );
2150 es_format_Clean( &fmt );
2152 /* */
2153 parent->cc.i_bitmap |= (1ULL << i);
2154 parent->cc.type = codec;
2156 /* Enable if user specified on command line */
2157 if (p_sys->sub.i_channel == i)
2158 EsOutSelect(out, *pp_es, true);
2163 * Send a block for the given es_out
2165 * \param out the es_out to send from
2166 * \param es the es_out_id
2167 * \param p_block the data block to send
2169 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
2171 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2172 input_thread_t *p_input = p_sys->p_input;
2174 assert( p_block->p_next == NULL );
2176 struct input_stats *stats = input_priv(p_input)->stats;
2177 if( stats != NULL )
2179 input_rate_Add( &stats->demux_bitrate, p_block->i_buffer );
2181 /* Update number of corrupted data packats */
2182 if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
2183 atomic_fetch_add_explicit(&stats->demux_corrupted, 1,
2184 memory_order_relaxed);
2186 /* Update number of discontinuities */
2187 if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )
2188 atomic_fetch_add_explicit(&stats->demux_discontinuity, 1,
2189 memory_order_relaxed);
2192 vlc_mutex_lock( &p_sys->lock );
2194 /* Mark preroll blocks */
2195 if( p_sys->i_preroll_end >= 0 )
2197 vlc_tick_t i_date = p_block->i_pts;
2198 if( p_block->i_pts == VLC_TICK_INVALID )
2199 i_date = p_block->i_dts;
2201 if( i_date + p_block->i_length < p_sys->i_preroll_end )
2202 p_block->i_flags |= BLOCK_FLAG_PREROLL;
2205 if( !es->p_dec )
2207 block_Release( p_block );
2208 vlc_mutex_unlock( &p_sys->lock );
2209 return VLC_SUCCESS;
2212 /* Check for sout mode */
2213 if( input_priv(p_input)->p_sout )
2215 /* FIXME review this, proper lock may be missing */
2216 if( input_priv(p_input)->p_sout->i_out_pace_nocontrol > 0 &&
2217 input_priv(p_input)->b_out_pace_control )
2219 msg_Dbg( p_input, "switching to sync mode" );
2220 input_priv(p_input)->b_out_pace_control = false;
2222 else if( input_priv(p_input)->p_sout->i_out_pace_nocontrol <= 0 &&
2223 !input_priv(p_input)->b_out_pace_control )
2225 msg_Dbg( p_input, "switching to async mode" );
2226 input_priv(p_input)->b_out_pace_control = true;
2230 /* Decode */
2231 if( es->p_dec_record )
2233 block_t *p_dup = block_Duplicate( p_block );
2234 if( p_dup )
2235 input_DecoderDecode( es->p_dec_record, p_dup,
2236 input_priv(p_input)->b_out_pace_control );
2238 input_DecoderDecode( es->p_dec, p_block,
2239 input_priv(p_input)->b_out_pace_control );
2241 es_format_t fmt_dsc;
2242 vlc_meta_t *p_meta_dsc;
2243 if( input_DecoderHasFormatChanged( es->p_dec, &fmt_dsc, &p_meta_dsc ) )
2245 if (EsOutEsUpdateFmt( out, es, &fmt_dsc) == VLC_SUCCESS)
2246 EsOutSendEsEvent(out, es, VLC_INPUT_ES_UPDATED);
2248 EsOutUpdateInfo(out, es, p_meta_dsc);
2250 es_format_Clean( &fmt_dsc );
2251 if( p_meta_dsc )
2252 vlc_meta_Delete( p_meta_dsc );
2255 /* Check CC status */
2256 decoder_cc_desc_t desc;
2258 input_DecoderGetCcDesc( es->p_dec, &desc );
2259 if( var_InheritInteger( p_input, "captions" ) == 708 )
2260 EsOutCreateCCChannels( out, VLC_CODEC_CEA708, desc.i_708_channels,
2261 _("DTVCC Closed captions %u"), es );
2262 EsOutCreateCCChannels( out, VLC_CODEC_CEA608, desc.i_608_channels,
2263 _("Closed captions %u"), es );
2265 vlc_mutex_unlock( &p_sys->lock );
2267 return VLC_SUCCESS;
2270 static void
2271 EsOutDrainDecoder( es_out_t *out, es_out_id_t *es )
2273 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2274 assert( es->p_dec );
2276 /* FIXME: This might hold the ES output caller (i.e. the demux), and
2277 * the corresponding thread (typically the input thread), for a little
2278 * bit too long if the ES is deleted in the middle of a stream. */
2279 input_DecoderDrain( es->p_dec );
2280 EsOutDrainCCChannels( es );
2281 while( !input_Stopped(p_sys->p_input) && !p_sys->b_buffering )
2283 if( input_DecoderIsEmpty( es->p_dec ) &&
2284 ( !es->p_dec_record || input_DecoderIsEmpty( es->p_dec_record ) ))
2285 break;
2286 /* FIXME there should be a way to have auto deleted es, but there will be
2287 * a problem when another codec of the same type is created (mainly video) */
2288 vlc_tick_sleep(VLC_TICK_FROM_MS(20));
2292 /*****************************************************************************
2293 * EsOutDelLocked:
2294 *****************************************************************************/
2295 static void EsOutDelLocked( es_out_t *out, es_out_id_t *es )
2297 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2298 bool b_reselect = false;
2300 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
2302 /* We don't try to reselect */
2303 if( es->p_dec )
2305 EsOutDrainDecoder( out, es );
2306 EsOutUnselectEs( out, es, es->p_pgrm == p_sys->p_pgrm );
2309 EsTerminate(es);
2311 if( es->p_pgrm == p_sys->p_pgrm )
2312 EsOutSendEsEvent( out, es, VLC_INPUT_ES_DELETED );
2314 EsOutDeleteInfoEs( out, es );
2316 /* Update program */
2317 es->p_pgrm->i_es--;
2318 if( es->p_pgrm->i_es == 0 )
2319 msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" );
2321 if( es->b_scrambled )
2322 EsOutProgramUpdateScrambled( out, es->p_pgrm );
2324 /* */
2325 if( p_esprops )
2327 if( p_esprops->p_main_es == es )
2329 b_reselect = true;
2330 p_esprops->p_main_es = NULL;
2332 p_esprops->i_count--;
2335 /* Re-select another track when needed */
2336 if( b_reselect )
2338 es_out_id_t *other;
2340 foreach_es_then_es_slaves(other)
2341 if( es->fmt.i_cat == other->fmt.i_cat )
2343 if (EsIsSelected(other))
2345 EsOutSendEsEvent(out, es, VLC_INPUT_ES_SELECTED);
2346 if( p_esprops->p_main_es == NULL )
2347 p_esprops->p_main_es = other;
2349 else
2350 EsOutSelect(out, other, false);
2354 EsRelease(es);
2357 static void EsOutDel( es_out_t *out, es_out_id_t *es )
2359 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2360 vlc_mutex_lock( &p_sys->lock );
2361 EsOutDelLocked( out, es );
2362 vlc_mutex_unlock( &p_sys->lock );
2365 static int EsOutVaControlLocked( es_out_t *, int, va_list );
2366 static int EsOutControlLocked( es_out_t *out, int i_query, ... )
2368 va_list args;
2370 va_start( args, i_query );
2371 int ret = EsOutVaControlLocked( out, i_query, args );
2372 va_end( args );
2373 return ret;
2377 * Control query handler
2379 * \param out the es_out to control
2380 * \param i_query A es_out query as defined in include/ninput.h
2381 * \param args a variable list of arguments for the query
2382 * \return VLC_SUCCESS or an error code
2384 static int EsOutVaControlLocked( es_out_t *out, int i_query, va_list args )
2386 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2388 switch( i_query )
2390 case ES_OUT_SET_ES_STATE:
2392 es_out_id_t *es = va_arg( args, es_out_id_t * );
2393 bool b = va_arg( args, int );
2394 if( b && !EsIsSelected( es ) )
2396 EsOutSelectEs( out, es );
2397 return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC;
2399 else if( !b && EsIsSelected( es ) )
2401 EsOutUnselectEs( out, es, es->p_pgrm == p_sys->p_pgrm );
2402 return VLC_SUCCESS;
2404 return VLC_SUCCESS;
2407 case ES_OUT_GET_ES_STATE:
2409 es_out_id_t *es = va_arg( args, es_out_id_t * );
2410 bool *pb = va_arg( args, bool * );
2412 *pb = EsIsSelected( es );
2413 return VLC_SUCCESS;
2416 case ES_OUT_SET_ES_CAT_POLICY:
2418 enum es_format_category_e i_cat = va_arg( args, enum es_format_category_e );
2419 enum es_out_policy_e i_pol = va_arg( args, enum es_out_policy_e );
2420 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, i_cat );
2421 if( p_esprops == NULL )
2422 return VLC_EGENERIC;
2423 p_esprops->e_policy = i_pol;
2424 return VLC_SUCCESS;
2427 case ES_OUT_GET_GROUP_FORCED:
2429 int *pi_group = va_arg( args, int * );
2430 *pi_group = p_sys->i_group_id;
2431 return VLC_SUCCESS;
2434 case ES_OUT_SET_MODE:
2436 const int i_mode = va_arg( args, int );
2437 assert( i_mode == ES_OUT_MODE_NONE || i_mode == ES_OUT_MODE_ALL ||
2438 i_mode == ES_OUT_MODE_AUTO || i_mode == ES_OUT_MODE_PARTIAL ||
2439 i_mode == ES_OUT_MODE_END );
2441 if (i_mode != ES_OUT_MODE_NONE && !p_sys->b_active && !vlc_list_is_empty(&p_sys->es))
2443 /* XXX Terminate vout if there are tracks but no video one.
2444 * This one is not mandatory but is he earliest place where it
2445 * can be done */
2446 es_out_id_t *p_es;
2447 bool found = false;
2449 foreach_es_then_es_slaves(p_es)
2450 if( p_es->fmt.i_cat == VIDEO_ES && !found /* nested loop */ )
2452 found = true;
2453 break;
2456 if (!found)
2457 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
2459 p_sys->b_active = i_mode != ES_OUT_MODE_NONE;
2460 p_sys->i_mode = i_mode;
2462 /* Reapply policy mode */
2463 es_out_id_t *es;
2465 foreach_es_then_es_slaves(es)
2467 if (EsIsSelected(es))
2468 EsOutUnselectEs(out, es, es->p_pgrm == p_sys->p_pgrm);
2470 foreach_es_then_es_slaves(es)
2472 EsOutSelect(out, es, false);
2475 if( i_mode == ES_OUT_MODE_END )
2476 EsOutTerminate( out );
2477 return VLC_SUCCESS;
2480 case ES_OUT_SET_ES:
2481 case ES_OUT_RESTART_ES:
2483 #define IGNORE_ES DATA_ES
2484 es_out_id_t *es = va_arg( args, es_out_id_t * ), *other;
2486 enum es_format_category_e i_cat;
2487 if( es == NULL )
2488 i_cat = UNKNOWN_ES;
2489 else if( es == es_cat + AUDIO_ES )
2490 i_cat = AUDIO_ES;
2491 else if( es == es_cat + VIDEO_ES )
2492 i_cat = VIDEO_ES;
2493 else if( es == es_cat + SPU_ES )
2494 i_cat = SPU_ES;
2495 else
2497 if (es->b_terminated)
2498 return VLC_EGENERIC;
2499 i_cat = IGNORE_ES;
2502 foreach_es_then_es_slaves(other)
2504 if( i_cat == IGNORE_ES )
2506 if (es == other)
2508 if (i_query == ES_OUT_RESTART_ES && es->p_dec != NULL)
2510 EsOutDestroyDecoder(out, es);
2511 EsOutCreateDecoder(out, es);
2513 else if( i_query == ES_OUT_SET_ES )
2515 EsOutSelect(out, es, true);
2517 break;
2520 else if (i_cat == UNKNOWN_ES || other->fmt.i_cat == i_cat)
2522 if (EsIsSelected(other))
2524 if (i_query == ES_OUT_RESTART_ES)
2526 if (other->p_dec != NULL)
2528 EsOutDestroyDecoder(out, other);
2529 EsOutCreateDecoder(out, other);
2532 else
2533 EsOutUnselectEs(out, other, other->p_pgrm == p_sys->p_pgrm);
2538 return VLC_SUCCESS;
2540 case ES_OUT_UNSET_ES:
2542 es_out_id_t *es = va_arg( args, es_out_id_t * ), *other;
2543 if (es->b_terminated)
2544 return VLC_EGENERIC;
2545 foreach_es_then_es_slaves(other)
2547 if (es == other)
2549 if (EsIsSelected(other))
2551 EsOutUnselectEs(out, other, other->p_pgrm == p_sys->p_pgrm);
2552 return VLC_SUCCESS;
2554 break;
2557 return VLC_EGENERIC;
2559 case ES_OUT_STOP_ALL_ES:
2561 es_out_id_t *es;
2562 int count = 0;
2564 foreach_es_then_es_slaves(es)
2565 count++;
2567 int *selected_es = vlc_alloc(count + 1, sizeof(int));
2568 if (!selected_es)
2569 return VLC_ENOMEM;
2571 *va_arg(args, void **) = selected_es;
2572 *selected_es = count;
2574 foreach_es_then_es_slaves(es)
2576 if (EsIsSelected(es))
2578 EsOutDestroyDecoder(out, es);
2579 *++selected_es = es->fmt.i_id;
2581 else
2582 *++selected_es = -1;
2584 return VLC_SUCCESS;
2586 case ES_OUT_START_ALL_ES:
2588 int *selected_es = va_arg( args, void * );
2589 int count = selected_es[0];
2590 for( int i = 0; i < count; ++i )
2592 int i_id = selected_es[i + 1];
2593 if( i_id != -1 )
2595 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2596 EsOutCreateDecoder( out, p_es );
2599 free(selected_es);
2600 return VLC_SUCCESS;
2603 case ES_OUT_SET_ES_DEFAULT:
2605 es_out_id_t *es = va_arg( args, es_out_id_t * );
2607 if( es == NULL )
2609 /*p_sys->i_default_video_id = -1;*/
2610 /*p_sys->i_default_audio_id = -1;*/
2611 p_sys->sub.i_demux_id = -1;
2613 else if( es == es_cat + AUDIO_ES )
2615 /*p_sys->i_default_video_id = -1;*/
2617 else if( es == es_cat + VIDEO_ES )
2619 /*p_sys->i_default_audio_id = -1;*/
2621 else if( es == es_cat + SPU_ES )
2623 p_sys->sub.i_demux_id = -1;
2625 else
2627 /*if( es->fmt.i_cat == VIDEO_ES )
2628 p_sys->i_default_video_id = es->fmt.i_id;
2629 else
2630 if( es->fmt.i_cat == AUDIO_ES )
2631 p_sys->i_default_audio_id = es->fmt.i_id;
2632 else*/
2633 if( es->fmt.i_cat == SPU_ES )
2634 p_sys->sub.i_demux_id = es->fmt.i_id;
2636 return VLC_SUCCESS;
2639 case ES_OUT_SET_PCR:
2640 case ES_OUT_SET_GROUP_PCR:
2642 es_out_pgrm_t *p_pgrm = NULL;
2643 int i_group = 0;
2644 vlc_tick_t i_pcr;
2646 /* Search program */
2647 if( i_query == ES_OUT_SET_PCR )
2649 p_pgrm = p_sys->p_pgrm;
2650 if( !p_pgrm )
2651 p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
2653 else
2655 i_group = va_arg( args, int );
2656 p_pgrm = EsOutProgramFind( out, i_group );
2658 if( !p_pgrm )
2659 return VLC_EGENERIC;
2661 i_pcr = va_arg( args, vlc_tick_t );
2662 if( i_pcr == VLC_TICK_INVALID )
2664 msg_Err( p_sys->p_input, "Invalid PCR value in ES_OUT_SET_(GROUP_)PCR !" );
2665 return VLC_EGENERIC;
2668 /* TODO do not use vlc_tick_now() but proper stream acquisition date */
2669 bool b_late;
2670 input_clock_Update( p_pgrm->p_input_clock, VLC_OBJECT(p_sys->p_input),
2671 &b_late,
2672 input_priv(p_sys->p_input)->b_can_pace_control || p_sys->b_buffering,
2673 EsOutIsExtraBufferingAllowed( out ),
2674 i_pcr, vlc_tick_now() );
2676 if( !p_sys->p_pgrm )
2677 return VLC_SUCCESS;
2679 if( p_sys->b_buffering )
2681 /* Check buffering state on master clock update */
2682 EsOutDecodersStopBuffering( out, false );
2684 else if( p_pgrm == p_sys->p_pgrm )
2686 if( b_late && ( !input_priv(p_sys->p_input)->p_sout ||
2687 !input_priv(p_sys->p_input)->b_out_pace_control ) )
2689 const vlc_tick_t i_pts_delay_base = p_sys->i_pts_delay - p_sys->i_pts_jitter;
2690 vlc_tick_t i_pts_delay = input_clock_GetJitter( p_pgrm->p_input_clock );
2692 /* Avoid dangerously high value */
2693 const vlc_tick_t i_jitter_max =
2694 VLC_TICK_FROM_MS(var_InheritInteger( p_sys->p_input, "clock-jitter" ));
2695 if( i_pts_delay > __MIN( i_pts_delay_base + i_jitter_max, INPUT_PTS_DELAY_MAX ) )
2697 es_out_pgrm_t *pgrm;
2699 msg_Err( p_sys->p_input,
2700 "ES_OUT_SET_(GROUP_)PCR is called too late (jitter of %d ms ignored)",
2701 (int)MS_FROM_VLC_TICK(i_pts_delay - i_pts_delay_base) );
2702 i_pts_delay = p_sys->i_pts_delay;
2704 /* reset clock */
2705 vlc_list_foreach(pgrm, &p_sys->programs, node)
2706 input_clock_Reset(pgrm->p_input_clock);
2708 else
2710 msg_Err( p_sys->p_input,
2711 "ES_OUT_SET_(GROUP_)PCR is called too late (pts_delay increased to %d ms)",
2712 (int)MS_FROM_VLC_TICK(i_pts_delay) );
2714 /* Force a rebufferization when we are too late */
2716 /* It is not really good, as we throw away already buffered data
2717 * TODO have a mean to correctly reenter bufferization */
2718 EsOutControlLocked( out, ES_OUT_RESET_PCR );
2721 EsOutControlLocked( out, ES_OUT_SET_JITTER, i_pts_delay_base,
2722 i_pts_delay - i_pts_delay_base,
2723 p_sys->i_cr_average );
2726 return VLC_SUCCESS;
2729 case ES_OUT_RESET_PCR:
2730 msg_Dbg( p_sys->p_input, "ES_OUT_RESET_PCR called" );
2731 EsOutChangePosition( out );
2732 return VLC_SUCCESS;
2734 case ES_OUT_SET_GROUP:
2736 int i = va_arg( args, int );
2737 es_out_pgrm_t *p_pgrm;
2739 vlc_list_foreach(p_pgrm, &p_sys->programs, node)
2740 if( p_pgrm->i_id == i )
2742 EsOutProgramSelect( out, p_pgrm );
2743 return VLC_SUCCESS;
2745 return VLC_EGENERIC;
2748 case ES_OUT_SET_ES_FMT:
2750 es_out_id_t *es = va_arg( args, es_out_id_t * );
2751 es_format_t *p_fmt = va_arg( args, es_format_t * );
2752 if( es == NULL || es->fmt.i_cat != p_fmt->i_cat
2753 || es->fmt.i_id != p_fmt->i_id
2754 || es->fmt.i_group != p_fmt->i_group )
2755 return VLC_EGENERIC;
2757 es_format_Clean( &es->fmt );
2758 int ret = es_format_Copy( &es->fmt, p_fmt );
2759 if( ret != VLC_SUCCESS )
2760 return ret;
2761 EsOutFillEsFmt( out, &es->fmt );
2762 EsOutUpdateEsLanguageTitle(es, &es->fmt);
2764 const bool b_was_selected = EsIsSelected( es );
2765 if( es->p_dec )
2767 EsOutDrainDecoder( out, es );
2768 EsDeleteCCChannels( out, es );
2769 EsOutDestroyDecoder( out, es );
2772 if(b_was_selected)
2773 EsOutCreateDecoder( out, es );
2775 EsOutSendEsEvent( out, es, VLC_INPUT_ES_UPDATED );
2777 return VLC_SUCCESS;
2780 case ES_OUT_SET_ES_SCRAMBLED_STATE:
2782 es_out_id_t *es = va_arg( args, es_out_id_t * );
2783 bool b_scrambled = (bool)va_arg( args, int );
2785 if( !es->b_scrambled != !b_scrambled )
2787 es->b_scrambled = b_scrambled;
2788 EsOutProgramUpdateScrambled( out, es->p_pgrm );
2790 return VLC_SUCCESS;
2793 case ES_OUT_SET_NEXT_DISPLAY_TIME:
2795 const int64_t i_date = va_arg( args, int64_t );
2797 if( i_date < 0 )
2798 return VLC_EGENERIC;
2800 p_sys->i_preroll_end = i_date;
2802 return VLC_SUCCESS;
2804 case ES_OUT_SET_GROUP_META:
2806 int i_group = va_arg( args, int );
2807 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
2809 EsOutProgramMeta( out, i_group, p_meta );
2810 return VLC_SUCCESS;
2812 case ES_OUT_SET_GROUP_EPG:
2814 int i_group = va_arg( args, int );
2815 const vlc_epg_t *p_epg = va_arg( args, const vlc_epg_t * );
2817 EsOutProgramEpg( out, i_group, p_epg );
2818 return VLC_SUCCESS;
2820 case ES_OUT_SET_GROUP_EPG_EVENT:
2822 int i_group = va_arg( args, int );
2823 const vlc_epg_event_t *p_evt = va_arg( args, const vlc_epg_event_t * );
2825 EsOutProgramEpgEvent( out, i_group, p_evt );
2826 return VLC_SUCCESS;
2828 case ES_OUT_SET_EPG_TIME:
2830 int64_t i64 = va_arg( args, int64_t );
2832 EsOutEpgTime( out, i64 );
2833 return VLC_SUCCESS;
2836 case ES_OUT_DEL_GROUP:
2838 int i_group = va_arg( args, int );
2840 return EsOutProgramDel( out, i_group );
2843 case ES_OUT_SET_META:
2845 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
2847 EsOutGlobalMeta( out, p_meta );
2848 return VLC_SUCCESS;
2851 case ES_OUT_GET_WAKE_UP:
2853 vlc_tick_t *pi_wakeup = va_arg( args, vlc_tick_t* );
2854 *pi_wakeup = EsOutGetWakeup( out );
2855 return VLC_SUCCESS;
2858 case ES_OUT_SET_ES_BY_ID:
2859 case ES_OUT_RESTART_ES_BY_ID:
2860 case ES_OUT_SET_ES_DEFAULT_BY_ID:
2862 const int i_id = va_arg( args, int );
2863 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2864 int i_new_query = 0;
2866 switch( i_query )
2868 case ES_OUT_SET_ES_BY_ID: i_new_query = ES_OUT_SET_ES;
2869 p_es->b_forced = va_arg( args, int );
2870 break;
2871 case ES_OUT_RESTART_ES_BY_ID: i_new_query = ES_OUT_RESTART_ES; break;
2872 case ES_OUT_SET_ES_DEFAULT_BY_ID: i_new_query = ES_OUT_SET_ES_DEFAULT; break;
2873 default:
2874 vlc_assert_unreachable();
2876 int i_ret = EsOutControlLocked( out, i_new_query, p_es );
2878 /* Clean up vout after user action (in active mode only).
2879 * FIXME it does not work well with multiple video windows */
2880 if( p_sys->b_active )
2881 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
2882 return i_ret;
2885 case ES_OUT_GET_ES_OBJECTS_BY_ID:
2887 const int i_id = va_arg( args, int );
2888 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2889 if( !p_es )
2890 return VLC_EGENERIC;
2892 vlc_object_t **pp_decoder = va_arg( args, vlc_object_t ** );
2893 vout_thread_t **pp_vout = va_arg( args, vout_thread_t ** );
2894 audio_output_t **pp_aout = va_arg( args, audio_output_t ** );
2895 if( p_es->p_dec )
2897 if( pp_decoder )
2898 *pp_decoder = vlc_object_hold( p_es->p_dec );
2899 input_DecoderGetObjects( p_es->p_dec, pp_vout, pp_aout );
2901 else
2903 if( pp_decoder )
2904 *pp_decoder = NULL;
2905 if( pp_vout )
2906 *pp_vout = NULL;
2907 if( pp_aout )
2908 *pp_aout = NULL;
2910 return VLC_SUCCESS;
2913 case ES_OUT_GET_BUFFERING:
2915 bool *pb = va_arg( args, bool* );
2916 *pb = p_sys->b_buffering;
2917 return VLC_SUCCESS;
2920 case ES_OUT_GET_EMPTY:
2922 bool *pb = va_arg( args, bool* );
2923 *pb = EsOutDecodersIsEmpty( out );
2924 return VLC_SUCCESS;
2927 case ES_OUT_SET_DELAY:
2929 const int i_cat = va_arg( args, int );
2930 const vlc_tick_t i_delay = va_arg( args, vlc_tick_t );
2931 EsOutSetDelay( out, i_cat, i_delay );
2932 return VLC_SUCCESS;
2935 case ES_OUT_SET_RECORD_STATE:
2937 bool b = va_arg( args, int );
2938 return EsOutSetRecord( out, b );
2941 case ES_OUT_SET_PAUSE_STATE:
2943 const bool b_source_paused = (bool)va_arg( args, int );
2944 const bool b_paused = (bool)va_arg( args, int );
2945 const vlc_tick_t i_date = va_arg( args, vlc_tick_t );
2947 assert( !b_source_paused == !b_paused );
2948 EsOutChangePause( out, b_paused, i_date );
2950 return VLC_SUCCESS;
2953 case ES_OUT_SET_RATE:
2955 const int i_src_rate = va_arg( args, int );
2956 const int i_rate = va_arg( args, int );
2958 assert( i_src_rate == i_rate );
2959 EsOutChangeRate( out, i_rate );
2961 return VLC_SUCCESS;
2964 case ES_OUT_SET_FRAME_NEXT:
2965 EsOutFrameNext( out );
2966 return VLC_SUCCESS;
2968 case ES_OUT_SET_TIMES:
2970 double f_position = va_arg( args, double );
2971 vlc_tick_t i_time = va_arg( args, vlc_tick_t );
2972 vlc_tick_t i_length = va_arg( args, vlc_tick_t );
2974 input_SendEventLength( p_sys->p_input, i_length );
2976 if( !p_sys->b_buffering )
2978 vlc_tick_t i_delay;
2980 /* Fix for buffering delay */
2981 if( !input_priv(p_sys->p_input)->p_sout ||
2982 !input_priv(p_sys->p_input)->b_out_pace_control )
2983 i_delay = EsOutGetBuffering( out );
2984 else
2985 i_delay = 0;
2987 i_time -= i_delay;
2988 if( i_time < 0 )
2989 i_time = 0;
2991 if( i_length > 0 )
2992 f_position -= (double)i_delay / i_length;
2993 if( f_position < 0 )
2994 f_position = 0;
2996 input_SendEventPosition( p_sys->p_input, f_position, i_time );
2998 return VLC_SUCCESS;
3000 case ES_OUT_SET_JITTER:
3002 vlc_tick_t i_pts_delay = va_arg( args, vlc_tick_t );
3003 vlc_tick_t i_pts_jitter = va_arg( args, vlc_tick_t );
3004 int i_cr_average = va_arg( args, int );
3005 es_out_pgrm_t *pgrm;
3007 bool b_change_clock =
3008 i_pts_delay + i_pts_jitter != p_sys->i_pts_delay ||
3009 i_cr_average != p_sys->i_cr_average;
3011 assert( i_pts_jitter >= 0 );
3012 p_sys->i_pts_delay = i_pts_delay + i_pts_jitter;
3013 p_sys->i_pts_jitter = i_pts_jitter;
3014 p_sys->i_cr_average = i_cr_average;
3016 if (b_change_clock)
3017 vlc_list_foreach(pgrm, &p_sys->programs, node)
3018 input_clock_SetJitter(pgrm->p_input_clock, i_pts_delay
3019 + i_pts_jitter, i_cr_average);
3020 return VLC_SUCCESS;
3023 case ES_OUT_GET_PCR_SYSTEM:
3025 if( p_sys->b_buffering )
3026 return VLC_EGENERIC;
3028 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
3029 if( !p_pgrm )
3030 return VLC_EGENERIC;
3032 vlc_tick_t *pi_system = va_arg( args, vlc_tick_t *);
3033 vlc_tick_t *pi_delay = va_arg( args, vlc_tick_t *);
3034 input_clock_GetSystemOrigin( p_pgrm->p_input_clock, pi_system, pi_delay );
3035 return VLC_SUCCESS;
3038 case ES_OUT_MODIFY_PCR_SYSTEM:
3040 if( p_sys->b_buffering )
3041 return VLC_EGENERIC;
3043 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
3044 if( !p_pgrm )
3045 return VLC_EGENERIC;
3047 const bool b_absolute = va_arg( args, int );
3048 const vlc_tick_t i_system = va_arg( args, vlc_tick_t );
3049 input_clock_ChangeSystemOrigin( p_pgrm->p_input_clock, b_absolute, i_system );
3050 return VLC_SUCCESS;
3052 case ES_OUT_SET_EOS:
3054 es_out_id_t *id;
3055 foreach_es_then_es_slaves(id)
3056 if (id->p_dec != NULL)
3057 input_DecoderDrain(id->p_dec);
3058 return VLC_SUCCESS;
3061 case ES_OUT_POST_SUBNODE:
3063 input_thread_t *input = p_sys->p_input;
3064 input_item_node_t *node = va_arg(args, input_item_node_t *);
3065 input_SendEventParsing(input, node);
3066 input_item_node_Delete(node);
3068 return VLC_SUCCESS;
3071 case ES_OUT_VOUT_SET_MOUSE_EVENT:
3073 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3075 if( !p_es || p_es->fmt.i_cat != VIDEO_ES )
3076 return VLC_EGENERIC;
3078 p_es->mouse_event_cb = va_arg( args, vlc_mouse_event );
3079 p_es->mouse_event_userdata = va_arg( args, void * );
3081 if( p_es->p_dec )
3082 input_DecoderSetVoutMouseEvent( p_es->p_dec,
3083 p_es->mouse_event_cb, p_es->mouse_event_userdata );
3085 return VLC_SUCCESS;
3087 case ES_OUT_VOUT_ADD_OVERLAY:
3089 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3090 subpicture_t *sub = va_arg( args, subpicture_t * );
3091 int *channel = va_arg( args, int * );
3092 if( p_es && p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
3093 return input_DecoderAddVoutOverlay( p_es->p_dec, sub, channel );
3094 return VLC_EGENERIC;
3096 case ES_OUT_VOUT_FLUSH_OVERLAY:
3098 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3099 int channel = va_arg( args, int );
3100 if( p_es && p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
3101 return input_DecoderFlushVoutOverlay( p_es->p_dec, channel );
3102 return VLC_EGENERIC;
3104 case ES_OUT_SPU_SET_HIGHLIGHT:
3106 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3107 const vlc_spu_highlight_t *spu_hl =
3108 va_arg( args, const vlc_spu_highlight_t * );
3109 if( p_es && p_es->fmt.i_cat == SPU_ES && p_es->p_dec )
3110 return input_DecoderSetSpuHighlight( p_es->p_dec, spu_hl );
3111 return VLC_EGENERIC;
3113 case ES_OUT_SET_VBI_PAGE:
3114 case ES_OUT_SET_VBI_TRANSPARENCY:
3116 es_out_id_t *es = va_arg( args, es_out_id_t * );
3117 assert(es);
3118 if( !es->p_dec || es->fmt.i_cat != SPU_ES
3119 || es->fmt.i_codec != VLC_CODEC_TELETEXT )
3120 return VLC_EGENERIC;
3122 int ret;
3123 if( i_query == ES_OUT_SET_VBI_PAGE )
3125 unsigned page = va_arg( args, unsigned );
3126 ret = var_SetInteger( es->p_dec, "vbi-page", page );
3127 if( ret == VLC_SUCCESS )
3128 input_SendEventVbiPage( p_sys->p_input, page );
3130 else
3132 bool opaque = va_arg( args, int );
3133 ret = var_SetBool( es->p_dec, "vbi-opaque", opaque );
3134 if( ret == VLC_SUCCESS )
3135 input_SendEventVbiTransparency( p_sys->p_input, opaque );
3137 return ret;
3139 default:
3140 msg_Err( p_sys->p_input, "unknown query 0x%x in %s", i_query,
3141 __func__ );
3142 return VLC_EGENERIC;
3145 static int EsOutControl( es_out_t *out, int i_query, va_list args )
3147 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3148 int i_ret;
3150 vlc_mutex_lock( &p_sys->lock );
3151 i_ret = EsOutVaControlLocked( out, i_query, args );
3152 vlc_mutex_unlock( &p_sys->lock );
3154 return i_ret;
3157 static const struct es_out_callbacks es_out_cbs =
3159 .add = EsOutAdd,
3160 .send = EsOutSend,
3161 .del = EsOutDel,
3162 .control = EsOutControl,
3163 .destroy = EsOutDelete,
3166 /****************************************************************************
3167 * LanguageGetName: try to expend iso639 into plain name
3168 ****************************************************************************/
3169 static char *LanguageGetName( const char *psz_code )
3171 const iso639_lang_t *pl;
3173 if( psz_code == NULL || !strcmp( psz_code, "und" ) )
3175 return strdup( "" );
3178 if( strlen( psz_code ) == 2 )
3180 pl = GetLang_1( psz_code );
3182 else if( strlen( psz_code ) == 3 )
3184 pl = GetLang_2B( psz_code );
3185 if( !strcmp( pl->psz_iso639_1, "??" ) )
3187 pl = GetLang_2T( psz_code );
3190 else
3192 char *lang = LanguageGetCode( psz_code );
3193 pl = GetLang_1( lang );
3194 free( lang );
3197 if( !strcmp( pl->psz_iso639_1, "??" ) )
3199 return strdup( psz_code );
3201 else
3203 return strdup( vlc_gettext(pl->psz_eng_name) );
3207 /* Get a 2 char code */
3208 static char *LanguageGetCode( const char *psz_lang )
3210 const iso639_lang_t *pl;
3212 if( psz_lang == NULL || *psz_lang == '\0' )
3213 return strdup("??");
3215 for( pl = p_languages; pl->psz_eng_name != NULL; pl++ )
3217 if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
3218 !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
3219 !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
3220 !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
3221 return strdup( pl->psz_iso639_1 );
3224 return strdup("??");
3227 static char **LanguageSplit( const char *psz_langs )
3229 char *psz_dup;
3230 char *psz_parser;
3231 char **ppsz = NULL;
3232 int i_psz = 0;
3234 if( psz_langs == NULL ) return NULL;
3236 psz_parser = psz_dup = strdup(psz_langs);
3238 while( psz_parser && *psz_parser )
3240 char *psz;
3241 char *psz_code;
3243 psz = strchr(psz_parser, ',' );
3244 if( psz ) *psz++ = '\0';
3246 if( !strcmp( psz_parser, "any" ) )
3248 TAB_APPEND( i_psz, ppsz, strdup("any") );
3250 else if( !strcmp( psz_parser, "none" ) )
3252 TAB_APPEND( i_psz, ppsz, strdup("none") );
3254 else
3256 psz_code = LanguageGetCode( psz_parser );
3257 if( strcmp( psz_code, "??" ) )
3259 TAB_APPEND( i_psz, ppsz, psz_code );
3261 else
3263 free( psz_code );
3267 psz_parser = psz;
3270 if( i_psz )
3272 TAB_APPEND( i_psz, ppsz, NULL );
3275 free( psz_dup );
3276 return ppsz;
3279 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang )
3281 if( !ppsz_langs || !psz_lang )
3282 return -1;
3284 for( int i = 0; ppsz_langs[i]; i++ )
3286 if( !strcasecmp( ppsz_langs[i], psz_lang ) ||
3287 ( !strcasecmp( ppsz_langs[i], "any" ) && strcasecmp( psz_lang, "none") ) )
3288 return i;
3289 if( !strcasecmp( ppsz_langs[i], "none" ) )
3290 break;
3293 return -1;
3296 static int EsOutEsUpdateFmt(es_out_t *out, es_out_id_t *es,
3297 const es_format_t *fmt)
3299 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3300 input_thread_t *p_input = p_sys->p_input;
3302 assert(es->fmt.i_cat == fmt->i_cat);
3304 es_format_t update = *fmt;
3306 /* decoder may overwrite these values */
3307 update.i_id = es->fmt.i_id;
3308 update.i_group = es->fmt.i_group;
3309 update.i_priority = es->fmt.i_priority;
3310 update.i_codec = es->fmt.i_codec;
3311 update.i_original_fourcc = es->fmt.i_original_fourcc;
3313 if (update.psz_language == NULL)
3314 update.psz_language = es->fmt.psz_language;
3315 if (update.psz_description == NULL)
3316 update.psz_description = es->fmt.psz_description;
3317 if (update.i_cat == SPU_ES && update.subs.psz_encoding == NULL)
3318 update.subs.psz_encoding = es->fmt.subs.psz_encoding;
3319 if (update.i_extra_languages == 0)
3321 assert(update.p_extra_languages == NULL);
3322 update.i_extra_languages = es->fmt.i_extra_languages;
3323 update.p_extra_languages = es->fmt.p_extra_languages;
3326 es_format_Clean(&es->fmt_out);
3327 int ret = es_format_Copy(&es->fmt_out, &update);
3328 if (ret == VLC_SUCCESS)
3330 EsOutUpdateEsLanguageTitle(es, &es->fmt_out);
3331 input_item_UpdateTracksInfo(input_GetItem(p_input), &es->fmt_out);
3334 return ret;
3337 /****************************************************************************
3338 * EsOutUpdateInfo:
3339 * - add meta info to the playlist item
3340 ****************************************************************************/
3341 static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const vlc_meta_t *p_meta )
3343 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3344 input_thread_t *p_input = p_sys->p_input;
3345 input_item_t *p_item = input_priv(p_input)->p_item;
3346 const es_format_t *p_fmt_es = &es->fmt;
3347 const es_format_t *fmt = es->fmt_out.i_cat != UNKNOWN_ES ? &es->fmt_out : &es->fmt;
3349 input_item_UpdateTracksInfo( p_item , fmt );
3351 /* Create category */
3352 char* psz_cat = EsInfoCategoryName( es );
3354 if( unlikely( !psz_cat ) )
3355 return;
3357 info_category_t* p_cat = info_category_New( psz_cat );
3359 free( psz_cat );
3361 if( unlikely( !p_cat ) )
3362 return;
3364 /* Add information */
3365 if( es->i_meta_id != es->fmt.i_id )
3366 info_category_AddInfo( p_cat, _("Original ID"),
3367 "%d", es->fmt.i_id );
3369 const vlc_fourcc_t i_codec_fourcc = p_fmt_es->i_original_fourcc;
3370 const char *psz_codec_description =
3371 vlc_fourcc_GetDescription( p_fmt_es->i_cat, i_codec_fourcc );
3372 if( psz_codec_description && *psz_codec_description )
3373 info_category_AddInfo( p_cat, _("Codec"), "%s (%.4s)",
3374 psz_codec_description, (char*)&i_codec_fourcc );
3375 else if ( i_codec_fourcc != VLC_FOURCC(0,0,0,0) )
3376 info_category_AddInfo( p_cat, _("Codec"), "%.4s",
3377 (char*)&i_codec_fourcc );
3379 if( es->psz_language && *es->psz_language )
3380 info_category_AddInfo( p_cat, _("Language"), "%s",
3381 es->psz_language );
3382 if( fmt->psz_description && *fmt->psz_description )
3383 info_category_AddInfo( p_cat, _("Description"), "%s",
3384 fmt->psz_description );
3386 switch( fmt->i_cat )
3388 case AUDIO_ES:
3389 info_category_AddInfo( p_cat, _("Type"), _("Audio") );
3391 if( fmt->audio.i_physical_channels )
3392 info_category_AddInfo( p_cat, _("Channels"), "%s",
3393 vlc_gettext( aout_FormatPrintChannels( &fmt->audio ) ) );
3395 if( fmt->audio.i_rate != 0 )
3397 info_category_AddInfo( p_cat, _("Sample rate"), _("%u Hz"),
3398 fmt->audio.i_rate );
3399 /* FIXME that should be removed or improved ! (used by text/strings.c) */
3400 var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate );
3403 unsigned int i_bitspersample = fmt->audio.i_bitspersample;
3404 if( i_bitspersample == 0 )
3405 i_bitspersample = aout_BitsPerSample( fmt->i_codec );
3406 if( i_bitspersample != 0 )
3407 info_category_AddInfo( p_cat, _("Bits per sample"), "%u",
3408 i_bitspersample );
3410 if( fmt->i_bitrate != 0 )
3412 info_category_AddInfo( p_cat, _("Bitrate"), _("%u kb/s"),
3413 fmt->i_bitrate / 1000 );
3414 /* FIXME that should be removed or improved ! (used by text/strings.c) */
3415 var_SetInteger( p_input, "bit-rate", fmt->i_bitrate );
3417 for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
3419 const audio_replay_gain_t *p_rg = &fmt->audio_replay_gain;
3420 if( !p_rg->pb_gain[i] )
3421 continue;
3422 const char *psz_name;
3423 if( i == AUDIO_REPLAY_GAIN_TRACK )
3424 psz_name = _("Track replay gain");
3425 else
3426 psz_name = _("Album replay gain");
3427 info_category_AddInfo( p_cat, psz_name, _("%.2f dB"),
3428 p_rg->pf_gain[i] );
3430 break;
3432 case VIDEO_ES:
3433 info_category_AddInfo( p_cat, _("Type"), _("Video") );
3435 if( fmt->video.i_visible_width > 0 &&
3436 fmt->video.i_visible_height > 0 )
3437 info_category_AddInfo( p_cat, _("Video resolution"), "%ux%u",
3438 fmt->video.i_visible_width,
3439 fmt->video.i_visible_height);
3441 if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
3442 info_category_AddInfo( p_cat, _("Buffer dimensions"), "%ux%u",
3443 fmt->video.i_width, fmt->video.i_height );
3445 if( fmt->video.i_frame_rate > 0 &&
3446 fmt->video.i_frame_rate_base > 0 )
3448 if( fmt->video.i_frame_rate_base == 1 )
3449 info_category_AddInfo( p_cat, _("Frame rate"), "%u",
3450 fmt->video.i_frame_rate );
3451 else
3452 info_category_AddInfo( p_cat, _("Frame rate"), "%.6f",
3453 (double)fmt->video.i_frame_rate
3454 / (double)fmt->video.i_frame_rate_base );
3456 if( fmt->i_codec != p_fmt_es->i_codec )
3458 const char *psz_chroma_description =
3459 vlc_fourcc_GetDescription( VIDEO_ES, fmt->i_codec );
3460 if( psz_chroma_description )
3461 info_category_AddInfo( p_cat, _("Decoded format"), "%s",
3462 psz_chroma_description );
3465 static const char orient_names[][13] = {
3466 N_("Top left"), N_("Left top"),
3467 N_("Right bottom"), N_("Top right"),
3468 N_("Bottom left"), N_("Bottom right"),
3469 N_("Left bottom"), N_("Right top"),
3471 info_category_AddInfo( p_cat, _("Orientation"), "%s",
3472 vlc_gettext(orient_names[fmt->video.orientation]) );
3474 if( fmt->video.primaries != COLOR_PRIMARIES_UNDEF )
3476 static const char primaries_names[][32] = {
3477 [COLOR_PRIMARIES_UNDEF] = N_("Undefined"),
3478 [COLOR_PRIMARIES_BT601_525] =
3479 N_("ITU-R BT.601 (525 lines, 60 Hz)"),
3480 [COLOR_PRIMARIES_BT601_625] =
3481 N_("ITU-R BT.601 (625 lines, 50 Hz)"),
3482 [COLOR_PRIMARIES_BT709] = "ITU-R BT.709",
3483 [COLOR_PRIMARIES_BT2020] = "ITU-R BT.2020",
3484 [COLOR_PRIMARIES_DCI_P3] = "DCI/P3 D65",
3485 [COLOR_PRIMARIES_BT470_M] = "ITU-R BT.470 M",
3487 static_assert(ARRAY_SIZE(primaries_names) == COLOR_PRIMARIES_MAX+1,
3488 "Color primiaries table mismatch");
3489 info_category_AddInfo( p_cat, _("Color primaries"), "%s",
3490 vlc_gettext(primaries_names[fmt->video.primaries]) );
3492 if( fmt->video.transfer != TRANSFER_FUNC_UNDEF )
3494 static const char func_names[][20] = {
3495 [TRANSFER_FUNC_UNDEF] = N_("Undefined"),
3496 [TRANSFER_FUNC_LINEAR] = N_("Linear"),
3497 [TRANSFER_FUNC_SRGB] = "sRGB",
3498 [TRANSFER_FUNC_BT470_BG] = "ITU-R BT.470 BG",
3499 [TRANSFER_FUNC_BT470_M] = "ITU-R BT.470 M",
3500 [TRANSFER_FUNC_BT709] = "ITU-R BT.709",
3501 [TRANSFER_FUNC_SMPTE_ST2084] = "SMPTE ST2084",
3502 [TRANSFER_FUNC_SMPTE_240] = "SMPTE 240M",
3503 [TRANSFER_FUNC_HLG] = N_("Hybrid Log-Gamma"),
3505 static_assert(ARRAY_SIZE(func_names) == TRANSFER_FUNC_MAX+1,
3506 "Transfer functions table mismatch");
3507 info_category_AddInfo( p_cat, _("Color transfer function"), "%s",
3508 vlc_gettext(func_names[fmt->video.transfer]) );
3510 if( fmt->video.space != COLOR_SPACE_UNDEF )
3512 static const char space_names[][16] = {
3513 [COLOR_SPACE_UNDEF] = N_("Undefined"),
3514 [COLOR_SPACE_BT601] = "ITU-R BT.601",
3515 [COLOR_SPACE_BT709] = "ITU-R BT.709",
3516 [COLOR_SPACE_BT2020] = "ITU-R BT.2020",
3518 static_assert(ARRAY_SIZE(space_names) == COLOR_SPACE_MAX+1,
3519 "Color space table mismatch");
3520 info_category_AddInfo( p_cat, _("Color space"), "%s",
3521 vlc_gettext(space_names[fmt->video.space]) );
3523 if( fmt->video.color_range != COLOR_RANGE_UNDEF )
3525 static const char range_names[][16] = {
3526 [COLOR_RANGE_UNDEF] = N_("Undefined"),
3527 [COLOR_RANGE_FULL] = N_("Full"),
3528 [COLOR_RANGE_LIMITED] = N_("Limited"),
3530 static_assert(ARRAY_SIZE(range_names) == COLOR_RANGE_MAX+1,
3531 "Color range table mismatch");
3532 info_category_AddInfo( p_cat, _("Color Range"), "%s",
3533 vlc_gettext(range_names[fmt->video.color_range]) );
3535 if( fmt->video.chroma_location != CHROMA_LOCATION_UNDEF )
3537 static const char c_loc_names[][16] = {
3538 [CHROMA_LOCATION_UNDEF] = N_("Undefined"),
3539 [CHROMA_LOCATION_LEFT] = N_("Left"),
3540 [CHROMA_LOCATION_CENTER] = N_("Center"),
3541 [CHROMA_LOCATION_TOP_LEFT] = N_("Top Left"),
3542 [CHROMA_LOCATION_TOP_CENTER] = N_("Top Center"),
3543 [CHROMA_LOCATION_BOTTOM_LEFT] =N_("Bottom Left"),
3544 [CHROMA_LOCATION_BOTTOM_CENTER] = N_("Bottom Center"),
3546 static_assert(ARRAY_SIZE(c_loc_names) == CHROMA_LOCATION_MAX+1,
3547 "Chroma location table mismatch");
3548 info_category_AddInfo( p_cat, _("Chroma location"), "%s",
3549 vlc_gettext(c_loc_names[fmt->video.chroma_location]) );
3551 if( fmt->video.multiview_mode != MULTIVIEW_2D )
3553 static const char c_multiview_names[][18] = {
3554 [MULTIVIEW_2D] = N_("2D"),
3555 [MULTIVIEW_STEREO_SBS] = N_("Side-By-Side"),
3556 [MULTIVIEW_STEREO_TB] = N_("Top-Bottom"),
3557 [MULTIVIEW_STEREO_ROW] = N_("Row Sequential"),
3558 [MULTIVIEW_STEREO_COL] = N_("Column Sequential"),
3559 [MULTIVIEW_STEREO_FRAME] =N_("Frame Sequential"),
3560 [MULTIVIEW_STEREO_CHECKERBOARD] = N_("Checkboard"),
3562 static_assert(ARRAY_SIZE(c_multiview_names) == MULTIVIEW_STEREO_MAX+1,
3563 "Multiview format table mismatch");
3564 info_category_AddInfo( p_cat, _("Stereo Mode"), "%s",
3565 vlc_gettext(c_multiview_names[fmt->video.multiview_mode]) );
3566 info_category_AddInfo( p_cat, _("First Stereo Eye"),
3567 vlc_gettext(fmt->video.b_multiview_right_eye_first ?
3568 N_("Right") : N_("Left")) );
3570 if( fmt->video.projection_mode != PROJECTION_MODE_RECTANGULAR )
3572 const char *psz_loc_name = NULL;
3573 switch (fmt->video.projection_mode)
3575 case PROJECTION_MODE_RECTANGULAR:
3576 psz_loc_name = N_("Rectangular");
3577 break;
3578 case PROJECTION_MODE_EQUIRECTANGULAR:
3579 psz_loc_name = N_("Equirectangular");
3580 break;
3581 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
3582 psz_loc_name = N_("Cubemap");
3583 break;
3584 default:
3585 vlc_assert_unreachable();
3586 break;
3588 info_category_AddInfo( p_cat, _("Projection"), "%s",
3589 vlc_gettext(psz_loc_name) );
3591 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Yaw"),
3592 "%.2f", fmt->video.pose.yaw );
3593 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Pitch"),
3594 "%.2f", fmt->video.pose.pitch );
3595 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Roll"),
3596 "%.2f", fmt->video.pose.roll );
3597 info_category_AddInfo( p_cat,
3598 vlc_pgettext("ViewPoint", "Field of view"),
3599 "%.2f", fmt->video.pose.fov );
3601 if ( fmt->video.mastering.max_luminance )
3603 info_category_AddInfo( p_cat, _("Max. mastering luminance"), "%.4f cd/m²",
3604 fmt->video.mastering.max_luminance / 10000.f );
3606 if ( fmt->video.mastering.min_luminance )
3608 info_category_AddInfo( p_cat, _("Min. mastering luminance"), "%.4f cd/m²",
3609 fmt->video.mastering.min_luminance / 10000.f );
3611 if ( fmt->video.mastering.primaries[4] &&
3612 fmt->video.mastering.primaries[5] )
3614 float x = (float)fmt->video.mastering.primaries[4] / 50000.f;
3615 float y = (float)fmt->video.mastering.primaries[5] / 50000.f;
3616 info_category_AddInfo( p_cat, _("Mastering Primary R"), "x=%.4f y=%.4f", x, y );
3618 if ( fmt->video.mastering.primaries[0] &&
3619 fmt->video.mastering.primaries[1] )
3621 float x = (float)fmt->video.mastering.primaries[0] / 50000.f;
3622 float y = (float)fmt->video.mastering.primaries[1] / 50000.f;
3623 info_category_AddInfo( p_cat, _("Mastering Primary G"), "x=%.4f y=%.4f", x, y );
3625 if ( fmt->video.mastering.primaries[2] &&
3626 fmt->video.mastering.primaries[3] )
3628 float x = (float)fmt->video.mastering.primaries[2] / 50000.f;
3629 float y = (float)fmt->video.mastering.primaries[3] / 50000.f;
3630 info_category_AddInfo( p_cat, _("Mastering Primary B"), "x=%.4f y=%.4f", x, y );
3632 if ( fmt->video.mastering.white_point[0] &&
3633 fmt->video.mastering.white_point[1] )
3635 float x = (float)fmt->video.mastering.white_point[0] / 50000.f;
3636 float y = (float)fmt->video.mastering.white_point[1] / 50000.f;
3637 info_category_AddInfo( p_cat, _("Mastering White point"), "x=%.4f y=%.4f", x, y );
3639 if ( fmt->video.lighting.MaxCLL )
3641 info_category_AddInfo( p_cat, "MaxCLL", "%" PRIu16 " cd/m²",
3642 fmt->video.lighting.MaxCLL );
3644 if ( fmt->video.lighting.MaxFALL )
3646 info_category_AddInfo( p_cat, "MaxFALL", "%" PRIu16 " cd/m²",
3647 fmt->video.lighting.MaxFALL );
3649 break;
3651 case SPU_ES:
3652 info_category_AddInfo( p_cat, _("Type"), _("Subtitle") );
3653 break;
3655 default:
3656 break;
3659 /* Append generic meta */
3660 if( p_meta )
3662 char **ppsz_all_keys = vlc_meta_CopyExtraNames( p_meta );
3663 for( int i = 0; ppsz_all_keys && ppsz_all_keys[i]; i++ )
3665 char *psz_key = ppsz_all_keys[i];
3666 const char *psz_value = vlc_meta_GetExtra( p_meta, psz_key );
3668 if( psz_value )
3669 info_category_AddInfo( p_cat, vlc_gettext(psz_key), "%s",
3670 vlc_gettext(psz_value) );
3671 free( psz_key );
3673 free( ppsz_all_keys );
3675 /* */
3676 input_item_ReplaceInfos( p_item, p_cat );
3677 if( !input_priv(p_input)->b_preparsing )
3678 input_SendEventMetaInfo( p_input );
3681 static void EsOutDeleteInfoEs( es_out_t *out, es_out_id_t *es )
3683 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3684 input_thread_t *p_input = p_sys->p_input;
3685 input_item_t *p_item = input_priv(p_input)->p_item;
3686 char* psz_info_category;
3688 if( likely( psz_info_category = EsInfoCategoryName( es ) ) )
3690 int ret = input_item_DelInfo( p_item, psz_info_category, NULL );
3691 free( psz_info_category );
3693 if( ret == VLC_SUCCESS && !input_priv(p_input)->b_preparsing )
3694 input_SendEventMetaInfo( p_input );
3698 static inline es_out_id_t *vlc_es_id_get_out(vlc_es_id_t *id)
3700 return container_of(id, es_out_id_t, id);
3703 vlc_es_id_t *
3704 vlc_es_id_Hold(vlc_es_id_t *id)
3706 EsHold(vlc_es_id_get_out(id));
3707 return id;
3710 void
3711 vlc_es_id_Release(vlc_es_id_t *id)
3713 EsRelease(vlc_es_id_get_out(id));
3717 vlc_es_id_GetInputId(vlc_es_id_t *id)
3719 return id->i_id;
3722 enum es_format_category_e
3723 vlc_es_id_GetCat(vlc_es_id_t *id)
3725 return id->i_cat;