qt: playlist: use item title if available
[vlc.git] / src / input / es_out.c
bloba209031406d4a25727a7086763ff664272f35ce3
1 /*****************************************************************************
2 * es_out.c: Es Out handler for input.
3 *****************************************************************************
4 * Copyright (C) 2003-2019 VLC authors, VideoLAN and Videolabs SAS
6 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7 * Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <stdio.h>
32 #include <assert.h>
33 #include <vlc_common.h>
35 #include <vlc_es_out.h>
36 #include <vlc_block.h>
37 #include <vlc_aout.h>
38 #include <vlc_fourcc.h>
39 #include <vlc_meta.h>
40 #include <vlc_list.h>
41 #include <vlc_decoder.h>
42 #include <vlc_memstream.h>
44 #include "input_internal.h"
45 #include "../clock/input_clock.h"
46 #include "../clock/clock.h"
47 #include "decoder.h"
48 #include "es_out.h"
49 #include "event.h"
50 #include "resource.h"
51 #include "info.h"
52 #include "item.h"
54 #include "../stream_output/stream_output.h"
56 #include <vlc_iso_lang.h>
57 /* FIXME we should find a better way than including that */
58 #include "../text/iso-639_def.h"
60 /*****************************************************************************
61 * Local prototypes
62 *****************************************************************************/
63 typedef struct
65 /* Program context */
66 input_source_t *source;
68 /* Program ID */
69 int i_id;
71 /* Number of es for this pgrm */
72 int i_es;
74 bool b_selected;
75 bool b_scrambled;
77 /* Clock for this program */
78 input_clock_t *p_input_clock;
79 vlc_clock_main_t *p_main_clock;
80 const vlc_clock_t *p_master_clock;
82 vlc_tick_t i_last_pcr;
84 vlc_meta_t *p_meta;
85 struct vlc_list node;
86 } es_out_pgrm_t;
89 /**
90 * Opaque structure representing an ES (Elementary Stream) track.
92 * This structure is propagated via the vlc_input_event_es event
94 struct vlc_es_id_t
96 int i_id;
97 enum es_format_category_e i_cat;
98 /* Input source used to create this ES. Equals to p_pgrm->source when not
99 * using a default program id. */
100 input_source_t *source;
101 bool stable;
102 char *str_id;
105 struct es_out_id_t
107 vlc_es_id_t id;
109 /* weak reference, used by input_decoder_callbacks and vlc_clock_cbs */
110 es_out_t *out;
112 /* ES ID */
113 es_out_pgrm_t *p_pgrm;
115 /* */
116 bool b_scrambled;
118 /* Channel in the track type */
119 int i_channel;
121 vlc_atomic_rc_t rc;
123 size_t i_pos; /* position, used to get the title of the track */
124 es_format_t fmt; /* input fmt from the demuxer */
125 es_format_t fmt_out; /* updated fmt (by the decoder) */
126 char *psz_language;
127 char *psz_language_code;
128 char *psz_title;
129 bool b_terminated;
131 vlc_input_decoder_t *p_dec;
132 vlc_input_decoder_t *p_dec_record;
133 vlc_clock_t *p_clock;
135 /* Used by vlc_clock_cbs, need to be const during the lifetime of the clock */
136 bool master;
138 vlc_tick_t i_pts_level;
139 vlc_tick_t delay;
141 /* Fields for Video with CC */
142 struct
144 vlc_fourcc_t type;
145 uint64_t i_bitmap; /* channels bitmap */
146 es_out_id_t *pp_es[64]; /* a max of 64 chans for CEA708 */
147 } cc;
149 /* Field for CC track from a master video */
150 es_out_id_t *p_master;
152 struct vlc_list node;
154 vlc_mouse_event mouse_event_cb;
155 void* mouse_event_userdata;
158 typedef struct
160 int i_count; /* es count */
161 es_out_id_t *p_main_es; /* current main es */
162 enum es_out_policy_e e_policy;
164 /* Parameters used for es selection */
165 bool b_autoselect; /* if we want to select an es when no user prefs */
166 char *str_ids; /* List of string id generated by EsOutCreateStrId() (delimited by ',') */
167 int i_demux_id; /* same as previous, demuxer set default value */
168 int i_channel; /* es number in creation order */
169 char **ppsz_language;
170 } es_out_es_props_t;
172 typedef struct
174 input_thread_t *p_input;
176 input_source_t *main_source;
178 /* */
179 vlc_mutex_t lock;
181 /* all programs */
182 struct vlc_list programs;
183 es_out_pgrm_t *p_pgrm; /* Master program */
185 enum vlc_clock_master_source clock_source;
187 /* all es */
188 int i_id;
189 struct vlc_list es;
190 struct vlc_list es_slaves; /* Dynamically created es on regular es selection */
192 /* mode gestion */
193 bool b_active;
194 int i_mode;
196 es_out_es_props_t video, audio, sub;
198 /* es/group to select */
199 int i_group_id;
201 /* delay */
202 vlc_tick_t i_audio_delay;
203 vlc_tick_t i_spu_delay;
205 /* Clock configuration */
206 vlc_tick_t i_pts_delay;
207 vlc_tick_t i_tracks_pts_delay;
208 vlc_tick_t i_pts_jitter;
209 int i_cr_average;
210 float rate;
212 /* */
213 bool b_paused;
214 vlc_tick_t i_pause_date;
216 /* Current preroll */
217 vlc_tick_t i_preroll_end;
219 /* Used for buffering */
220 bool b_buffering;
221 vlc_tick_t i_buffering_extra_initial;
222 vlc_tick_t i_buffering_extra_stream;
223 vlc_tick_t i_buffering_extra_system;
225 /* Record */
226 sout_instance_t *p_sout_record;
228 /* Used only to limit debugging output */
229 int i_prev_stream_level;
231 es_out_t out;
232 } es_out_sys_t;
234 static void EsOutDelLocked( es_out_t *, es_out_id_t * );
235 static void EsOutDel ( es_out_t *, es_out_id_t * );
237 static void EsOutTerminate( es_out_t * );
238 static void EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force );
239 static void EsOutSelectList( es_out_t *, enum es_format_category_e cat,
240 vlc_es_id_t *const* es_id_list );
241 static void EsOutUpdateInfo( es_out_t *, es_out_id_t *es, const vlc_meta_t * );
242 static int EsOutSetRecord( es_out_t *, bool b_record );
244 static bool EsIsSelected( es_out_id_t *es );
245 static void EsOutSelectEs( es_out_t *out, es_out_id_t *es, bool b_force );
246 static void EsOutDeleteInfoEs( es_out_t *, es_out_id_t *es );
247 static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update );
248 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
249 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date );
250 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date );
251 static void EsOutProgramsChangeRate( es_out_t *out );
252 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
253 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta );
254 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_progmeta );
255 static int EsOutEsUpdateFmt(es_out_t *out, es_out_id_t *es, const es_format_t *fmt);
256 static int EsOutControlLocked( es_out_t *out, input_source_t *, int i_query, ... );
257 static int EsOutPrivControlLocked( es_out_t *out, int i_query, ... );
259 static char *LanguageGetName( const char *psz_code );
260 static char *LanguageGetCode( const char *psz_lang );
261 static char **LanguageSplit( const char *psz_langs );
262 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang );
264 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
265 static char *EsInfoCategoryName( es_out_id_t* es );
267 struct clock_source_mapping
269 char key[sizeof("monotonic")];
270 enum vlc_clock_master_source val;
273 static int clock_source_mapping_cmp(const void *key, const void *val)
275 const struct clock_source_mapping *entry = val;
276 return strcasecmp( key, entry->key );
279 static enum vlc_clock_master_source
280 clock_source_Inherit(vlc_object_t *obj)
282 static const struct clock_source_mapping clock_source_list[] =
284 { "0", VLC_CLOCK_MASTER_AUDIO }, /* legacy option */
285 { "1", VLC_CLOCK_MASTER_MONOTONIC }, /* legacy option */
286 { "audio", VLC_CLOCK_MASTER_AUDIO },
287 { "auto", VLC_CLOCK_MASTER_AUTO },
288 { "input", VLC_CLOCK_MASTER_INPUT },
289 { "monotonic", VLC_CLOCK_MASTER_MONOTONIC },
292 char *master_source_str = var_InheritString(obj, "clock-master");
293 if (master_source_str == NULL)
294 goto default_val;
296 const struct clock_source_mapping *entry =
297 bsearch(master_source_str, clock_source_list, ARRAY_SIZE(clock_source_list),
298 sizeof (*clock_source_list), clock_source_mapping_cmp);
299 free(master_source_str);
300 if (entry == NULL)
301 goto default_val;
303 return entry->val;
305 default_val:
306 return VLC_CLOCK_MASTER_AUTO;
309 static inline int EsOutGetClosedCaptionsChannel( const es_format_t *p_fmt )
311 int i_channel;
312 if( p_fmt->i_codec == VLC_CODEC_CEA608 && p_fmt->subs.cc.i_channel < 4 )
313 i_channel = p_fmt->subs.cc.i_channel;
314 else if( p_fmt->i_codec == VLC_CODEC_CEA708 && p_fmt->subs.cc.i_channel < 64 )
315 i_channel = p_fmt->subs.cc.i_channel;
316 else
317 i_channel = -1;
318 return i_channel;
321 #define foreach_es_then_es_slaves( pos ) \
322 for( int fetes_i=0; fetes_i<2; fetes_i++ ) \
323 vlc_list_foreach( pos, (!fetes_i ? &p_sys->es : &p_sys->es_slaves), node )
325 static void
326 decoder_on_vout_started(vlc_input_decoder_t *decoder, vout_thread_t *vout,
327 enum vlc_vout_order order, void *userdata)
329 (void) decoder;
331 es_out_id_t *id = userdata;
332 es_out_t *out = id->out;
333 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
335 if (!p_sys->p_input)
336 return;
338 struct vlc_input_event_vout event = {
339 .action = VLC_INPUT_EVENT_VOUT_STARTED,
340 .vout = vout,
341 .order = order,
342 .id = &id->id,
345 input_SendEventVout(p_sys->p_input, &event);
348 static void
349 decoder_on_vout_stopped(vlc_input_decoder_t *decoder, vout_thread_t *vout, void *userdata)
351 (void) decoder;
353 es_out_id_t *id = userdata;
354 es_out_t *out = id->out;
355 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
357 if (!p_sys->p_input)
358 return;
360 struct vlc_input_event_vout event = {
361 .action = VLC_INPUT_EVENT_VOUT_STOPPED,
362 .vout = vout,
363 .order = VLC_VOUT_ORDER_NONE,
364 .id = &id->id,
367 input_SendEventVout(p_sys->p_input, &event);
370 static void
371 decoder_on_thumbnail_ready(vlc_input_decoder_t *decoder, picture_t *pic, void *userdata)
373 (void) decoder;
375 es_out_id_t *id = userdata;
376 es_out_t *out = id->out;
377 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
379 if (!p_sys->p_input)
380 return;
382 struct vlc_input_event event = {
383 .type = INPUT_EVENT_THUMBNAIL_READY,
384 .thumbnail = pic,
387 input_SendEvent(p_sys->p_input, &event);
390 static void
391 decoder_on_new_video_stats(vlc_input_decoder_t *decoder, unsigned decoded, unsigned lost,
392 unsigned displayed, unsigned late, void *userdata)
394 (void) decoder;
396 es_out_id_t *id = userdata;
397 es_out_t *out = id->out;
398 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
400 if (!p_sys->p_input)
401 return;
403 struct input_stats *stats = input_priv(p_sys->p_input)->stats;
404 if (!stats)
405 return;
407 atomic_fetch_add_explicit(&stats->decoded_video, decoded,
408 memory_order_relaxed);
409 atomic_fetch_add_explicit(&stats->lost_pictures, lost,
410 memory_order_relaxed);
411 atomic_fetch_add_explicit(&stats->displayed_pictures, displayed,
412 memory_order_relaxed);
413 atomic_fetch_add_explicit(&stats->late_pictures, late,
414 memory_order_relaxed);
417 static void
418 decoder_on_new_audio_stats(vlc_input_decoder_t *decoder, unsigned decoded, unsigned lost,
419 unsigned played, void *userdata)
421 (void) decoder;
423 es_out_id_t *id = userdata;
424 es_out_t *out = id->out;
425 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
427 if (!p_sys->p_input)
428 return;
430 struct input_stats *stats = input_priv(p_sys->p_input)->stats;
431 if (!stats)
432 return;
434 atomic_fetch_add_explicit(&stats->decoded_audio, decoded,
435 memory_order_relaxed);
436 atomic_fetch_add_explicit(&stats->lost_abuffers, lost,
437 memory_order_relaxed);
438 atomic_fetch_add_explicit(&stats->played_abuffers, played,
439 memory_order_relaxed);
442 static int
443 decoder_get_attachments(vlc_input_decoder_t *decoder,
444 input_attachment_t ***ppp_attachment,
445 void *userdata)
447 (void) decoder;
449 es_out_id_t *id = userdata;
450 es_out_t *out = id->out;
451 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
453 if (!p_sys->p_input)
454 return -1;
456 return input_GetAttachments(p_sys->p_input, ppp_attachment);
459 static const struct vlc_input_decoder_callbacks decoder_cbs = {
460 .on_vout_started = decoder_on_vout_started,
461 .on_vout_stopped = decoder_on_vout_stopped,
462 .on_thumbnail_ready = decoder_on_thumbnail_ready,
463 .on_new_video_stats = decoder_on_new_video_stats,
464 .on_new_audio_stats = decoder_on_new_audio_stats,
465 .get_attachments = decoder_get_attachments,
468 /*****************************************************************************
469 * Es category specific structs
470 *****************************************************************************/
471 static es_out_es_props_t * GetPropsByCat( es_out_sys_t *p_sys, int i_cat )
473 switch( i_cat )
475 case AUDIO_ES:
476 return &p_sys->audio;
477 case SPU_ES:
478 return &p_sys->sub;
479 case VIDEO_ES:
480 return &p_sys->video;
482 return NULL;
485 static void EsOutPropsCleanup( es_out_es_props_t *p_props )
487 free( p_props->str_ids );
488 if( p_props->ppsz_language )
490 for( int i = 0; p_props->ppsz_language[i]; i++ )
491 free( p_props->ppsz_language[i] );
492 free( p_props->ppsz_language );
496 static void EsOutPropsInit( es_out_es_props_t *p_props,
497 bool autoselect,
498 input_thread_t *p_input,
499 enum es_out_policy_e e_default_policy,
500 const char *psz_trackidvar,
501 const char *psz_trackvar,
502 const char *psz_langvar,
503 const char *psz_debug )
505 p_props->e_policy = e_default_policy;
506 p_props->i_count = 0;
507 p_props->b_autoselect = autoselect;
508 p_props->str_ids = (psz_trackidvar) ? var_GetNonEmptyString( p_input, psz_trackidvar ) : NULL;
509 p_props->i_channel = (psz_trackvar) ? var_GetInteger( p_input, psz_trackvar ): -1;
510 p_props->i_demux_id = -1;
511 p_props->p_main_es = NULL;
513 if( !input_priv(p_input)->b_preparsing && psz_langvar )
515 char *psz_string = var_GetString( p_input, psz_langvar );
516 p_props->ppsz_language = LanguageSplit( psz_string );
517 if( p_props->ppsz_language )
519 for( int i = 0; p_props->ppsz_language[i]; i++ )
520 msg_Dbg( p_input, "selected %s language[%d] %s",
521 psz_debug, i, p_props->ppsz_language[i] );
523 free( psz_string );
527 static const struct es_out_callbacks es_out_cbs;
529 /*****************************************************************************
530 * input_EsOutNew:
531 *****************************************************************************/
532 es_out_t *input_EsOutNew( input_thread_t *p_input, input_source_t *main_source, float rate )
534 es_out_sys_t *p_sys = calloc( 1, sizeof( *p_sys ) );
535 if( !p_sys )
536 return NULL;
538 p_sys->out.cbs = &es_out_cbs;
540 vlc_mutex_init( &p_sys->lock );
541 p_sys->p_input = p_input;
542 p_sys->main_source = main_source;
544 p_sys->b_active = false;
545 p_sys->i_mode = ES_OUT_MODE_NONE;
547 vlc_list_init(&p_sys->programs);
548 vlc_list_init(&p_sys->es);
549 vlc_list_init(&p_sys->es_slaves);
551 /* */
552 EsOutPropsInit( &p_sys->video, true, p_input, ES_OUT_ES_POLICY_AUTO,
553 "video-track-id", "video-track", NULL, NULL );
554 EsOutPropsInit( &p_sys->audio, true, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,
555 "audio-track-id", "audio-track", "audio-language", "audio" );
556 EsOutPropsInit( &p_sys->sub, false, p_input, ES_OUT_ES_POLICY_AUTO,
557 "sub-track-id", "sub-track", "sub-language", "sub" );
559 p_sys->i_group_id = var_GetInteger( p_input, "program" );
561 p_sys->clock_source = clock_source_Inherit( VLC_OBJECT(p_input) );
563 p_sys->i_pause_date = -1;
565 p_sys->rate = rate;
567 p_sys->b_buffering = true;
568 p_sys->i_preroll_end = -1;
569 p_sys->i_prev_stream_level = -1;
571 return &p_sys->out;
574 /*****************************************************************************
576 *****************************************************************************/
577 static void EsTerminate(es_out_id_t *es)
579 vlc_list_remove(&es->node);
581 es->b_terminated = true;
584 static char *EsGetTitle( es_out_id_t *es )
586 const es_format_t *fmt = &es->fmt;
587 char *title;
589 /* Take care of the ES description */
590 if( fmt->psz_description && *fmt->psz_description )
592 if( es->psz_language && *es->psz_language )
594 if( asprintf( &title, "%s - [%s]", fmt->psz_description,
595 es->psz_language ) == -1 )
596 title = NULL;
598 else
599 title = strdup( fmt->psz_description );
601 else
603 if( es->psz_language && *es->psz_language )
605 if( asprintf( &title, "%s %zu - [%s]", _("Track"),
606 es->i_pos, es->psz_language ) == -1 )
607 title = NULL;
609 else
611 if( asprintf( &title, "%s %zu", _("Track"), es->i_pos ) == -1 )
612 title = NULL;
616 return title;
619 static void EsRelease(es_out_id_t *es)
621 if (vlc_atomic_rc_dec(&es->rc))
623 free(es->psz_title);
624 free(es->psz_language);
625 free(es->psz_language_code);
626 es_format_Clean(&es->fmt);
627 input_source_Release(es->id.source);
628 free(es->id.str_id);
629 free(es);
633 static void EsHold(es_out_id_t *es)
635 vlc_atomic_rc_inc(&es->rc);
638 static void EsOutDelete( es_out_t *out )
640 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
642 assert(vlc_list_is_empty(&p_sys->es));
643 assert(vlc_list_is_empty(&p_sys->es_slaves));
644 assert(vlc_list_is_empty(&p_sys->programs));
645 assert(p_sys->p_pgrm == NULL);
646 EsOutPropsCleanup( &p_sys->video );
647 EsOutPropsCleanup( &p_sys->audio );
648 EsOutPropsCleanup( &p_sys->sub );
650 free( p_sys );
653 static void ProgramDelete( es_out_pgrm_t *p_pgrm )
655 input_clock_Delete( p_pgrm->p_input_clock );
656 vlc_clock_main_Delete( p_pgrm->p_main_clock );
657 if( p_pgrm->p_meta )
658 vlc_meta_Delete( p_pgrm->p_meta );
659 input_source_Release( p_pgrm->source );
661 free( p_pgrm );
664 static void EsOutTerminate( es_out_t *out )
666 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
667 es_out_id_t *es;
669 if( p_sys->p_sout_record )
670 EsOutSetRecord( out, false );
672 foreach_es_then_es_slaves(es)
674 if (es->p_dec != NULL)
675 vlc_input_decoder_Delete(es->p_dec);
677 EsTerminate(es);
678 EsRelease(es);
681 es_out_pgrm_t *p_pgrm;
682 vlc_list_foreach(p_pgrm, &p_sys->programs, node)
684 vlc_list_remove(&p_pgrm->node);
685 input_SendEventProgramDel( p_sys->p_input, p_pgrm->i_id );
686 ProgramDelete(p_pgrm);
689 p_sys->p_pgrm = NULL;
691 input_item_SetEpgOffline( input_priv(p_sys->p_input)->p_item );
692 input_SendEventMetaEpg( p_sys->p_input );
695 static vlc_tick_t EsOutGetWakeup( es_out_t *out )
697 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
698 input_thread_t *p_input = p_sys->p_input;
700 if( !p_sys->p_pgrm )
701 return 0;
703 /* We do not have a wake up date if the input cannot have its speed
704 * controlled or sout is imposing its own or while buffering
706 * FIXME for !input_priv(p_input)->b_can_pace_control a wake-up time is still needed
707 * to avoid too heavy buffering */
708 if( !input_priv(p_input)->b_can_pace_control ||
709 input_priv(p_input)->b_out_pace_control ||
710 p_sys->b_buffering )
711 return 0;
713 return input_clock_GetWakeup( p_sys->p_pgrm->p_input_clock );
716 static es_out_id_t es_cat[DATA_ES];
718 static es_out_id_t *EsOutGetSelectedCat( es_out_t *out,
719 enum es_format_category_e cat )
721 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
722 es_out_id_t *es;
724 foreach_es_then_es_slaves( es )
725 if( es->fmt.i_cat == cat && EsIsSelected( es ) )
726 return es;
727 return NULL;
730 static bool EsOutDecodersIsEmpty( es_out_t *out )
732 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
733 es_out_id_t *es;
735 if( p_sys->b_buffering && p_sys->p_pgrm )
737 EsOutDecodersStopBuffering( out, true );
738 if( p_sys->b_buffering )
739 return true;
742 foreach_es_then_es_slaves(es)
744 if( es->p_dec && !vlc_input_decoder_IsEmpty( es->p_dec ) )
745 return false;
746 if( es->p_dec_record && !vlc_input_decoder_IsEmpty( es->p_dec_record ) )
747 return false;
749 return true;
752 static void EsOutSetEsDelay(es_out_t *out, es_out_id_t *es, vlc_tick_t delay)
754 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
756 assert(es->fmt.i_cat == AUDIO_ES || es->fmt.i_cat == SPU_ES);
758 es->delay = delay;
760 EsOutDecoderChangeDelay(out, es);
762 /* Update the clock pts delay only if the extra tracks delay changed */
763 EsOutPrivControlLocked(out, ES_OUT_PRIV_SET_JITTER, p_sys->i_pts_delay,
764 p_sys->i_pts_jitter, p_sys->i_cr_average);
767 static void EsOutSetDelay( es_out_t *out, int i_cat, vlc_tick_t i_delay )
769 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
770 es_out_id_t *es;
772 if( i_cat == AUDIO_ES )
773 p_sys->i_audio_delay = i_delay;
774 else if( i_cat == SPU_ES )
775 p_sys->i_spu_delay = i_delay;
777 foreach_es_then_es_slaves(es)
778 EsOutDecoderChangeDelay(out, es);
780 /* Update the clock pts delay only if the extra tracks delay changed */
781 EsOutPrivControlLocked(out, ES_OUT_PRIV_SET_JITTER, p_sys->i_pts_delay,
782 p_sys->i_pts_jitter, p_sys->i_cr_average);
785 static int EsOutSetRecord( es_out_t *out, bool b_record )
787 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
788 input_thread_t *p_input = p_sys->p_input;
789 es_out_id_t *p_es;
791 assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
793 if( b_record )
795 char *psz_path = var_CreateGetNonEmptyString( p_input, "input-record-path" );
796 if( !psz_path )
798 if( var_CountChoices( p_input, "video-es" ) )
799 psz_path = config_GetUserDir( VLC_VIDEOS_DIR );
800 else if( var_CountChoices( p_input, "audio-es" ) )
801 psz_path = config_GetUserDir( VLC_MUSIC_DIR );
802 else
803 psz_path = config_GetUserDir( VLC_DOWNLOAD_DIR );
806 char *psz_sout = NULL; // TODO conf
808 if( !psz_sout && psz_path )
810 char *psz_file = input_item_CreateFilename( input_GetItem(p_input),
811 psz_path,
812 INPUT_RECORD_PREFIX, NULL );
813 if( psz_file )
815 char* psz_file_esc = config_StringEscape( psz_file );
816 if ( psz_file_esc )
818 if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file_esc ) < 0 )
819 psz_sout = NULL;
820 free( psz_file_esc );
822 free( psz_file );
825 free( psz_path );
827 if( !psz_sout )
828 return VLC_EGENERIC;
830 #ifdef ENABLE_SOUT
831 p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
832 #endif
833 free( psz_sout );
835 if( !p_sys->p_sout_record )
836 return VLC_EGENERIC;
838 vlc_list_foreach( p_es, &p_sys->es, node ) /* Only master es */
840 if( !p_es->p_dec )
841 continue;
843 p_es->p_dec_record =
844 vlc_input_decoder_New( VLC_OBJECT(p_input), &p_es->fmt, NULL,
845 input_priv(p_input)->p_resource,
846 p_sys->p_sout_record, false,
847 &decoder_cbs, p_es );
849 if( p_es->p_dec_record && p_sys->b_buffering )
850 vlc_input_decoder_StartWait( p_es->p_dec_record );
853 else
855 vlc_list_foreach( p_es, &p_sys->es, node ) /* Only master es */
857 if( !p_es->p_dec_record )
858 continue;
860 vlc_input_decoder_Delete( p_es->p_dec_record );
861 p_es->p_dec_record = NULL;
863 #ifdef ENABLE_SOUT
864 sout_DeleteInstance( p_sys->p_sout_record );
865 #endif
866 p_sys->p_sout_record = NULL;
869 return VLC_SUCCESS;
871 static void EsOutChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date )
873 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
875 /* XXX the order is important */
876 if( b_paused )
878 EsOutDecodersChangePause( out, true, i_date );
879 EsOutProgramChangePause( out, true, i_date );
881 else
883 if( p_sys->i_buffering_extra_initial > 0 )
885 vlc_tick_t i_stream_start;
886 vlc_tick_t i_system_start;
887 vlc_tick_t i_stream_duration;
888 vlc_tick_t i_system_duration;
889 int i_ret;
890 i_ret = input_clock_GetState( p_sys->p_pgrm->p_input_clock,
891 &i_stream_start, &i_system_start,
892 &i_stream_duration, &i_system_duration );
893 if( !i_ret )
895 /* FIXME pcr != exactly what wanted */
896 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;
897 i_date -= i_used;
899 p_sys->i_buffering_extra_initial = 0;
900 p_sys->i_buffering_extra_stream = 0;
901 p_sys->i_buffering_extra_system = 0;
903 EsOutProgramChangePause( out, false, i_date );
904 EsOutDecodersChangePause( out, false, i_date );
906 EsOutProgramsChangeRate( out );
908 p_sys->b_paused = b_paused;
909 p_sys->i_pause_date = i_date;
912 static void EsOutChangeRate( es_out_t *out, float rate )
914 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
915 es_out_id_t *es;
917 p_sys->rate = rate;
918 EsOutProgramsChangeRate( out );
920 foreach_es_then_es_slaves(es)
921 if( es->p_dec != NULL )
922 vlc_input_decoder_ChangeRate( es->p_dec, rate );
925 static void EsOutChangePosition( es_out_t *out, bool b_flush )
927 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
928 es_out_id_t *p_es;
930 input_SendEventCache( p_sys->p_input, 0.0 );
932 foreach_es_then_es_slaves(p_es)
934 if( p_es->p_dec != NULL )
936 if( b_flush )
937 vlc_input_decoder_Flush( p_es->p_dec );
938 if( !p_sys->b_buffering )
940 vlc_input_decoder_StartWait( p_es->p_dec );
941 if( p_es->p_dec_record != NULL )
942 vlc_input_decoder_StartWait( p_es->p_dec_record );
945 p_es->i_pts_level = VLC_TICK_INVALID;
948 es_out_pgrm_t *pgrm;
949 vlc_list_foreach(pgrm, &p_sys->programs, node)
951 input_clock_Reset(pgrm->p_input_clock);
952 vlc_clock_main_Reset(pgrm->p_main_clock);
953 pgrm->i_last_pcr = VLC_TICK_INVALID;
956 p_sys->b_buffering = true;
957 p_sys->i_buffering_extra_initial = 0;
958 p_sys->i_buffering_extra_stream = 0;
959 p_sys->i_buffering_extra_system = 0;
960 p_sys->i_preroll_end = -1;
961 p_sys->i_prev_stream_level = -1;
964 static void EsOutStopFreeVout( es_out_t *out )
966 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
968 /* Clean up vout after user action (in active mode only). */
969 if( p_sys->b_active )
970 input_resource_StopFreeVout( input_priv(p_sys->p_input)->p_resource );
973 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
975 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
976 es_out_id_t *p_es;
978 vlc_tick_t i_stream_start;
979 vlc_tick_t i_system_start;
980 vlc_tick_t i_stream_duration;
981 vlc_tick_t i_system_duration;
982 if (input_clock_GetState( p_sys->p_pgrm->p_input_clock,
983 &i_stream_start, &i_system_start,
984 &i_stream_duration, &i_system_duration ))
985 return;
987 vlc_tick_t i_preroll_duration = 0;
988 if( p_sys->i_preroll_end >= 0 )
989 i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
991 const vlc_tick_t i_buffering_duration = p_sys->i_pts_delay +
992 p_sys->i_pts_jitter +
993 p_sys->i_tracks_pts_delay +
994 i_preroll_duration +
995 p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
997 if( i_stream_duration <= i_buffering_duration && !b_forced )
999 double f_level;
1000 if (i_buffering_duration == 0)
1001 f_level = 0;
1002 else
1003 f_level = __MAX( (double)i_stream_duration / i_buffering_duration, 0 );
1004 input_SendEventCache( p_sys->p_input, f_level );
1006 int i_level = (int)(100 * f_level);
1007 if( p_sys->i_prev_stream_level != i_level )
1009 msg_Dbg( p_sys->p_input, "Buffering %d%%", i_level );
1010 p_sys->i_prev_stream_level = i_level;
1013 return;
1015 input_SendEventCache( p_sys->p_input, 1.0 );
1017 msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
1018 (int)MS_FROM_VLC_TICK(i_stream_duration), (int)MS_FROM_VLC_TICK(i_system_duration) );
1019 p_sys->b_buffering = false;
1020 p_sys->i_preroll_end = -1;
1021 p_sys->i_prev_stream_level = -1;
1023 if( p_sys->i_buffering_extra_initial > 0 )
1025 /* FIXME wrong ? */
1026 return;
1029 const vlc_tick_t i_decoder_buffering_start = vlc_tick_now();
1030 foreach_es_then_es_slaves(p_es)
1032 if( !p_es->p_dec || p_es->fmt.i_cat == SPU_ES )
1033 continue;
1034 vlc_input_decoder_Wait( p_es->p_dec );
1035 if( p_es->p_dec_record )
1036 vlc_input_decoder_Wait( p_es->p_dec_record );
1039 msg_Dbg( p_sys->p_input, "Decoder wait done in %d ms",
1040 (int)MS_FROM_VLC_TICK(vlc_tick_now() - i_decoder_buffering_start) );
1042 /* Here is a good place to destroy unused vout with every demuxer */
1043 EsOutStopFreeVout( out );
1045 /* */
1046 const vlc_tick_t i_wakeup_delay = VLC_TICK_FROM_MS(10); /* FIXME CLEANUP thread wake up time*/
1047 const vlc_tick_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : vlc_tick_now();
1049 const vlc_tick_t update = i_current_date + i_wakeup_delay - i_buffering_duration;
1051 /* Send the first PCR to the output clock. This will be used as a reference
1052 * point for the sync point. */
1053 vlc_clock_main_SetFirstPcr(p_sys->p_pgrm->p_main_clock, update,
1054 i_stream_start);
1056 input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_input_clock, true, update );
1058 foreach_es_then_es_slaves(p_es)
1060 if( !p_es->p_dec )
1061 continue;
1063 vlc_input_decoder_StopWait( p_es->p_dec );
1064 if( p_es->p_dec_record )
1065 vlc_input_decoder_StopWait( p_es->p_dec_record );
1068 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date )
1070 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1071 es_out_id_t *es;
1073 /* Pause decoders first */
1074 foreach_es_then_es_slaves(es)
1075 if( es->p_dec )
1077 vlc_input_decoder_ChangePause( es->p_dec, b_paused, i_date );
1078 if( es->p_dec_record )
1079 vlc_input_decoder_ChangePause( es->p_dec_record, b_paused,
1080 i_date );
1084 static bool EsOutIsExtraBufferingAllowed( es_out_t *out )
1086 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1087 es_out_id_t *p_es;
1089 size_t i_size = 0;
1090 foreach_es_then_es_slaves(p_es)
1092 if( p_es->p_dec )
1093 i_size += vlc_input_decoder_GetFifoSize( p_es->p_dec );
1094 if( p_es->p_dec_record )
1095 i_size += vlc_input_decoder_GetFifoSize( p_es->p_dec_record );
1097 //msg_Info( out, "----- EsOutIsExtraBufferingAllowed =% 5d KiB -- ", i_size / 1024 );
1099 /* TODO maybe we want to be able to tune it ? */
1100 #if defined(OPTIMIZE_MEMORY)
1101 const size_t i_level_high = 512*1024; /* 0.5 MiB */
1102 #else
1103 const size_t i_level_high = 10*1024*1024; /* 10 MiB */
1104 #endif
1105 return i_size < i_level_high;
1108 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, vlc_tick_t i_date )
1110 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1111 es_out_pgrm_t *pgrm;
1113 vlc_list_foreach(pgrm, &p_sys->programs, node)
1115 input_clock_ChangePause(pgrm->p_input_clock, b_paused, i_date);
1116 vlc_clock_main_ChangePause(pgrm->p_main_clock, i_date, b_paused);
1120 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
1122 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1124 vlc_tick_t i_delay;
1125 if( p_es->delay != INT64_MAX )
1126 i_delay = p_es->delay; /* The track use its own delay, and not a category delay */
1127 else if( p_es->fmt.i_cat == AUDIO_ES )
1128 i_delay = p_sys->i_audio_delay;
1129 else if( p_es->fmt.i_cat == SPU_ES )
1130 i_delay = p_sys->i_spu_delay;
1131 else
1132 return;
1134 if( p_es->p_dec )
1135 vlc_input_decoder_ChangeDelay( p_es->p_dec, i_delay );
1136 if( p_es->p_dec_record )
1137 vlc_input_decoder_ChangeDelay( p_es->p_dec_record, i_delay );
1139 static void EsOutProgramsChangeRate( es_out_t *out )
1141 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1142 es_out_pgrm_t *pgrm;
1144 vlc_list_foreach(pgrm, &p_sys->programs, node)
1145 input_clock_ChangeRate(pgrm->p_input_clock, p_sys->rate);
1148 static void EsOutFrameNext( es_out_t *out )
1150 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1151 es_out_id_t *p_es_video = NULL, *p_es;
1153 if( p_sys->b_buffering )
1155 msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" );
1156 return;
1159 assert( p_sys->b_paused );
1161 foreach_es_then_es_slaves(p_es)
1162 if( p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec && !p_es_video /* nested loop */ )
1164 p_es_video = p_es;
1165 break;
1168 if( !p_es_video )
1170 msg_Warn( p_sys->p_input, "No video track selected, ignoring 'frame next'" );
1171 return;
1174 vlc_tick_t i_duration;
1175 vlc_input_decoder_FrameNext( p_es_video->p_dec, &i_duration );
1177 msg_Dbg( p_sys->p_input, "EsOutFrameNext consummed %d ms", (int)MS_FROM_VLC_TICK(i_duration) );
1179 if( i_duration <= 0 )
1180 i_duration = VLC_TICK_FROM_MS(40);
1182 /* FIXME it is not a clean way ? */
1183 if( p_sys->i_buffering_extra_initial <= 0 )
1185 vlc_tick_t i_stream_start;
1186 vlc_tick_t i_system_start;
1187 vlc_tick_t i_stream_duration;
1188 vlc_tick_t i_system_duration;
1189 int i_ret;
1191 i_ret = input_clock_GetState( p_sys->p_pgrm->p_input_clock,
1192 &i_stream_start, &i_system_start,
1193 &i_stream_duration, &i_system_duration );
1194 if( i_ret )
1195 return;
1197 p_sys->i_buffering_extra_initial = 1 + i_stream_duration
1198 - p_sys->i_pts_delay
1199 - p_sys->i_pts_jitter
1200 - p_sys->i_tracks_pts_delay; /* FIXME < 0 ? */
1201 p_sys->i_buffering_extra_system =
1202 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
1205 const float rate = input_clock_GetRate( p_sys->p_pgrm->p_input_clock );
1207 p_sys->b_buffering = true;
1208 p_sys->i_buffering_extra_system += i_duration;
1209 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial +
1210 ( p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial ) * rate;
1212 p_sys->i_preroll_end = -1;
1213 p_sys->i_prev_stream_level = -1;
1215 static vlc_tick_t EsOutGetBuffering( es_out_t *out )
1217 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1218 vlc_tick_t i_stream_duration, i_system_start;
1220 if( !p_sys->p_pgrm )
1221 return 0;
1222 else
1224 vlc_tick_t i_stream_start, i_system_duration;
1226 if( input_clock_GetState( p_sys->p_pgrm->p_input_clock,
1227 &i_stream_start, &i_system_start,
1228 &i_stream_duration, &i_system_duration ) )
1229 return 0;
1232 vlc_tick_t i_delay;
1234 if( p_sys->b_buffering && p_sys->i_buffering_extra_initial <= 0 )
1236 i_delay = i_stream_duration;
1238 else
1240 vlc_tick_t i_system_duration;
1242 if( p_sys->b_paused )
1244 i_system_duration = p_sys->i_pause_date - i_system_start;
1245 if( p_sys->i_buffering_extra_initial > 0 )
1246 i_system_duration += p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
1248 else
1250 i_system_duration = vlc_tick_now() - i_system_start;
1253 const vlc_tick_t i_consumed = i_system_duration * p_sys->rate - i_stream_duration;
1254 i_delay = p_sys->i_pts_delay + p_sys->i_pts_jitter
1255 + p_sys->i_tracks_pts_delay - i_consumed;
1257 if( i_delay < 0 )
1258 return 0;
1259 return i_delay;
1262 static void EsOutSendEsEvent(es_out_t *out, es_out_id_t *es, int action,
1263 bool forced)
1265 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1266 input_thread_t *p_input = p_sys->p_input;
1268 input_SendEventEs(p_input, &(struct vlc_input_event_es) {
1269 .action = action,
1270 .id = &es->id,
1271 .title = es->psz_title ? es->psz_title : "",
1272 .fmt = es->fmt_out.i_cat != UNKNOWN_ES ? &es->fmt_out : &es->fmt,
1273 .forced = forced,
1277 /* EsOutIsGroupSticky
1279 * A sticky group can be attached to any other programs. This is the case for
1280 * default groups (i_group == 0) sent by slave sources.
1282 static inline bool EsOutIsGroupSticky( es_out_t *p_out, input_source_t *source,
1283 int i_group )
1285 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1286 return source != input_priv(p_sys->p_input)->master && i_group == 0;
1289 static bool EsOutIsProgramVisible( es_out_t *out, input_source_t *source, int i_group )
1291 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1292 return p_sys->i_group_id == 0
1293 || (p_sys->i_group_id == i_group &&
1294 p_sys->p_pgrm && p_sys->p_pgrm->source == source);
1297 /* EsOutProgramSelect:
1298 * Select a program and update the object variable
1300 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
1302 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1303 input_thread_t *p_input = p_sys->p_input;
1304 es_out_id_t *es;
1306 if( p_sys->p_pgrm == p_pgrm )
1307 return; /* Nothing to do */
1309 if( p_sys->p_pgrm )
1311 es_out_pgrm_t *old = p_sys->p_pgrm;
1313 msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
1315 foreach_es_then_es_slaves(es)
1317 if (es->p_pgrm != old)
1318 continue;
1320 if (EsIsSelected(es) && p_sys->i_mode != ES_OUT_MODE_ALL)
1321 EsOutUnselectEs(out, es, true);
1323 if( EsOutIsGroupSticky( out, es->id.source, es->fmt.i_group ) )
1324 es->p_pgrm = NULL; /* Skip the DELETED event, cf. bellow */
1325 else
1327 /* ES tracks are deleted (and unselected) when their programs
1328 * are unselected (they will be added back when their programs
1329 * are selected back). */
1330 EsOutSendEsEvent( out, es, VLC_INPUT_ES_DELETED, false );
1335 p_sys->audio.p_main_es = NULL;
1336 p_sys->video.p_main_es = NULL;
1337 p_sys->sub.p_main_es = NULL;
1340 msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
1342 /* Mark it selected */
1343 p_pgrm->b_selected = true;
1345 /* Switch master stream */
1346 p_sys->p_pgrm = p_pgrm;
1348 /* Update "program" */
1349 input_SendEventProgramSelect( p_input, p_pgrm->i_id );
1351 /* Update "es-*" */
1352 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, p_pgrm->b_scrambled );
1354 foreach_es_then_es_slaves(es)
1357 if (es->p_pgrm == NULL)
1359 /* Attach this sticky ES to this new program. Skip the ADDED event,
1360 * cf.above */
1361 es->p_pgrm = p_sys->p_pgrm;
1363 else if (es->p_pgrm == p_sys->p_pgrm)
1365 EsOutSendEsEvent(out, es, VLC_INPUT_ES_ADDED, false);
1366 EsOutUpdateInfo(out, es, NULL);
1369 EsOutSelect(out, es, false);
1372 /* Ensure the correct running EPG table is selected */
1373 input_item_ChangeEPGSource( input_priv(p_input)->p_item, p_pgrm->i_id );
1375 /* Update now playing */
1376 if( p_pgrm->p_meta )
1378 input_item_SetESNowPlaying( input_priv(p_input)->p_item,
1379 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) );
1380 input_item_SetPublisher( input_priv(p_input)->p_item,
1381 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Publisher ) );
1382 input_item_SetTitle( input_priv(p_input)->p_item,
1383 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1384 input_SendEventMeta( p_input );
1385 /* FIXME: we probably want to replace every input meta */
1389 /* EsOutAddProgram:
1390 * Add a program
1392 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, input_source_t *source, int i_group )
1394 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1395 input_thread_t *p_input = p_sys->p_input;
1396 input_thread_private_t *priv = input_priv(p_input);
1398 /* Sticky groups will be attached to any existing programs, no need to
1399 * create one. */
1400 if( EsOutIsGroupSticky( out, source, i_group ) )
1401 return NULL;
1403 es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
1404 if( !p_pgrm )
1405 return NULL;
1407 /* Init */
1408 p_pgrm->source = input_source_Hold( source );
1409 p_pgrm->i_id = i_group;
1410 p_pgrm->i_es = 0;
1411 p_pgrm->b_selected = false;
1412 p_pgrm->b_scrambled = false;
1413 p_pgrm->i_last_pcr = VLC_TICK_INVALID;
1414 p_pgrm->p_meta = NULL;
1416 p_pgrm->p_master_clock = NULL;
1418 p_pgrm->p_main_clock = vlc_clock_main_New();
1419 if( !p_pgrm->p_main_clock )
1421 free( p_pgrm );
1422 return NULL;
1425 vlc_clock_t *p_master_clock = NULL;
1426 switch( p_sys->clock_source )
1428 case VLC_CLOCK_MASTER_AUTO:
1429 if (priv->b_can_pace_control)
1430 break;
1431 msg_Dbg( p_input, "The input can't pace, selecting the input (PCR) as the "
1432 "clock source" );
1433 /* Fall-through */
1434 case VLC_CLOCK_MASTER_INPUT:
1435 p_pgrm->p_master_clock = p_master_clock =
1436 vlc_clock_main_CreateMaster( p_pgrm->p_main_clock, NULL, NULL );
1437 break;
1438 default:
1439 break;
1442 p_pgrm->p_input_clock = input_clock_New( p_master_clock, p_sys->rate );
1443 if( !p_pgrm->p_input_clock )
1445 vlc_clock_main_Delete( p_pgrm->p_main_clock );
1446 free( p_pgrm );
1447 return NULL;
1450 if( p_sys->b_paused )
1451 input_clock_ChangePause( p_pgrm->p_input_clock, p_sys->b_paused, p_sys->i_pause_date );
1452 const vlc_tick_t pts_delay = p_sys->i_pts_delay + p_sys->i_pts_jitter
1453 + p_sys->i_tracks_pts_delay;
1454 input_clock_SetJitter( p_pgrm->p_input_clock, pts_delay, p_sys->i_cr_average );
1455 vlc_clock_main_SetInputDejitter( p_pgrm->p_main_clock, pts_delay );
1457 /* In case of low delay: don't use any output dejitter. This may result on
1458 * some audio/video glitches when starting, but low-delay is more important
1459 * than the visual quality if the user chose this option. */
1460 if (input_priv(p_input)->b_low_delay)
1461 vlc_clock_main_SetDejitter(p_pgrm->p_main_clock, 0);
1463 /* Append it */
1464 vlc_list_append(&p_pgrm->node, &p_sys->programs);
1466 /* Update "program" variable */
1467 input_SendEventProgramAdd( p_input, i_group, NULL );
1469 if( i_group == p_sys->i_group_id || ( !p_sys->p_pgrm && p_sys->i_group_id == 0 ) )
1470 EsOutProgramSelect( out, p_pgrm );
1472 return p_pgrm;
1475 /* EsOutProgramSearch
1477 static es_out_pgrm_t *EsOutProgramSearch( es_out_t *p_out, input_source_t *source,
1478 int i_group )
1480 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1481 es_out_pgrm_t *pgrm;
1483 vlc_list_foreach(pgrm, &p_sys->programs, node)
1484 if (pgrm->i_id == i_group && pgrm->source == source)
1485 return pgrm;
1487 return NULL;
1490 /* EsOutProgramInsert
1492 static es_out_pgrm_t *EsOutProgramInsert( es_out_t *p_out, input_source_t *source,
1493 int i_group )
1495 es_out_pgrm_t *pgrm = EsOutProgramSearch( p_out, source, i_group );
1496 return pgrm ? pgrm : EsOutProgramAdd( p_out, source, i_group );
1499 /* EsOutDelProgram:
1500 * Delete a program
1502 static int EsOutProgramDel( es_out_t *out, input_source_t *source, int i_group )
1504 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1505 input_thread_t *p_input = p_sys->p_input;
1507 es_out_pgrm_t *p_pgrm = EsOutProgramSearch( out, source, i_group );
1508 if( p_pgrm == NULL )
1509 return VLC_EGENERIC;
1511 if( p_pgrm->i_es )
1513 msg_Dbg( p_input, "can't delete program %d which still has %i ES",
1514 i_group, p_pgrm->i_es );
1515 return VLC_EGENERIC;
1518 /* Unselect sticky ES tracks */
1519 es_out_id_t *es;
1520 foreach_es_then_es_slaves(es)
1522 if (es->p_pgrm != p_pgrm)
1523 continue;
1525 /* The remaining ES tracks are necessary sticky, cf. 'p_pgrm->i_es'
1526 * test above. */
1527 assert(EsOutIsGroupSticky( out, es->id.source, es->fmt.i_group));
1529 EsOutUnselectEs(out, es, true);
1530 es->p_pgrm = NULL;
1533 vlc_list_remove(&p_pgrm->node);
1535 /* If program is selected we need to unselect it */
1536 if( p_sys->p_pgrm == p_pgrm )
1537 p_sys->p_pgrm = NULL;
1539 /* Update "program" variable */
1540 input_SendEventProgramDel( p_input, i_group );
1542 ProgramDelete( p_pgrm );
1544 return VLC_SUCCESS;
1547 /* EsOutProgramMeta:
1549 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
1551 char *psz = NULL;
1552 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1554 if( asprintf( &psz, _("%s [%s %d]"), vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ),
1555 _("Program"), p_pgrm->i_id ) == -1 )
1556 return NULL;
1558 else
1560 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1561 return NULL;
1563 return psz;
1566 static char *EsOutProgramGetProgramName( es_out_pgrm_t *p_pgrm )
1568 char *psz = NULL;
1569 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1571 return strdup( vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1573 else
1575 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1576 return NULL;
1578 return psz;
1581 static char *EsInfoCategoryName( es_out_id_t* es )
1583 char *psz_category;
1585 if( asprintf( &psz_category, _("Stream '%s'"), es->id.str_id ) == -1 )
1586 return NULL;
1588 return psz_category;
1591 static void EsOutProgramMeta( es_out_t *out, input_source_t *source,
1592 int i_group, const vlc_meta_t *p_meta )
1594 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1595 es_out_pgrm_t *p_pgrm;
1596 input_thread_t *p_input = p_sys->p_input;
1597 input_item_t *p_item = input_priv(p_input)->p_item;
1598 const char *psz_title = NULL;
1599 const char *psz_provider = NULL;
1600 int i;
1601 bool b_has_new_infos = false;
1603 msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
1605 /* Check against empty meta data (empty for what we handle) */
1606 if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
1607 !vlc_meta_Get( p_meta, vlc_meta_ESNowPlaying) &&
1608 !vlc_meta_Get( p_meta, vlc_meta_Publisher) )
1610 return;
1613 if( i_group < 0 )
1615 EsOutGlobalMeta( out, p_meta );
1616 return;
1619 /* Find program */
1620 if( !EsOutIsProgramVisible( out, source, i_group ) )
1621 return;
1622 p_pgrm = EsOutProgramInsert( out, source, i_group );
1623 if( !p_pgrm )
1624 return;
1626 if( p_pgrm->p_meta )
1628 const char *psz_current_title = vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title );
1629 const char *psz_new_title = vlc_meta_Get( p_meta, vlc_meta_Title );
1630 if( (psz_current_title != NULL && psz_new_title != NULL)
1631 ? strcmp(psz_new_title, psz_current_title)
1632 : (psz_current_title != psz_new_title) )
1634 /* Remove old entries */
1635 char *psz_oldinfokey = EsOutProgramGetMetaName( p_pgrm );
1637 if( !input_item_DelInfo( p_item, psz_oldinfokey, NULL ) )
1638 b_has_new_infos = true;
1639 /* TODO update epg name ?
1640 * TODO update scrambled info name ? */
1641 free( psz_oldinfokey );
1643 vlc_meta_Delete( p_pgrm->p_meta );
1645 p_pgrm->p_meta = vlc_meta_New();
1646 if( p_pgrm->p_meta )
1647 vlc_meta_Merge( p_pgrm->p_meta, p_meta );
1649 if( p_sys->p_pgrm == p_pgrm )
1651 EsOutMeta( out, NULL, p_meta );
1653 /* */
1654 psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
1655 psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
1657 /* Update the description text of the program */
1658 if( psz_title && *psz_title )
1660 char *psz_text;
1661 if( psz_provider && *psz_provider )
1663 if( asprintf( &psz_text, "%s [%s]", psz_title, psz_provider ) < 0 )
1664 psz_text = NULL;
1666 else
1668 psz_text = strdup( psz_title );
1671 if( psz_text )
1673 input_SendEventProgramUpdated( p_input, i_group, psz_text );
1674 if( p_sys->p_pgrm == p_pgrm )
1675 input_SendEventProgramSelect( p_input, i_group );
1676 free( psz_text );
1680 /* */
1681 char **ppsz_all_keys = vlc_meta_CopyExtraNames(p_meta );
1683 info_category_t *p_cat = NULL;
1684 if( psz_provider || ( ppsz_all_keys[0] && *ppsz_all_keys[0] ) )
1686 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1687 if( psz_cat )
1688 p_cat = info_category_New( psz_cat );
1689 free( psz_cat );
1692 for( i = 0; ppsz_all_keys[i]; i++ )
1694 if( p_cat )
1695 info_category_AddInfo( p_cat, vlc_gettext(ppsz_all_keys[i]), "%s",
1696 vlc_meta_GetExtra( p_meta, ppsz_all_keys[i] ) );
1697 free( ppsz_all_keys[i] );
1699 free( ppsz_all_keys );
1701 if( psz_provider )
1703 if( p_sys->p_pgrm == p_pgrm )
1705 input_item_SetPublisher( input_priv(p_input)->p_item, psz_provider );
1706 input_SendEventMeta( p_input );
1708 if( p_cat )
1709 info_category_AddInfo( p_cat, vlc_meta_TypeToLocalizedString(vlc_meta_Publisher),
1710 "%s",psz_provider );
1712 if( p_cat )
1714 input_item_MergeInfos( p_item, p_cat );
1715 b_has_new_infos = true;
1717 if( !input_priv(p_input)->b_preparsing && b_has_new_infos )
1718 input_SendEventMetaInfo( p_input );
1721 static void EsOutProgramEpgEvent( es_out_t *out, input_source_t *source,
1722 int i_group, const vlc_epg_event_t *p_event )
1724 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1725 input_thread_t *p_input = p_sys->p_input;
1726 input_item_t *p_item = input_priv(p_input)->p_item;
1727 es_out_pgrm_t *p_pgrm;
1729 /* Find program */
1730 if( !EsOutIsProgramVisible( out, source, i_group ) )
1731 return;
1732 p_pgrm = EsOutProgramInsert( out, source, i_group );
1733 if( !p_pgrm )
1734 return;
1736 input_item_SetEpgEvent( p_item, p_event );
1739 static void EsOutProgramEpg( es_out_t *out, input_source_t *source,
1740 int i_group, const vlc_epg_t *p_epg )
1742 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1743 input_thread_t *p_input = p_sys->p_input;
1744 input_item_t *p_item = input_priv(p_input)->p_item;
1745 es_out_pgrm_t *p_pgrm;
1746 char *psz_cat;
1748 /* Find program */
1749 if( !EsOutIsProgramVisible( out, source, i_group ) )
1750 return;
1751 p_pgrm = EsOutProgramInsert( out, source, i_group );
1752 if( !p_pgrm )
1753 return;
1755 /* Update info */
1756 psz_cat = EsOutProgramGetMetaName( p_pgrm );
1757 msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, psz_cat );
1759 /* Merge EPG */
1760 vlc_epg_t epg;
1762 epg = *p_epg;
1763 epg.psz_name = EsOutProgramGetProgramName( p_pgrm );
1765 input_item_SetEpg( p_item, &epg, p_sys->p_pgrm && (p_epg->i_source_id == p_sys->p_pgrm->i_id) );
1766 input_SendEventMetaEpg( p_sys->p_input );
1768 free( epg.psz_name );
1770 /* Update now playing */
1771 if( p_epg->b_present && p_pgrm->p_meta &&
1772 ( p_epg->p_current || p_epg->i_event == 0 ) )
1774 vlc_meta_SetNowPlaying( p_pgrm->p_meta, NULL );
1777 vlc_mutex_lock( &p_item->lock );
1778 for( int i = 0; i < p_item->i_epg; i++ )
1780 const vlc_epg_t *p_tmp = p_item->pp_epg[i];
1782 if( p_tmp->b_present && p_tmp->i_source_id == p_pgrm->i_id )
1784 const char *psz_name = ( p_tmp->p_current ) ? p_tmp->p_current->psz_name : NULL;
1785 if( !p_pgrm->p_meta )
1786 p_pgrm->p_meta = vlc_meta_New();
1787 if( p_pgrm->p_meta )
1788 vlc_meta_Set( p_pgrm->p_meta, vlc_meta_ESNowPlaying, psz_name );
1789 break;
1792 vlc_mutex_unlock( &p_item->lock );
1794 /* Update selected program input info */
1795 if( p_pgrm == p_sys->p_pgrm )
1797 const char *psz_nowplaying = p_pgrm->p_meta ?
1798 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) : NULL;
1800 input_item_SetESNowPlaying( input_priv(p_input)->p_item, psz_nowplaying );
1801 input_SendEventMeta( p_input );
1803 const char *now_playing_tr =
1804 vlc_meta_TypeToLocalizedString(vlc_meta_ESNowPlaying);
1805 int ret;
1806 if( psz_nowplaying )
1807 ret = input_item_AddInfo( p_item, psz_cat, now_playing_tr,
1808 "%s", psz_nowplaying );
1809 else
1810 ret = input_item_DelInfo( p_item, psz_cat, now_playing_tr );
1812 if( ret == VLC_SUCCESS && !input_priv(p_input)->b_preparsing )
1813 input_SendEventMetaInfo( p_input );
1816 free( psz_cat );
1819 static void EsOutEpgTime( es_out_t *out, int64_t time )
1821 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1822 input_thread_t *p_input = p_sys->p_input;
1823 input_item_t *p_item = input_priv(p_input)->p_item;
1825 input_item_SetEpgTime( p_item, time );
1828 static void EsOutProgramUpdateScrambled( es_out_t *p_out, es_out_pgrm_t *p_pgrm )
1830 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1831 input_thread_t *p_input = p_sys->p_input;
1832 input_item_t *p_item = input_priv(p_input)->p_item;
1833 es_out_id_t *es;
1834 bool b_scrambled = false;
1836 vlc_list_foreach( es, &p_sys->es, node ) /* Only master es */
1837 if (es->p_pgrm == p_pgrm && es->b_scrambled)
1839 b_scrambled = true;
1840 break;
1843 if( !p_pgrm->b_scrambled == !b_scrambled )
1844 return;
1846 p_pgrm->b_scrambled = b_scrambled;
1847 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1849 int ret;
1850 if( b_scrambled )
1851 ret = input_item_AddInfo( p_item, psz_cat, _("Scrambled"), _("Yes") );
1852 else
1853 ret = input_item_DelInfo( p_item, psz_cat, _("Scrambled") );
1854 free( psz_cat );
1856 if( ret == VLC_SUCCESS && !input_priv(p_input)->b_preparsing )
1857 input_SendEventMetaInfo( p_input );
1858 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, b_scrambled );
1861 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_program_meta )
1863 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1864 input_thread_t *p_input = p_sys->p_input;
1865 input_item_t *p_item = input_GetItem( p_input );
1867 vlc_mutex_lock( &p_item->lock );
1868 if( p_meta )
1869 vlc_meta_Merge( p_item->p_meta, p_meta );
1870 vlc_mutex_unlock( &p_item->lock );
1872 /* Check program meta to not override GROUP_META values */
1873 if( p_meta && (!p_program_meta || vlc_meta_Get( p_program_meta, vlc_meta_Title ) == NULL) &&
1874 vlc_meta_Get( p_meta, vlc_meta_Title ) != NULL )
1875 input_item_SetName( p_item, vlc_meta_Get( p_meta, vlc_meta_Title ) );
1877 const char *psz_arturl = NULL;
1878 char *psz_alloc = NULL;
1880 if( p_program_meta )
1881 psz_arturl = vlc_meta_Get( p_program_meta, vlc_meta_ArtworkURL );
1882 if( psz_arturl == NULL && p_meta )
1883 psz_arturl = vlc_meta_Get( p_meta, vlc_meta_ArtworkURL );
1885 if( psz_arturl == NULL ) /* restore/favor previously set item art URL */
1886 psz_arturl = psz_alloc = input_item_GetArtURL( p_item );
1888 if( psz_arturl != NULL )
1889 input_item_SetArtURL( p_item, psz_arturl );
1891 if( psz_arturl != NULL && !strncmp( psz_arturl, "attachment://", 13 ) )
1892 { /* Clear art cover if streaming out.
1893 * FIXME: Why? Remove this when sout gets meta data support. */
1894 if( input_priv(p_input)->p_sout != NULL )
1895 input_item_SetArtURL( p_item, NULL );
1896 else
1897 input_ExtractAttachmentAndCacheArt( p_input, psz_arturl + 13 );
1899 free( psz_alloc );
1901 input_item_SetPreparsed( p_item, true );
1903 input_SendEventMeta( p_input );
1904 /* TODO handle sout meta ? */
1907 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta )
1909 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
1910 EsOutMeta( p_out, p_meta,
1911 (p_sys->p_pgrm && p_sys->p_pgrm->p_meta) ? p_sys->p_pgrm->p_meta : NULL );
1914 static void EsOutUpdateEsLanguageTitle(es_out_id_t *es,
1915 const es_format_t *fmt)
1917 free( es->psz_title );
1918 free( es->psz_language );
1919 free( es->psz_language_code );
1921 es->psz_language = LanguageGetName( fmt->psz_language );
1922 es->psz_language_code = LanguageGetCode( fmt->psz_language );
1924 es->psz_title = EsGetTitle(es);
1927 static void EsOutFillEsFmt(es_out_t *out, es_format_t *fmt)
1929 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
1930 input_thread_t *p_input = p_sys->p_input;
1932 switch( fmt->i_cat )
1934 case AUDIO_ES:
1936 fmt->i_codec = vlc_fourcc_GetCodecAudio( fmt->i_codec,
1937 fmt->audio.i_bitspersample );
1938 audio_replay_gain_t rg;
1939 memset( &rg, 0, sizeof(rg) );
1940 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
1941 vlc_audio_replay_gain_MergeFromMeta( &rg, input_priv(p_input)->p_item->p_meta );
1942 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1944 for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
1946 if( !fmt->audio_replay_gain.pb_peak[i] )
1948 fmt->audio_replay_gain.pb_peak[i] = rg.pb_peak[i];
1949 fmt->audio_replay_gain.pf_peak[i] = rg.pf_peak[i];
1951 if( !fmt->audio_replay_gain.pb_gain[i] )
1953 fmt->audio_replay_gain.pb_gain[i] = rg.pb_gain[i];
1954 fmt->audio_replay_gain.pf_gain[i] = rg.pf_gain[i];
1957 break;
1960 case VIDEO_ES:
1961 fmt->i_codec = vlc_fourcc_GetCodec( fmt->i_cat, fmt->i_codec );
1963 if( !fmt->video.i_visible_width || !fmt->video.i_visible_height )
1965 fmt->video.i_visible_width = fmt->video.i_width;
1966 fmt->video.i_visible_height = fmt->video.i_height;
1969 if( fmt->video.i_frame_rate && fmt->video.i_frame_rate_base )
1970 vlc_ureduce( &fmt->video.i_frame_rate,
1971 &fmt->video.i_frame_rate_base,
1972 fmt->video.i_frame_rate,
1973 fmt->video.i_frame_rate_base, 0 );
1974 break;
1976 case SPU_ES:
1977 fmt->i_codec = vlc_fourcc_GetCodec( fmt->i_cat, fmt->i_codec );
1978 break;
1980 default:
1981 break;
1985 static char *EsOutCreateStrId( es_out_id_t *es, bool stable, const char *id,
1986 es_out_id_t *p_master )
1988 struct vlc_memstream ms;
1989 int ret = vlc_memstream_open( &ms );
1990 if( ret != 0 )
1991 return NULL;
1993 if( p_master )
1995 vlc_memstream_puts( &ms, p_master->id.str_id );
1996 vlc_memstream_puts( &ms, "/cc/" );
1998 else if ( id )
2000 /* ',' is used as a delimiter */
2001 assert( strchr( id, ',' ) == NULL);
2003 vlc_memstream_puts( &ms, id );
2004 vlc_memstream_putc( &ms, '/' );
2007 switch (es->fmt.i_cat)
2009 case VIDEO_ES: vlc_memstream_puts( &ms, "video" ); break;
2010 case AUDIO_ES: vlc_memstream_puts( &ms, "audio" ); break;
2011 case SPU_ES: vlc_memstream_puts( &ms, "spu" ); break;
2012 case DATA_ES: vlc_memstream_puts( &ms, "data" ); break;
2013 default: vlc_memstream_puts( &ms, "unknown" ); break;
2016 if( !stable )
2017 vlc_memstream_puts( &ms, "auto/" );
2019 vlc_memstream_printf( &ms, "/%d", es->fmt.i_id );
2020 ret = vlc_memstream_close( &ms );
2022 return ret == 0 ? ms.ptr : NULL;
2025 static es_out_id_t *EsOutAddLocked( es_out_t *out, input_source_t *source,
2026 const es_format_t *fmt,
2027 es_out_id_t *p_master )
2029 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2030 input_thread_t *p_input = p_sys->p_input;
2031 assert( source ); /* == p_sys->main_source if the given source is NULL */
2033 if( fmt->i_group < 0 )
2035 msg_Err( p_input, "invalid group number" );
2036 return NULL;
2039 es_out_id_t *es = malloc( sizeof( *es ) );
2040 es_out_pgrm_t *p_pgrm;
2042 if( !es )
2043 return NULL;
2045 es->out = out;
2046 es->id.source = input_source_Hold( source );
2048 if( es_format_Copy( &es->fmt, fmt ) != VLC_SUCCESS )
2050 free( es );
2051 return NULL;
2054 bool stable;
2055 if( es->fmt.i_id < 0 )
2057 es->fmt.i_id = input_source_GetNewAutoId( source );
2058 stable = false;
2060 else
2061 stable = true;
2063 if( !es->fmt.i_original_fourcc )
2064 es->fmt.i_original_fourcc = es->fmt.i_codec;
2066 char *str_id =
2067 EsOutCreateStrId( es, stable, input_source_GetStrId(source), p_master );
2068 if( !str_id )
2070 es_format_Clean( &es->fmt );
2071 input_source_Release( es->id.source );
2072 free( es );
2073 return NULL;
2076 if( !EsOutIsGroupSticky( out, source, fmt->i_group ) )
2078 /* Search the program */
2079 p_pgrm = EsOutProgramInsert( out, source, fmt->i_group );
2080 if( !p_pgrm )
2082 es_format_Clean( &es->fmt );
2083 input_source_Release( es->id.source );
2084 free( str_id );
2085 free( es );
2086 return NULL;
2088 /* Increase ref count for program */
2089 if( p_pgrm )
2090 p_pgrm->i_es++;
2092 /* The group 0 is the default one and can be used by different contexts */
2093 assert( fmt->i_group == 0 || p_pgrm->source == es->id.source );
2095 else
2096 p_pgrm = p_sys->p_pgrm; /* Use the selected program (can be NULL) */
2098 /* Get the number of ES already added in order to get the position of the es */
2099 es->i_pos = 0;
2100 es_out_id_t *it;
2101 foreach_es_then_es_slaves(it)
2102 if( it->fmt.i_cat == fmt->i_cat && it->fmt.i_group == fmt->i_group )
2103 es->i_pos++;
2105 /* Set up ES */
2106 es->p_pgrm = p_pgrm;
2108 es->id.i_id = es->fmt.i_id;
2109 es->id.i_cat = es->fmt.i_cat;
2110 es->id.str_id = str_id;
2111 es->id.stable = stable;
2113 es_format_Init( &es->fmt_out, UNKNOWN_ES, 0 );
2115 es->b_scrambled = false;
2116 es->b_terminated = false;
2118 switch( es->fmt.i_cat )
2120 case AUDIO_ES:
2121 es->i_channel = p_sys->audio.i_count++;
2122 break;
2124 case VIDEO_ES:
2125 es->i_channel = p_sys->video.i_count++;
2126 break;
2128 case SPU_ES:
2129 es->i_channel = p_sys->sub.i_count++;
2130 break;
2132 default:
2133 es->i_channel = 0;
2134 break;
2136 EsOutFillEsFmt( out, &es->fmt );
2137 es->psz_language = LanguageGetName( es->fmt.psz_language ); /* remember so we only need to do it once */
2138 es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
2139 es->psz_title = EsGetTitle(es);
2140 es->p_dec = NULL;
2141 es->p_dec_record = NULL;
2142 es->p_clock = NULL;
2143 es->master = false;
2144 es->cc.type = 0;
2145 es->cc.i_bitmap = 0;
2146 es->p_master = p_master;
2147 es->mouse_event_cb = NULL;
2148 es->mouse_event_userdata = NULL;
2149 es->i_pts_level = VLC_TICK_INVALID;
2150 es->delay = INT64_MAX;
2152 vlc_list_append(&es->node, es->p_master ? &p_sys->es_slaves : &p_sys->es);
2154 vlc_atomic_rc_init(&es->rc);
2156 if( es->p_pgrm == p_sys->p_pgrm )
2157 EsOutSendEsEvent( out, es, VLC_INPUT_ES_ADDED, false );
2159 EsOutUpdateInfo( out, es, NULL );
2160 EsOutSelect( out, es, false );
2162 return es;
2165 /* EsOutAdd:
2166 * Add an es_out
2168 static es_out_id_t *EsOutAdd( es_out_t *out, input_source_t *source, const es_format_t *fmt )
2170 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2172 if( !source )
2173 source = p_sys->main_source;
2175 vlc_mutex_lock( &p_sys->lock );
2176 es_out_id_t *es = EsOutAddLocked( out, source, fmt, NULL );
2177 vlc_mutex_unlock( &p_sys->lock );
2178 return es;
2181 static bool EsIsSelected( es_out_id_t *es )
2183 if( es->p_master )
2185 bool b_decode = false;
2186 if( es->p_master->p_dec )
2188 int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
2189 vlc_input_decoder_GetCcState( es->p_master->p_dec, es->fmt.i_codec,
2190 i_channel, &b_decode );
2192 return b_decode;
2194 else
2196 return es->p_dec != NULL;
2200 static void ClockUpdate(vlc_tick_t system_ts, vlc_tick_t ts, double rate,
2201 unsigned frame_rate, unsigned frame_rate_base,
2202 void *data)
2204 es_out_id_t *es = data;
2205 es_out_sys_t *p_sys = container_of(es->out, es_out_sys_t, out);
2207 input_SendEventOutputClock(p_sys->p_input, &es->id, es->master, system_ts,
2208 ts, rate, frame_rate, frame_rate_base);
2211 static void EsOutCreateDecoder( es_out_t *out, es_out_id_t *p_es )
2213 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2214 input_thread_t *p_input = p_sys->p_input;
2215 vlc_input_decoder_t *dec;
2217 static const struct vlc_clock_cbs clock_cbs = {
2218 .on_update = ClockUpdate
2221 assert( p_es->p_pgrm );
2223 enum es_format_category_e clock_source_cat;
2224 switch( p_sys->clock_source )
2226 case VLC_CLOCK_MASTER_AUTO:
2227 case VLC_CLOCK_MASTER_AUDIO:
2228 clock_source_cat = AUDIO_ES;
2229 break;
2230 case VLC_CLOCK_MASTER_MONOTONIC:
2231 case VLC_CLOCK_MASTER_INPUT:
2232 clock_source_cat = UNKNOWN_ES;
2233 break;
2234 default:
2235 vlc_assert_unreachable();
2238 if( p_es->fmt.i_cat != UNKNOWN_ES
2239 && p_es->fmt.i_cat == clock_source_cat
2240 && p_es->p_pgrm->p_master_clock == NULL )
2242 p_es->master = true;
2243 p_es->p_pgrm->p_master_clock = p_es->p_clock =
2244 vlc_clock_main_CreateMaster( p_es->p_pgrm->p_main_clock,
2245 &clock_cbs, p_es );
2247 else
2249 p_es->master = false;
2250 p_es->p_clock = vlc_clock_main_CreateSlave( p_es->p_pgrm->p_main_clock,
2251 p_es->fmt.i_cat,
2252 &clock_cbs, p_es );
2255 if( !p_es->p_clock )
2257 p_es->master = false;
2258 return;
2261 input_thread_private_t *priv = input_priv(p_input);
2262 dec = vlc_input_decoder_New( VLC_OBJECT(p_input), &p_es->fmt, p_es->p_clock,
2263 priv->p_resource, priv->p_sout,
2264 priv->b_thumbnailing, &decoder_cbs, p_es );
2265 if( dec != NULL )
2267 vlc_input_decoder_ChangeRate( dec, p_sys->rate );
2269 if( p_sys->b_buffering )
2270 vlc_input_decoder_StartWait( dec );
2272 if( !p_es->p_master && p_sys->p_sout_record )
2274 p_es->p_dec_record =
2275 vlc_input_decoder_New( VLC_OBJECT(p_input), &p_es->fmt, NULL,
2276 priv->p_resource, p_sys->p_sout_record,
2277 false, &decoder_cbs, p_es );
2278 if( p_es->p_dec_record && p_sys->b_buffering )
2279 vlc_input_decoder_StartWait( p_es->p_dec_record );
2282 if( p_es->mouse_event_cb && p_es->fmt.i_cat == VIDEO_ES )
2283 vlc_input_decoder_SetVoutMouseEvent( dec, p_es->mouse_event_cb,
2284 p_es->mouse_event_userdata );
2286 else
2288 vlc_clock_Delete( p_es->p_clock );
2289 p_es->p_clock = NULL;
2291 p_es->p_dec = dec;
2293 EsOutDecoderChangeDelay( out, p_es );
2295 static void EsOutDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
2297 VLC_UNUSED(out);
2299 if( !p_es->p_dec )
2300 return;
2302 assert( p_es->p_pgrm );
2304 vlc_input_decoder_Delete( p_es->p_dec );
2305 p_es->p_dec = NULL;
2306 if( p_es->p_pgrm->p_master_clock == p_es->p_clock )
2307 p_es->p_pgrm->p_master_clock = NULL;
2308 vlc_clock_Delete( p_es->p_clock );
2309 p_es->p_clock = NULL;
2311 if( p_es->p_dec_record )
2313 vlc_input_decoder_Delete( p_es->p_dec_record );
2314 p_es->p_dec_record = NULL;
2317 es_format_Clean( &p_es->fmt_out );
2320 static void EsOutSelectEs( es_out_t *out, es_out_id_t *es, bool b_force )
2322 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2323 input_thread_t *p_input = p_sys->p_input;
2324 bool b_thumbnailing = input_priv(p_input)->b_thumbnailing;
2326 if( EsIsSelected( es ) )
2328 msg_Warn( p_input, "ES 0x%x is already selected", es->fmt.i_id );
2329 return;
2332 if( !es->p_pgrm )
2333 return;
2335 if( es->p_master )
2337 int i_channel;
2338 if( !es->p_master->p_dec )
2339 return;
2341 i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
2343 if( i_channel == -1 ||
2344 vlc_input_decoder_SetCcState( es->p_master->p_dec, es->fmt.i_codec,
2345 i_channel, true ) )
2346 return;
2348 else
2350 const bool b_sout = input_priv(p_input)->p_sout != NULL;
2351 /* If b_forced, the ES is specifically requested by the user, so bypass
2352 * the following vars check. */
2353 if( !b_force )
2355 if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
2357 if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) )
2359 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
2360 es->fmt.i_id );
2361 return;
2364 else if( es->fmt.i_cat == AUDIO_ES )
2366 if( b_thumbnailing
2367 || !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) )
2369 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
2370 es->fmt.i_id );
2371 return;
2374 if( es->fmt.i_cat == SPU_ES )
2376 if( b_thumbnailing
2377 || !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) )
2379 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
2380 es->fmt.i_id );
2381 return;
2386 EsOutCreateDecoder( out, es );
2388 if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
2389 return;
2392 /* Mark it as selected */
2393 EsOutSendEsEvent(out, es, VLC_INPUT_ES_SELECTED, b_force);
2395 /* Special case of the zvbi decoder for teletext: send the initial selected
2396 * page and transparency */
2397 if( !es->p_master )
2399 bool vbi_opaque;
2400 int vbi_page = vlc_input_decoder_GetVbiPage( es->p_dec, &vbi_opaque );
2401 if( vbi_page >= 0 )
2403 input_SendEventVbiPage( p_input, vbi_page );
2404 input_SendEventVbiTransparency( p_input, !vbi_opaque );
2409 static void EsOutDrainCCChannels( es_out_id_t *parent )
2411 /* Drain captions sub ES as well */
2412 uint64_t i_bitmap = parent->cc.i_bitmap;
2413 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1 )
2415 if( (i_bitmap & 1) == 0 || !parent->cc.pp_es[i] ||
2416 !parent->cc.pp_es[i]->p_dec )
2417 continue;
2418 vlc_input_decoder_Drain( parent->cc.pp_es[i]->p_dec );
2422 static void EsDeleteCCChannels( es_out_t *out, es_out_id_t *parent )
2424 if( parent->cc.type == 0 )
2425 return;
2427 es_out_id_t *spu_es = EsOutGetSelectedCat( out, SPU_ES );
2428 const int i_spu_id = spu_es ? spu_es->fmt.i_id : -1;
2430 uint64_t i_bitmap = parent->cc.i_bitmap;
2431 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1 )
2433 if( (i_bitmap & 1) == 0 || !parent->cc.pp_es[i] )
2434 continue;
2436 if( i_spu_id == parent->cc.pp_es[i]->fmt.i_id )
2438 /* Force unselection of the CC */
2439 EsOutSendEsEvent(out, parent->cc.pp_es[i], VLC_INPUT_ES_UNSELECTED,
2440 false);
2442 EsOutDelLocked( out, parent->cc.pp_es[i] );
2445 parent->cc.i_bitmap = 0;
2446 parent->cc.type = 0;
2449 static void EsOutUnselectEs( es_out_t *out, es_out_id_t *es, bool b_update )
2451 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2452 input_thread_t *p_input = p_sys->p_input;
2454 if( !EsIsSelected( es ) )
2456 msg_Warn( p_input, "ES 0x%x is already unselected", es->fmt.i_id );
2457 return;
2460 if( es->p_master )
2462 if( es->p_master->p_dec )
2464 int i_channel = EsOutGetClosedCaptionsChannel( &es->fmt );
2465 if( i_channel != -1 )
2466 vlc_input_decoder_SetCcState( es->p_master->p_dec, es->fmt.i_codec,
2467 i_channel, false );
2470 else
2472 EsDeleteCCChannels( out, es );
2473 EsOutDestroyDecoder( out, es );
2476 if( !b_update )
2477 return;
2479 /* Mark it as unselected */
2480 EsOutSendEsEvent(out, es, VLC_INPUT_ES_UNSELECTED, false);
2483 static bool EsOutSelectMatchPrioritized( const es_out_es_props_t *p_esprops,
2484 const es_out_id_t *es )
2486 /* Otherwise, fallback by priority */
2487 if( p_esprops->p_main_es != NULL )
2489 return ( es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority );
2491 else
2493 return ( es->fmt.i_priority > ES_PRIORITY_NOT_DEFAULTABLE );
2497 static bool EsOutSelectHasExplicitParams( const es_out_es_props_t *p_esprops )
2499 return p_esprops->str_ids || p_esprops->i_channel >= 0;
2502 static bool EsOutSelectMatchExplicitParams( const es_out_es_props_t *p_esprops,
2503 const es_out_id_t *es )
2505 /* user designated by ID ES have higher prio than everything */
2506 if( p_esprops->str_ids )
2508 char *saveptr, *str_ids = strdup( p_esprops->str_ids );
2509 if( str_ids )
2511 for( const char *str_id = strtok_r( str_ids, ",", &saveptr );
2512 str_id != NULL ;
2513 str_id = strtok_r( NULL, ",", &saveptr ) )
2515 if( strcmp( str_id, es->id.str_id ) == 0 )
2517 free( str_ids );
2518 return true;
2522 free( str_ids );
2525 /* then channel index */
2526 if( p_esprops->i_channel >= 0 )
2528 return ( es->i_channel == p_esprops->i_channel );
2531 return false;
2535 * Select an ES given the current mode
2536 * XXX: you need to take a the lock before (stream.stream_lock)
2538 * \param out The es_out structure
2539 * \param es es_out_id structure
2540 * \param b_force ...
2541 * \return nothing
2543 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
2545 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2546 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
2547 if( !p_esprops || !p_sys->b_active || !es->p_pgrm )
2549 return;
2552 enum es_out_policy_e policy = p_esprops->e_policy;
2553 if( policy == ES_OUT_ES_POLICY_AUTO )
2555 if( p_esprops->str_ids && strchr( p_esprops->str_ids, ',' ) != NULL )
2556 policy = ES_OUT_ES_POLICY_SIMULTANEOUS;
2557 else
2558 policy = ES_OUT_ES_POLICY_EXCLUSIVE;
2561 bool b_auto_selected = p_esprops->b_autoselect
2562 || input_source_IsCatAutoselected( es->id.source, es->fmt.i_cat );
2563 bool b_auto_unselect = p_sys->i_mode == ES_OUT_MODE_AUTO &&
2564 policy == ES_OUT_ES_POLICY_EXCLUSIVE &&
2565 p_esprops->p_main_es && p_esprops->p_main_es != es;
2567 if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
2569 if( !EsIsSelected( es ) )
2571 if( b_auto_unselect )
2572 EsOutUnselectEs( out, p_esprops->p_main_es, true );
2574 EsOutSelectEs( out, es, b_force );
2577 else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
2579 char *prgms = var_GetNonEmptyString( p_sys->p_input, "programs" );
2580 if( prgms != NULL )
2582 char *buf;
2584 for ( const char *prgm = strtok_r( prgms, ",", &buf );
2585 prgm != NULL;
2586 prgm = strtok_r( NULL, ",", &buf ) )
2588 if( atoi( prgm ) == es->p_pgrm->i_id )
2590 if( !EsIsSelected( es ) )
2591 EsOutSelectEs( out, es, b_force );
2592 break;
2595 free( prgms );
2598 else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
2600 const es_out_id_t *wanted_es = NULL;
2602 if( es->p_pgrm != p_sys->p_pgrm )
2603 return;
2605 if( EsOutSelectHasExplicitParams( p_esprops ) )
2607 if( !EsOutSelectMatchExplicitParams( p_esprops, es ) )
2608 return;
2609 wanted_es = es;
2611 else if( p_esprops->ppsz_language )
2613 /* If not deactivated */
2614 const int i_stop_idx = LanguageArrayIndex( p_esprops->ppsz_language, "none" );
2615 const int current_es_idx = ( p_esprops->p_main_es == NULL ) ? -1 :
2616 LanguageArrayIndex( p_esprops->ppsz_language,
2617 p_esprops->p_main_es->psz_language_code );
2618 const int es_idx = LanguageArrayIndex( p_esprops->ppsz_language,
2619 es->psz_language_code );
2620 if( es_idx >= 0 && (i_stop_idx < 0 || i_stop_idx > es_idx) )
2622 /* Only select the language if it's in the list */
2623 if( current_es_idx < 0 || /* current es was not selected by lang prefs */
2624 es_idx < current_es_idx || /* current es has lower lang prio */
2625 ( es_idx == current_es_idx && /* lang is same, but es has higher prio */
2626 p_esprops->p_main_es->fmt.i_priority < es->fmt.i_priority ) )
2628 wanted_es = es;
2632 if( wanted_es || /* We did find a language matching our prefs */
2633 i_stop_idx >= 0 || /* If fallback disabled by 'none' */
2634 current_es_idx >= 0 ) /* Is currently selected by lang pref */
2636 b_auto_selected = false; /* do not perform other selection rules */
2640 /* If demux has specified a default active track */
2641 if( wanted_es == NULL &&
2642 p_esprops->i_demux_id >= 0 &&
2643 p_esprops->i_demux_id == es->fmt.i_id )
2645 wanted_es = es;
2648 /* If there is no user preference, select the default track
2649 * or adapt by ES priority */
2650 if( b_auto_selected && wanted_es == NULL &&
2651 EsOutSelectMatchPrioritized( p_esprops, es ) )
2653 wanted_es = es;
2656 /* Do ES activation/deactivation */
2657 if( wanted_es == es && !EsIsSelected( es ) )
2659 if( b_auto_unselect )
2660 EsOutUnselectEs( out, p_esprops->p_main_es, true );
2662 EsOutSelectEs( out, es, b_force );
2666 /* FIXME TODO handle priority here */
2667 if( p_esprops && p_sys->i_mode == ES_OUT_MODE_AUTO && EsIsSelected( es ) )
2668 p_esprops->p_main_es = es;
2671 static void EsOutSelectListFromProps( es_out_t *out, enum es_format_category_e cat )
2673 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2674 es_out_es_props_t *esprops = GetPropsByCat( p_sys, cat );
2675 if( !esprops || !esprops->str_ids )
2676 return;
2678 char *buffer = malloc( strlen( esprops->str_ids ) + 1);
2679 if( !buffer )
2680 return;
2682 bool unselect_others = false;
2683 es_out_id_t *other;
2684 foreach_es_then_es_slaves( other )
2686 if( other->fmt.i_cat != cat )
2687 continue;
2689 bool select = false;
2690 if( !unselect_others )
2692 /* strtok_r will modify str_ids */
2693 strcpy( buffer, esprops->str_ids );
2694 char *saveptr;
2695 for( const char *str_id = strtok_r( buffer, ",", &saveptr );
2696 str_id != NULL;
2697 str_id = strtok_r( NULL, ",", &saveptr ) )
2699 if( strcmp( other->id.str_id, str_id ) == 0 )
2701 select = true;
2702 break;
2707 if( !select )
2709 if( EsIsSelected( other ) )
2710 EsOutUnselectEs( out, other, other->p_pgrm == p_sys->p_pgrm );
2712 else
2714 if( !EsIsSelected( other ) )
2715 EsOutSelectEs( out, other, true );
2716 if( esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE )
2717 unselect_others = true;
2721 free( buffer );
2724 static void EsOutSelectList( es_out_t *out, enum es_format_category_e cat,
2725 vlc_es_id_t * const*es_id_list )
2727 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2728 es_out_id_t *other;
2729 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, cat );
2731 bool unselect_others = false;
2732 foreach_es_then_es_slaves(other)
2734 if( other->fmt.i_cat != cat )
2735 continue;
2737 bool select = false;
2738 if( !unselect_others )
2740 for( size_t i = 0; ; i++ )
2742 vlc_es_id_t *es_id = es_id_list[i];
2743 if( es_id == NULL )
2744 break;
2745 else if( es_id == &other->id )
2747 select = true;
2748 break;
2752 if( !select )
2754 if( EsIsSelected( other ) )
2755 EsOutUnselectEs( out, other, other->p_pgrm == p_sys->p_pgrm );
2757 else
2759 if( !EsIsSelected( other ) )
2760 EsOutSelectEs( out, other, true );
2761 if( p_esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE )
2762 unselect_others = true;
2767 static void EsOutCreateCCChannels( es_out_t *out, vlc_fourcc_t codec, uint64_t i_bitmap,
2768 const char *psz_descfmt, es_out_id_t *parent )
2770 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2771 input_thread_t *p_input = p_sys->p_input;
2773 /* Only one type of captions is allowed ! */
2774 if( parent->cc.type && parent->cc.type != codec )
2775 return;
2777 uint64_t i_existingbitmap = parent->cc.i_bitmap;
2778 for( int i = 0; i_bitmap > 0; i++, i_bitmap >>= 1, i_existingbitmap >>= 1 )
2780 es_format_t fmt;
2782 if( (i_bitmap & 1) == 0 || (i_existingbitmap & 1) )
2783 continue;
2785 msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, parent->fmt.i_id );
2787 es_format_Init( &fmt, SPU_ES, codec );
2788 fmt.subs.cc.i_channel = i;
2789 fmt.i_group = parent->fmt.i_group;
2790 if( asprintf( &fmt.psz_description, psz_descfmt, 1 + i ) == -1 )
2791 fmt.psz_description = NULL;
2793 es_out_id_t **pp_es = &parent->cc.pp_es[i];
2794 *pp_es = EsOutAddLocked( out, parent->p_pgrm->source, &fmt, parent );
2795 es_format_Clean( &fmt );
2797 /* */
2798 parent->cc.i_bitmap |= (1ULL << i);
2799 parent->cc.type = codec;
2801 /* Enable if user specified on command line */
2802 if (p_sys->sub.i_channel == i)
2803 EsOutSelect(out, *pp_es, true);
2808 * Send a block for the given es_out
2810 * \param out the es_out to send from
2811 * \param es the es_out_id
2812 * \param p_block the data block to send
2814 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
2816 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2817 input_thread_t *p_input = p_sys->p_input;
2819 assert( p_block->p_next == NULL );
2821 struct input_stats *stats = input_priv(p_input)->stats;
2822 if( stats != NULL )
2824 input_rate_Add( &stats->demux_bitrate, p_block->i_buffer );
2826 /* Update number of corrupted data packats */
2827 if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
2828 atomic_fetch_add_explicit(&stats->demux_corrupted, 1,
2829 memory_order_relaxed);
2831 /* Update number of discontinuities */
2832 if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )
2833 atomic_fetch_add_explicit(&stats->demux_discontinuity, 1,
2834 memory_order_relaxed);
2837 vlc_mutex_lock( &p_sys->lock );
2839 /* Mark preroll blocks */
2840 if( p_sys->i_preroll_end >= 0 )
2842 vlc_tick_t i_date = p_block->i_pts;
2843 if( p_block->i_pts == VLC_TICK_INVALID )
2844 i_date = p_block->i_dts;
2846 /* In some cases, the demuxer sends non dated packets.
2847 We use interpolation, previous, or pcr value to compare with
2848 preroll target timestamp */
2849 if( i_date == VLC_TICK_INVALID )
2851 if( es->i_pts_level != VLC_TICK_INVALID )
2852 i_date = es->i_pts_level;
2853 else if( es->p_pgrm->i_last_pcr != VLC_TICK_INVALID )
2854 i_date = es->p_pgrm->i_last_pcr;
2857 if( i_date != VLC_TICK_INVALID )
2858 es->i_pts_level = i_date + p_block->i_length;
2860 /* If i_date is still invalid (first/all non dated), expect to be in preroll */
2861 if( i_date == VLC_TICK_INVALID ||
2862 es->i_pts_level < p_sys->i_preroll_end )
2863 p_block->i_flags |= BLOCK_FLAG_PREROLL;
2866 if( !es->p_dec )
2868 block_Release( p_block );
2869 vlc_mutex_unlock( &p_sys->lock );
2870 return VLC_SUCCESS;
2873 #ifdef ENABLE_SOUT
2874 /* Check for sout mode */
2875 if( input_priv(p_input)->p_sout )
2877 bool pace = sout_instance_ControlsPace(input_priv(p_input)->p_sout);
2879 if( input_priv(p_input)->b_out_pace_control != pace )
2881 msg_Dbg( p_input, "switching to %ssync mode", pace ? "a" : "" );
2882 input_priv(p_input)->b_out_pace_control = pace;
2885 #endif
2887 /* Decode */
2888 if( es->p_dec_record )
2890 block_t *p_dup = block_Duplicate( p_block );
2891 if( p_dup )
2892 vlc_input_decoder_Decode( es->p_dec_record, p_dup,
2893 input_priv(p_input)->b_out_pace_control );
2895 vlc_input_decoder_Decode( es->p_dec, p_block,
2896 input_priv(p_input)->b_out_pace_control );
2898 es_format_t fmt_dsc;
2899 vlc_meta_t *p_meta_dsc;
2900 if( vlc_input_decoder_HasFormatChanged( es->p_dec, &fmt_dsc, &p_meta_dsc ) )
2902 if (EsOutEsUpdateFmt( out, es, &fmt_dsc) == VLC_SUCCESS)
2903 EsOutSendEsEvent(out, es, VLC_INPUT_ES_UPDATED, false);
2905 EsOutUpdateInfo(out, es, p_meta_dsc);
2907 es_format_Clean( &fmt_dsc );
2908 if( p_meta_dsc )
2909 vlc_meta_Delete( p_meta_dsc );
2912 /* Check CC status */
2913 decoder_cc_desc_t desc;
2915 vlc_input_decoder_GetCcDesc( es->p_dec, &desc );
2916 if( var_InheritInteger( p_input, "captions" ) == 708 )
2917 EsOutCreateCCChannels( out, VLC_CODEC_CEA708, desc.i_708_channels,
2918 _("DTVCC Closed captions %u"), es );
2919 EsOutCreateCCChannels( out, VLC_CODEC_CEA608, desc.i_608_channels,
2920 _("Closed captions %u"), es );
2922 vlc_mutex_unlock( &p_sys->lock );
2924 return VLC_SUCCESS;
2927 static void
2928 EsOutDrainDecoder( es_out_t *out, es_out_id_t *es )
2930 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2931 assert( es->p_dec );
2933 /* FIXME: This might hold the ES output caller (i.e. the demux), and
2934 * the corresponding thread (typically the input thread), for a little
2935 * bit too long if the ES is deleted in the middle of a stream. */
2936 vlc_input_decoder_Drain( es->p_dec );
2937 EsOutDrainCCChannels( es );
2938 while( !input_Stopped(p_sys->p_input) && !p_sys->b_buffering )
2940 if( vlc_input_decoder_IsEmpty( es->p_dec ) &&
2941 ( !es->p_dec_record || vlc_input_decoder_IsEmpty( es->p_dec_record ) ))
2942 break;
2943 /* FIXME there should be a way to have auto deleted es, but there will be
2944 * a problem when another codec of the same type is created (mainly video) */
2945 vlc_tick_sleep(VLC_TICK_FROM_MS(20));
2949 /*****************************************************************************
2950 * EsOutDelLocked:
2951 *****************************************************************************/
2952 static void EsOutDelLocked( es_out_t *out, es_out_id_t *es )
2954 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
2955 bool b_reselect = false;
2957 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
2959 /* We don't try to reselect */
2960 if( es->p_dec )
2962 EsOutDrainDecoder( out, es );
2963 EsOutUnselectEs( out, es, es->p_pgrm == p_sys->p_pgrm );
2966 EsTerminate(es);
2968 if( es->p_pgrm == p_sys->p_pgrm )
2969 EsOutSendEsEvent( out, es, VLC_INPUT_ES_DELETED, false );
2971 EsOutDeleteInfoEs( out, es );
2973 /* Update program */
2974 if( !EsOutIsGroupSticky( out, es->id.source, es->fmt.i_group ) )
2976 assert( es->p_pgrm );
2978 es->p_pgrm->i_es--;
2979 if( es->p_pgrm->i_es == 0 )
2980 msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" );
2982 if( es->b_scrambled && es->p_pgrm )
2983 EsOutProgramUpdateScrambled( out, es->p_pgrm );
2985 /* */
2986 if( p_esprops )
2988 if( p_esprops->p_main_es == es )
2990 b_reselect = true;
2991 p_esprops->p_main_es = NULL;
2993 p_esprops->i_count--;
2996 /* Re-select another track when needed */
2997 if( b_reselect )
2999 es_out_id_t *other;
3001 foreach_es_then_es_slaves(other)
3002 if( es->fmt.i_cat == other->fmt.i_cat )
3004 if (EsIsSelected(other))
3006 EsOutSendEsEvent(out, es, VLC_INPUT_ES_SELECTED, false);
3007 if( p_esprops->p_main_es == NULL )
3008 p_esprops->p_main_es = other;
3010 else
3011 EsOutSelect(out, other, false);
3015 EsRelease(es);
3018 static void EsOutDel( es_out_t *out, es_out_id_t *es )
3020 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3021 vlc_mutex_lock( &p_sys->lock );
3022 EsOutDelLocked( out, es );
3023 vlc_mutex_unlock( &p_sys->lock );
3026 static int EsOutVaControlLocked( es_out_t *, input_source_t *, int, va_list );
3027 static int EsOutControlLocked( es_out_t *out, input_source_t *source, int i_query, ... )
3029 va_list args;
3031 va_start( args, i_query );
3032 int ret = EsOutVaControlLocked( out, source, i_query, args );
3033 va_end( args );
3034 return ret;
3037 static vlc_tick_t EsOutGetTracksDelay(es_out_t *out)
3039 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3041 vlc_tick_t tracks_delay = 0;
3042 bool has_audio = false;
3043 bool has_spu = false;
3045 /* Get the smaller (and negative) delay between category delays and tracks
3046 * delays */
3047 es_out_id_t *es;
3048 foreach_es_then_es_slaves(es)
3050 if (es->p_dec)
3052 if (es->delay != INT64_MAX)
3053 tracks_delay = __MIN(tracks_delay, es->delay);
3054 else if (es->fmt.i_cat == AUDIO_ES)
3055 has_audio = true;
3056 else if (es->fmt.i_cat == SPU_ES)
3057 has_spu = true;
3060 if (has_audio)
3061 tracks_delay = __MIN(tracks_delay, p_sys->i_audio_delay);
3062 if (has_spu)
3063 tracks_delay = __MIN(tracks_delay, p_sys->i_spu_delay);
3064 return -tracks_delay;
3068 * Control query handler
3070 * \param out the es_out to control
3071 * \param i_query A es_out query as defined in include/ninput.h
3072 * \param args a variable list of arguments for the query
3073 * \return VLC_SUCCESS or an error code
3075 static int EsOutVaControlLocked( es_out_t *out, input_source_t *source,
3076 int i_query, va_list args )
3078 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3079 assert( source ); /* == p_sys->main_source if the given source is NULL */
3081 switch( i_query )
3083 case ES_OUT_SET_ES_STATE:
3085 es_out_id_t *es = va_arg( args, es_out_id_t * );
3086 bool b = va_arg( args, int );
3087 if( b && !EsIsSelected( es ) )
3089 EsOutSelectEs( out, es, true );
3090 return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC;
3092 else if( !b && EsIsSelected( es ) )
3094 EsOutUnselectEs( out, es, es->p_pgrm == p_sys->p_pgrm );
3095 return VLC_SUCCESS;
3097 return VLC_SUCCESS;
3100 case ES_OUT_GET_ES_STATE:
3102 es_out_id_t *es = va_arg( args, es_out_id_t * );
3103 bool *pb = va_arg( args, bool * );
3105 *pb = EsIsSelected( es );
3106 return VLC_SUCCESS;
3109 case ES_OUT_SET_ES_CAT_POLICY:
3111 enum es_format_category_e i_cat = va_arg( args, enum es_format_category_e );
3112 enum es_out_policy_e i_pol = va_arg( args, enum es_out_policy_e );
3113 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, i_cat );
3114 if( p_esprops == NULL )
3115 return VLC_EGENERIC;
3116 p_esprops->e_policy = i_pol;
3117 return VLC_SUCCESS;
3120 case ES_OUT_SET_ES:
3121 case ES_OUT_RESTART_ES:
3123 #define IGNORE_ES DATA_ES
3124 es_out_id_t *es = va_arg( args, es_out_id_t * ), *other;
3126 enum es_format_category_e i_cat;
3127 if( es == NULL )
3128 i_cat = UNKNOWN_ES;
3129 else if( es == es_cat + AUDIO_ES )
3130 i_cat = AUDIO_ES;
3131 else if( es == es_cat + VIDEO_ES )
3132 i_cat = VIDEO_ES;
3133 else if( es == es_cat + SPU_ES )
3134 i_cat = SPU_ES;
3135 else
3137 if (es->b_terminated)
3138 return VLC_EGENERIC;
3139 i_cat = IGNORE_ES;
3142 foreach_es_then_es_slaves(other)
3144 if( i_cat == IGNORE_ES )
3146 if (es == other)
3148 if (i_query == ES_OUT_RESTART_ES && es->p_dec != NULL)
3150 EsOutDestroyDecoder(out, es);
3151 EsOutCreateDecoder(out, es);
3153 else if( i_query == ES_OUT_SET_ES )
3155 EsOutSelect(out, es, true);
3157 break;
3160 else if (i_cat == UNKNOWN_ES || other->fmt.i_cat == i_cat)
3162 if (EsIsSelected(other))
3164 if (i_query == ES_OUT_RESTART_ES)
3166 if (other->p_dec != NULL)
3168 EsOutDestroyDecoder(out, other);
3169 EsOutCreateDecoder(out, other);
3172 else
3173 EsOutUnselectEs(out, other, other->p_pgrm == p_sys->p_pgrm);
3178 EsOutStopFreeVout( out );
3179 return VLC_SUCCESS;
3181 case ES_OUT_UNSET_ES:
3183 es_out_id_t *es = va_arg( args, es_out_id_t * ), *other;
3184 if (es->b_terminated)
3185 return VLC_EGENERIC;
3186 foreach_es_then_es_slaves(other)
3188 if (es == other)
3190 if (EsIsSelected(other))
3192 EsOutUnselectEs(out, other, other->p_pgrm == p_sys->p_pgrm);
3193 EsOutStopFreeVout( out );
3194 return VLC_SUCCESS;
3196 break;
3199 return VLC_EGENERIC;
3202 case ES_OUT_SET_ES_DEFAULT:
3204 es_out_id_t *es = va_arg( args, es_out_id_t * );
3206 if( es == NULL )
3208 /*p_sys->i_default_video_id = -1;*/
3209 /*p_sys->i_default_audio_id = -1;*/
3210 p_sys->sub.i_demux_id = -1;
3212 else if( es == es_cat + AUDIO_ES )
3214 /*p_sys->i_default_video_id = -1;*/
3216 else if( es == es_cat + VIDEO_ES )
3218 /*p_sys->i_default_audio_id = -1;*/
3220 else if( es == es_cat + SPU_ES )
3222 p_sys->sub.i_demux_id = -1;
3224 else
3226 /*if( es->fmt.i_cat == VIDEO_ES )
3227 p_sys->i_default_video_id = es->fmt.i_id;
3228 else
3229 if( es->fmt.i_cat == AUDIO_ES )
3230 p_sys->i_default_audio_id = es->fmt.i_id;
3231 else*/
3232 if( es->fmt.i_cat == SPU_ES )
3233 p_sys->sub.i_demux_id = es->fmt.i_id;
3235 return VLC_SUCCESS;
3238 case ES_OUT_SET_PCR:
3239 case ES_OUT_SET_GROUP_PCR:
3241 es_out_pgrm_t *p_pgrm = NULL;
3242 int i_group = 0;
3243 vlc_tick_t i_pcr;
3245 /* Search program */
3246 if( i_query == ES_OUT_SET_PCR )
3248 p_pgrm = p_sys->p_pgrm;
3249 if( !p_pgrm )
3250 p_pgrm = EsOutProgramAdd( out, source, i_group ); /* Create it */
3252 else
3254 i_group = va_arg( args, int );
3255 p_pgrm = EsOutProgramInsert( out, source, i_group );
3257 if( !p_pgrm )
3258 return VLC_EGENERIC;
3260 i_pcr = va_arg( args, vlc_tick_t );
3261 if( i_pcr == VLC_TICK_INVALID )
3263 msg_Err( p_sys->p_input, "Invalid PCR value in ES_OUT_SET_(GROUP_)PCR !" );
3264 return VLC_EGENERIC;
3267 p_pgrm->i_last_pcr = i_pcr;
3269 input_thread_private_t *priv = input_priv(p_sys->p_input);
3271 /* TODO do not use vlc_tick_now() but proper stream acquisition date */
3272 const bool b_low_delay = priv->b_low_delay;
3273 bool b_extra_buffering_allowed = !b_low_delay && EsOutIsExtraBufferingAllowed( out );
3274 vlc_tick_t i_late = input_clock_Update(
3275 p_pgrm->p_input_clock, VLC_OBJECT(p_sys->p_input),
3276 priv->b_can_pace_control || p_sys->b_buffering,
3277 b_extra_buffering_allowed,
3278 i_pcr, vlc_tick_now() );
3280 if( !p_sys->p_pgrm )
3281 return VLC_SUCCESS;
3283 if( p_sys->b_buffering )
3285 /* Check buffering state on master clock update */
3286 EsOutDecodersStopBuffering( out, false );
3288 else if( p_pgrm == p_sys->p_pgrm )
3290 /* Last pcr/clock update was late. We need to compensate by offsetting
3291 from the clock the rendering dates */
3292 if( i_late > 0 && ( !priv->p_sout ||
3293 !priv->b_out_pace_control ) )
3295 /* input_clock_GetJitter returns compound delay:
3296 * - initial pts delay (buffering/caching)
3297 * - jitter compensation
3298 * - track offset pts delay
3299 * updated on input_clock_Update
3300 * Late/jitter amount is updated from median of late values */
3301 vlc_tick_t i_clock_total_delay = input_clock_GetJitter( p_pgrm->p_input_clock );
3303 /* Current jitter */
3304 vlc_tick_t i_new_jitter = i_clock_total_delay
3305 - p_sys->i_tracks_pts_delay
3306 - p_sys->i_pts_delay;
3308 /* If the clock update is late, we have 2 possibilities:
3309 * - offset rendering a bit more by increasing the total pts-delay
3310 * - ignore, set clock to a new reference ahead of previous one
3311 * and flush buffers (because all previous pts will now be late) */
3313 /* Avoid dangerously high value */
3314 /* If the jitter increase is over our max or the total hits the maximum */
3315 if( i_new_jitter > priv->i_jitter_max ||
3316 i_clock_total_delay > INPUT_PTS_DELAY_MAX ||
3317 /* jitter is always 0 due to median calculation first output
3318 and low delay can't allow non reversible jitter increase
3319 in branch below */
3320 (b_low_delay && i_late > priv->i_jitter_max) )
3322 msg_Err( p_sys->p_input,
3323 "ES_OUT_SET_(GROUP_)PCR is called %d ms late (jitter of %d ms ignored)",
3324 (int)MS_FROM_VLC_TICK(i_late),
3325 (int)MS_FROM_VLC_TICK(i_new_jitter) );
3327 /* don't change the current jitter */
3328 i_new_jitter = p_sys->i_pts_jitter;
3330 else
3332 msg_Err( p_sys->p_input,
3333 "ES_OUT_SET_(GROUP_)PCR is called %d ms late (pts_delay increased to %d ms)",
3334 (int)MS_FROM_VLC_TICK(i_late),
3335 (int)MS_FROM_VLC_TICK(i_clock_total_delay) );
3338 /* Force a rebufferization when we are too late */
3339 EsOutControlLocked( out, source, ES_OUT_RESET_PCR );
3341 EsOutPrivControlLocked( out, ES_OUT_PRIV_SET_JITTER,
3342 p_sys->i_pts_delay, i_new_jitter,
3343 p_sys->i_cr_average );
3346 return VLC_SUCCESS;
3349 case ES_OUT_RESET_PCR:
3350 msg_Dbg( p_sys->p_input, "ES_OUT_RESET_PCR called" );
3351 EsOutChangePosition( out, true );
3352 return VLC_SUCCESS;
3354 case ES_OUT_SET_GROUP:
3356 int i = va_arg( args, int );
3357 es_out_pgrm_t *p_pgrm;
3359 p_sys->i_group_id = i;
3361 vlc_list_foreach(p_pgrm, &p_sys->programs, node)
3362 if( p_pgrm->i_id == i )
3364 EsOutProgramSelect( out, p_pgrm );
3365 return VLC_SUCCESS;
3367 return VLC_EGENERIC;
3370 case ES_OUT_SET_ES_FMT:
3372 es_out_id_t *es = va_arg( args, es_out_id_t * );
3373 es_format_t *p_fmt = va_arg( args, es_format_t * );
3374 if( es == NULL || es->fmt.i_cat != p_fmt->i_cat
3375 || es->fmt.i_id != p_fmt->i_id
3376 || es->fmt.i_group != p_fmt->i_group )
3377 return VLC_EGENERIC;
3379 es_format_Clean( &es->fmt );
3380 int ret = es_format_Copy( &es->fmt, p_fmt );
3381 if( ret != VLC_SUCCESS )
3382 return ret;
3383 EsOutFillEsFmt( out, &es->fmt );
3384 EsOutUpdateEsLanguageTitle(es, &es->fmt);
3386 const bool b_was_selected = EsIsSelected( es );
3387 if( es->p_dec )
3389 EsOutDrainDecoder( out, es );
3390 EsDeleteCCChannels( out, es );
3391 EsOutDestroyDecoder( out, es );
3394 if(b_was_selected)
3395 EsOutCreateDecoder( out, es );
3397 EsOutSendEsEvent( out, es, VLC_INPUT_ES_UPDATED, false );
3399 return VLC_SUCCESS;
3402 case ES_OUT_SET_ES_SCRAMBLED_STATE:
3404 es_out_id_t *es = va_arg( args, es_out_id_t * );
3405 bool b_scrambled = (bool)va_arg( args, int );
3407 if( es->p_pgrm && !es->b_scrambled != !b_scrambled )
3409 es->b_scrambled = b_scrambled;
3410 EsOutProgramUpdateScrambled( out, es->p_pgrm );
3412 return VLC_SUCCESS;
3415 case ES_OUT_SET_NEXT_DISPLAY_TIME:
3417 const int64_t i_date = va_arg( args, int64_t );
3419 if( i_date < 0 )
3420 return VLC_EGENERIC;
3422 p_sys->i_preroll_end = i_date;
3424 return VLC_SUCCESS;
3426 case ES_OUT_SET_GROUP_META:
3428 int i_group = va_arg( args, int );
3429 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
3431 EsOutProgramMeta( out, source, i_group, p_meta );
3432 return VLC_SUCCESS;
3434 case ES_OUT_SET_GROUP_EPG:
3436 int i_group = va_arg( args, int );
3437 const vlc_epg_t *p_epg = va_arg( args, const vlc_epg_t * );
3439 EsOutProgramEpg( out, source, i_group, p_epg );
3440 return VLC_SUCCESS;
3442 case ES_OUT_SET_GROUP_EPG_EVENT:
3444 int i_group = va_arg( args, int );
3445 const vlc_epg_event_t *p_evt = va_arg( args, const vlc_epg_event_t * );
3447 EsOutProgramEpgEvent( out, source, i_group, p_evt );
3448 return VLC_SUCCESS;
3450 case ES_OUT_SET_EPG_TIME:
3452 int64_t i64 = va_arg( args, int64_t );
3454 EsOutEpgTime( out, i64 );
3455 return VLC_SUCCESS;
3458 case ES_OUT_DEL_GROUP:
3460 int i_group = va_arg( args, int );
3462 return EsOutProgramDel( out, source, i_group );
3465 case ES_OUT_SET_META:
3467 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
3469 EsOutGlobalMeta( out, p_meta );
3470 return VLC_SUCCESS;
3473 case ES_OUT_GET_EMPTY:
3475 bool *pb = va_arg( args, bool* );
3476 *pb = EsOutDecodersIsEmpty( out );
3477 return VLC_SUCCESS;
3480 case ES_OUT_GET_PCR_SYSTEM:
3482 if( p_sys->b_buffering )
3483 return VLC_EGENERIC;
3485 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
3486 if( !p_pgrm )
3487 return VLC_EGENERIC;
3489 vlc_tick_t *pi_system = va_arg( args, vlc_tick_t *);
3490 vlc_tick_t *pi_delay = va_arg( args, vlc_tick_t *);
3491 input_clock_GetSystemOrigin( p_pgrm->p_input_clock, pi_system, pi_delay );
3492 return VLC_SUCCESS;
3495 case ES_OUT_MODIFY_PCR_SYSTEM:
3497 if( p_sys->b_buffering )
3498 return VLC_EGENERIC;
3500 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
3501 if( !p_pgrm )
3502 return VLC_EGENERIC;
3504 const bool b_absolute = va_arg( args, int );
3505 const vlc_tick_t i_system = va_arg( args, vlc_tick_t );
3506 input_clock_ChangeSystemOrigin( p_pgrm->p_input_clock, b_absolute, i_system );
3507 return VLC_SUCCESS;
3510 case ES_OUT_POST_SUBNODE:
3512 input_thread_t *input = p_sys->p_input;
3513 input_item_node_t *node = va_arg(args, input_item_node_t *);
3514 input_SendEventParsing(input, node);
3515 input_item_node_Delete(node);
3517 return VLC_SUCCESS;
3520 case ES_OUT_VOUT_SET_MOUSE_EVENT:
3522 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3524 if( !p_es || p_es->fmt.i_cat != VIDEO_ES )
3525 return VLC_EGENERIC;
3527 p_es->mouse_event_cb = va_arg( args, vlc_mouse_event );
3528 p_es->mouse_event_userdata = va_arg( args, void * );
3530 if( p_es->p_dec )
3531 vlc_input_decoder_SetVoutMouseEvent( p_es->p_dec,
3532 p_es->mouse_event_cb, p_es->mouse_event_userdata );
3534 return VLC_SUCCESS;
3536 case ES_OUT_VOUT_ADD_OVERLAY:
3538 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3539 subpicture_t *sub = va_arg( args, subpicture_t * );
3540 size_t *channel = va_arg( args, size_t * );
3541 if( p_es && p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
3542 return vlc_input_decoder_AddVoutOverlay( p_es->p_dec, sub, channel );
3543 return VLC_EGENERIC;
3545 case ES_OUT_VOUT_DEL_OVERLAY:
3547 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3548 size_t channel = va_arg( args, size_t );
3549 if( p_es && p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
3550 return vlc_input_decoder_DelVoutOverlay( p_es->p_dec, channel );
3551 return VLC_EGENERIC;
3553 case ES_OUT_SPU_SET_HIGHLIGHT:
3555 es_out_id_t *p_es = va_arg( args, es_out_id_t * );
3556 const vlc_spu_highlight_t *spu_hl =
3557 va_arg( args, const vlc_spu_highlight_t * );
3558 if( p_es && p_es->fmt.i_cat == SPU_ES && p_es->p_dec )
3559 return vlc_input_decoder_SetSpuHighlight( p_es->p_dec, spu_hl );
3560 return VLC_EGENERIC;
3562 default: vlc_assert_unreachable();
3566 static int EsOutVaPrivControlLocked( es_out_t *out, int query, va_list args )
3568 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3570 switch (query)
3572 case ES_OUT_PRIV_SET_MODE:
3574 const int i_mode = va_arg( args, int );
3575 assert( i_mode == ES_OUT_MODE_NONE || i_mode == ES_OUT_MODE_ALL ||
3576 i_mode == ES_OUT_MODE_AUTO || i_mode == ES_OUT_MODE_PARTIAL ||
3577 i_mode == ES_OUT_MODE_END );
3579 if (i_mode != ES_OUT_MODE_NONE && !p_sys->b_active && !vlc_list_is_empty(&p_sys->es))
3581 /* XXX Terminate vout if there are tracks but no video one.
3582 * This one is not mandatory but is he earliest place where it
3583 * can be done */
3584 es_out_id_t *p_es;
3585 bool found = false;
3587 foreach_es_then_es_slaves(p_es)
3588 if( p_es->fmt.i_cat == VIDEO_ES && !found /* nested loop */ )
3590 found = true;
3591 break;
3594 if (!found)
3595 EsOutStopFreeVout( out );
3597 p_sys->b_active = i_mode != ES_OUT_MODE_NONE;
3598 p_sys->i_mode = i_mode;
3600 /* Reapply policy mode */
3601 es_out_id_t *es;
3603 foreach_es_then_es_slaves(es)
3605 if (EsIsSelected(es))
3606 EsOutUnselectEs(out, es, es->p_pgrm == p_sys->p_pgrm);
3608 foreach_es_then_es_slaves(es)
3610 EsOutSelect(out, es, false);
3613 if( i_mode == ES_OUT_MODE_END )
3614 EsOutTerminate( out );
3615 return VLC_SUCCESS;
3617 case ES_OUT_PRIV_SET_ES:
3618 case ES_OUT_PRIV_UNSET_ES:
3619 case ES_OUT_PRIV_RESTART_ES:
3621 vlc_es_id_t *es_id = va_arg( args, vlc_es_id_t * );
3622 es_out_id_t *es = vlc_es_id_get_out( es_id );
3623 int new_query;
3624 switch( query )
3626 case ES_OUT_PRIV_SET_ES: new_query = ES_OUT_SET_ES; break;
3627 case ES_OUT_PRIV_UNSET_ES: new_query = ES_OUT_UNSET_ES; break;
3628 case ES_OUT_PRIV_RESTART_ES: new_query = ES_OUT_RESTART_ES; break;
3629 default: vlc_assert_unreachable();
3631 return EsOutControlLocked( out, p_sys->main_source, new_query, es );
3633 case ES_OUT_PRIV_SET_ES_CAT_IDS:
3635 enum es_format_category_e cat = va_arg( args, enum es_format_category_e );
3636 const char *str_ids = va_arg( args, const char * );
3637 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, cat );
3638 free( p_esprops->str_ids );
3639 p_esprops->str_ids = str_ids ? strdup( str_ids ) : NULL;
3641 if( p_esprops->str_ids )
3643 /* Update new tracks selection using the new str_ids */
3644 EsOutSelectListFromProps( out, cat );
3647 return VLC_SUCCESS;
3649 case ES_OUT_PRIV_GET_WAKE_UP:
3651 vlc_tick_t *pi_wakeup = va_arg( args, vlc_tick_t* );
3652 *pi_wakeup = EsOutGetWakeup( out );
3653 return VLC_SUCCESS;
3655 case ES_OUT_PRIV_SET_ES_LIST:
3657 enum es_format_category_e cat = va_arg( args, enum es_format_category_e );
3658 vlc_es_id_t *const*es_id_list = va_arg( args, vlc_es_id_t ** );
3659 EsOutSelectList( out, cat, es_id_list );
3660 return VLC_SUCCESS;
3662 case ES_OUT_PRIV_STOP_ALL_ES:
3664 es_out_id_t *es;
3665 int count = 0;
3667 foreach_es_then_es_slaves(es)
3668 count++;
3670 vlc_es_id_t **selected_es = vlc_alloc(count + 1, sizeof(vlc_es_id_t *));
3671 if (!selected_es)
3672 return VLC_ENOMEM;
3674 *va_arg(args, vlc_es_id_t ***) = selected_es;
3676 foreach_es_then_es_slaves(es)
3678 if (EsIsSelected(es))
3680 EsOutDestroyDecoder(out, es);
3681 *selected_es++ = vlc_es_id_Hold(&es->id);
3683 *selected_es = NULL;
3685 return VLC_SUCCESS;
3687 case ES_OUT_PRIV_START_ALL_ES:
3689 vlc_es_id_t **selected_es = va_arg( args, vlc_es_id_t ** );
3690 vlc_es_id_t **selected_es_it = selected_es;
3691 for( vlc_es_id_t *id = *selected_es_it; id != NULL;
3692 id = *++selected_es_it )
3694 EsOutCreateDecoder( out, vlc_es_id_get_out( id ) );
3695 vlc_es_id_Release( id );
3697 free(selected_es);
3698 EsOutStopFreeVout( out );
3699 return VLC_SUCCESS;
3701 case ES_OUT_PRIV_GET_BUFFERING:
3703 bool *pb = va_arg( args, bool* );
3704 *pb = p_sys->b_buffering;
3705 return VLC_SUCCESS;
3707 case ES_OUT_PRIV_SET_ES_DELAY:
3709 vlc_es_id_t *es_id = va_arg( args, vlc_es_id_t * );
3710 es_out_id_t *es = vlc_es_id_get_out( es_id );
3711 const vlc_tick_t delay = va_arg(args, vlc_tick_t);
3712 EsOutSetEsDelay(out, es, delay);
3713 return VLC_SUCCESS;
3715 case ES_OUT_PRIV_SET_DELAY:
3717 const int i_cat = va_arg( args, int );
3718 const vlc_tick_t i_delay = va_arg( args, vlc_tick_t );
3719 EsOutSetDelay( out, i_cat, i_delay );
3720 return VLC_SUCCESS;
3722 case ES_OUT_PRIV_SET_RECORD_STATE:
3724 bool b = va_arg( args, int );
3725 return EsOutSetRecord( out, b );
3727 case ES_OUT_PRIV_SET_PAUSE_STATE:
3729 const bool b_source_paused = (bool)va_arg( args, int );
3730 const bool b_paused = (bool)va_arg( args, int );
3731 const vlc_tick_t i_date = va_arg( args, vlc_tick_t );
3733 assert( !b_source_paused == !b_paused );
3734 EsOutChangePause( out, b_paused, i_date );
3736 return VLC_SUCCESS;
3738 case ES_OUT_PRIV_SET_RATE:
3740 const float src_rate = va_arg( args, double );
3741 const float rate = va_arg( args, double );
3743 assert( src_rate == rate );
3744 EsOutChangeRate( out, rate );
3746 return VLC_SUCCESS;
3748 case ES_OUT_PRIV_SET_FRAME_NEXT:
3749 EsOutFrameNext( out );
3750 return VLC_SUCCESS;
3751 case ES_OUT_PRIV_SET_TIMES:
3753 double f_position = va_arg( args, double );
3754 vlc_tick_t i_time = va_arg( args, vlc_tick_t );
3755 vlc_tick_t i_normal_time = va_arg( args, vlc_tick_t );
3756 vlc_tick_t i_length = va_arg( args, vlc_tick_t );
3758 if( !p_sys->b_buffering )
3760 vlc_tick_t i_delay;
3762 /* Fix for buffering delay */
3763 if( !input_priv(p_sys->p_input)->p_sout ||
3764 !input_priv(p_sys->p_input)->b_out_pace_control )
3765 i_delay = EsOutGetBuffering( out );
3766 else
3767 i_delay = 0;
3769 if( i_time != VLC_TICK_INVALID )
3771 i_time -= i_delay;
3772 if( i_time < VLC_TICK_0 )
3773 i_time = VLC_TICK_0;
3776 if( i_length != 0 )
3777 f_position -= (double)i_delay / i_length;
3778 if( f_position < 0 )
3779 f_position = 0;
3781 assert( i_normal_time >= VLC_TICK_0 );
3783 input_SendEventTimes( p_sys->p_input, f_position, i_time,
3784 i_normal_time, i_length );
3786 else
3787 input_SendEventTimes( p_sys->p_input, 0.0, VLC_TICK_INVALID,
3788 i_normal_time, i_length );
3789 return VLC_SUCCESS;
3791 case ES_OUT_PRIV_SET_JITTER:
3793 vlc_tick_t i_pts_delay = va_arg( args, vlc_tick_t );
3794 vlc_tick_t i_pts_jitter = va_arg( args, vlc_tick_t );
3795 int i_cr_average = va_arg( args, int );
3796 es_out_pgrm_t *pgrm;
3798 const vlc_tick_t i_tracks_pts_delay = EsOutGetTracksDelay(out);
3799 bool b_change_clock =
3800 i_pts_delay != p_sys->i_pts_delay ||
3801 i_pts_jitter != p_sys->i_pts_jitter ||
3802 i_cr_average != p_sys->i_cr_average ||
3803 i_tracks_pts_delay != p_sys->i_tracks_pts_delay;
3805 assert( i_pts_jitter >= 0 );
3806 p_sys->i_pts_delay = i_pts_delay;
3807 p_sys->i_pts_jitter = i_pts_jitter;
3808 p_sys->i_cr_average = i_cr_average;
3809 p_sys->i_tracks_pts_delay = i_tracks_pts_delay;
3811 if (b_change_clock)
3813 i_pts_delay += i_pts_jitter + i_tracks_pts_delay;
3815 vlc_list_foreach(pgrm, &p_sys->programs, node)
3817 input_clock_SetJitter(pgrm->p_input_clock, i_pts_delay,
3818 i_cr_average);
3819 vlc_clock_main_SetInputDejitter(pgrm->p_main_clock, i_pts_delay);
3822 return VLC_SUCCESS;
3824 case ES_OUT_PRIV_GET_GROUP_FORCED:
3826 int *pi_group = va_arg( args, int * );
3827 *pi_group = p_sys->i_group_id;
3828 return VLC_SUCCESS;
3830 case ES_OUT_PRIV_SET_EOS:
3832 es_out_id_t *id;
3833 foreach_es_then_es_slaves(id)
3834 if (id->p_dec != NULL)
3835 vlc_input_decoder_Drain(id->p_dec);
3836 return VLC_SUCCESS;
3838 case ES_OUT_PRIV_SET_VBI_PAGE:
3839 case ES_OUT_PRIV_SET_VBI_TRANSPARENCY:
3841 vlc_es_id_t *es_id = va_arg( args, vlc_es_id_t * );
3842 es_out_id_t *es = vlc_es_id_get_out( es_id );
3843 assert(es);
3844 if( !es->p_dec )
3845 return VLC_EGENERIC;
3847 int ret;
3848 if( query == ES_OUT_PRIV_SET_VBI_PAGE )
3850 unsigned page = va_arg( args, unsigned );
3851 ret = vlc_input_decoder_SetVbiPage( es->p_dec, page );
3852 if( ret == VLC_SUCCESS )
3853 input_SendEventVbiPage( p_sys->p_input, page );
3855 else
3857 bool transp = va_arg( args, int );
3858 ret = vlc_input_decoder_SetVbiOpaque( es->p_dec, !transp );
3859 if( ret == VLC_SUCCESS )
3860 input_SendEventVbiTransparency( p_sys->p_input, transp );
3862 return ret;
3864 default: vlc_assert_unreachable();
3869 static int EsOutControl( es_out_t *out, input_source_t *source,
3870 int i_query, va_list args )
3872 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3873 int i_ret;
3875 if( !source )
3876 source = p_sys->main_source;
3878 vlc_mutex_lock( &p_sys->lock );
3879 i_ret = EsOutVaControlLocked( out, source, i_query, args );
3880 vlc_mutex_unlock( &p_sys->lock );
3882 return i_ret;
3885 static int EsOutPrivControlLocked( es_out_t *out, int i_query, ... )
3887 va_list args;
3889 va_start( args, i_query );
3890 int ret = EsOutVaPrivControlLocked( out, i_query, args );
3891 va_end( args );
3892 return ret;
3895 static int EsOutPrivControl( es_out_t *out, int query, va_list args )
3897 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
3899 vlc_mutex_lock( &p_sys->lock );
3900 int ret = EsOutVaPrivControlLocked( out, query, args );
3901 vlc_mutex_unlock( &p_sys->lock );
3903 return ret;
3906 static const struct es_out_callbacks es_out_cbs =
3908 .add = EsOutAdd,
3909 .send = EsOutSend,
3910 .del = EsOutDel,
3911 .control = EsOutControl,
3912 .destroy = EsOutDelete,
3913 .priv_control = EsOutPrivControl,
3916 /****************************************************************************
3917 * LanguageGetName: try to expend iso639 into plain name
3918 ****************************************************************************/
3919 static char *LanguageGetName( const char *psz_code )
3921 const iso639_lang_t *pl;
3923 if( psz_code == NULL || !strcmp( psz_code, "und" ) )
3925 return strdup( "" );
3928 if( strlen( psz_code ) == 2 )
3930 pl = GetLang_1( psz_code );
3932 else if( strlen( psz_code ) == 3 )
3934 pl = GetLang_2B( psz_code );
3935 if( !strcmp( pl->psz_iso639_1, "??" ) )
3937 pl = GetLang_2T( psz_code );
3940 else
3942 char *lang = LanguageGetCode( psz_code );
3943 pl = GetLang_1( lang );
3944 free( lang );
3947 if( !strcmp( pl->psz_iso639_1, "??" ) )
3949 return strdup( psz_code );
3951 else
3953 return strdup( vlc_gettext(pl->psz_eng_name) );
3957 /* Get a 2 char code */
3958 static char *LanguageGetCode( const char *psz_lang )
3960 const iso639_lang_t *pl;
3962 if( psz_lang == NULL || *psz_lang == '\0' )
3963 return strdup("??");
3965 for( pl = p_languages; pl->psz_eng_name != NULL; pl++ )
3967 if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
3968 !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
3969 !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
3970 !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
3971 return strdup( pl->psz_iso639_1 );
3974 return strdup("??");
3977 static char **LanguageSplit( const char *psz_langs )
3979 char *psz_dup;
3980 char *psz_parser;
3981 char **ppsz = NULL;
3982 int i_psz = 0;
3984 if( psz_langs == NULL ) return NULL;
3986 psz_parser = psz_dup = strdup(psz_langs);
3988 while( psz_parser && *psz_parser )
3990 char *psz;
3991 char *psz_code;
3993 psz = strchr(psz_parser, ',' );
3994 if( psz ) *psz++ = '\0';
3996 if( !strcmp( psz_parser, "any" ) )
3998 TAB_APPEND( i_psz, ppsz, strdup("any") );
4000 else if( !strcmp( psz_parser, "none" ) )
4002 TAB_APPEND( i_psz, ppsz, strdup("none") );
4004 else
4006 psz_code = LanguageGetCode( psz_parser );
4007 if( strcmp( psz_code, "??" ) )
4009 TAB_APPEND( i_psz, ppsz, psz_code );
4011 else
4013 free( psz_code );
4017 psz_parser = psz;
4020 if( i_psz )
4022 TAB_APPEND( i_psz, ppsz, NULL );
4025 free( psz_dup );
4026 return ppsz;
4029 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang )
4031 if( !ppsz_langs || !psz_lang )
4032 return -1;
4034 for( int i = 0; ppsz_langs[i]; i++ )
4036 if( !strcasecmp( ppsz_langs[i], psz_lang ) ||
4037 ( !strcasecmp( ppsz_langs[i], "any" ) && strcasecmp( psz_lang, "none") ) )
4038 return i;
4039 if( !strcasecmp( ppsz_langs[i], "none" ) )
4040 break;
4043 return -1;
4046 static int EsOutEsUpdateFmt(es_out_t *out, es_out_id_t *es,
4047 const es_format_t *fmt)
4049 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
4050 input_thread_t *p_input = p_sys->p_input;
4052 assert(es->fmt.i_cat == fmt->i_cat);
4054 es_format_t update = *fmt;
4056 /* decoder may overwrite these values */
4057 update.i_id = es->fmt.i_id;
4058 update.i_group = es->fmt.i_group;
4059 update.i_priority = es->fmt.i_priority;
4060 update.i_codec = es->fmt.i_codec;
4061 update.i_original_fourcc = es->fmt.i_original_fourcc;
4063 if (update.psz_language == NULL)
4064 update.psz_language = es->fmt.psz_language;
4065 if (update.psz_description == NULL)
4066 update.psz_description = es->fmt.psz_description;
4067 if (update.i_cat == SPU_ES && update.subs.psz_encoding == NULL)
4068 update.subs.psz_encoding = es->fmt.subs.psz_encoding;
4069 if (update.i_extra_languages == 0)
4071 assert(update.p_extra_languages == NULL);
4072 update.i_extra_languages = es->fmt.i_extra_languages;
4073 update.p_extra_languages = es->fmt.p_extra_languages;
4076 es_format_Clean(&es->fmt_out);
4077 int ret = es_format_Copy(&es->fmt_out, &update);
4078 if (ret == VLC_SUCCESS)
4080 EsOutUpdateEsLanguageTitle(es, &es->fmt_out);
4081 input_item_UpdateTracksInfo(input_GetItem(p_input), &es->fmt_out);
4084 return ret;
4087 static void info_category_AddCodecInfo( info_category_t* p_cat,
4088 const char *psz_info,
4089 vlc_fourcc_t i_fourcc,
4090 const char *psz_description )
4092 const char *ps_fcc = (const char*)&i_fourcc;
4093 if( psz_description && *psz_description )
4094 info_category_AddInfo( p_cat, psz_info, "%s (%.4s)",
4095 psz_description, ps_fcc );
4096 else if ( i_fourcc != VLC_FOURCC(0,0,0,0) )
4097 info_category_AddInfo( p_cat, psz_info, "%.4s", ps_fcc );
4099 /****************************************************************************
4100 * EsOutUpdateInfo:
4101 * - add meta info to the playlist item
4102 ****************************************************************************/
4103 static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const vlc_meta_t *p_meta )
4105 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
4106 input_thread_t *p_input = p_sys->p_input;
4107 input_item_t *p_item = input_priv(p_input)->p_item;
4108 const es_format_t *p_fmt_es = &es->fmt;
4109 const es_format_t *fmt = es->fmt_out.i_cat != UNKNOWN_ES ? &es->fmt_out : &es->fmt;
4111 input_item_UpdateTracksInfo( p_item , fmt );
4113 /* Create category */
4114 char* psz_cat = EsInfoCategoryName( es );
4116 if( unlikely( !psz_cat ) )
4117 return;
4119 info_category_t* p_cat = info_category_New( psz_cat );
4121 free( psz_cat );
4123 if( unlikely( !p_cat ) )
4124 return;
4126 if( es->psz_title )
4127 info_category_AddInfo( p_cat, _("Title"), "%s", es->psz_title );
4129 const vlc_fourcc_t i_codec_fourcc = p_fmt_es->i_original_fourcc;
4130 const char *psz_codec_description =
4131 vlc_fourcc_GetDescription( p_fmt_es->i_cat, i_codec_fourcc );
4132 info_category_AddCodecInfo( p_cat, _("Codec"),
4133 i_codec_fourcc, psz_codec_description );
4135 if( es->psz_language && *es->psz_language )
4136 info_category_AddInfo( p_cat, _("Language"), "%s",
4137 es->psz_language );
4138 if( fmt->psz_description && *fmt->psz_description )
4139 info_category_AddInfo( p_cat, _("Description"), "%s",
4140 fmt->psz_description );
4142 switch( fmt->i_cat )
4144 case AUDIO_ES:
4145 info_category_AddInfo( p_cat, _("Type"), _("Audio") );
4147 if( p_fmt_es->audio.i_physical_channels )
4148 info_category_AddInfo( p_cat, _("Channels"), "%s",
4149 vlc_gettext( aout_FormatPrintChannels( &p_fmt_es->audio ) ) );
4151 if( p_fmt_es->audio.i_rate )
4152 info_category_AddInfo( p_cat, _("Sample rate"), _("%u Hz"),
4153 p_fmt_es->audio.i_rate );
4155 unsigned int i_orgbps = p_fmt_es->audio.i_bitspersample;
4156 if( i_orgbps == 0 )
4157 i_orgbps = aout_BitsPerSample( p_fmt_es->i_codec );
4158 if( i_orgbps != 0 )
4159 info_category_AddInfo( p_cat, _("Bits per sample"), "%u",
4160 i_orgbps );
4162 if( fmt->audio.i_format &&
4163 fmt->audio.i_format != p_fmt_es->i_codec )
4165 psz_codec_description = vlc_fourcc_GetDescription( AUDIO_ES,
4166 fmt->audio.i_format );
4167 info_category_AddCodecInfo( p_cat, _("Decoded format"),
4168 fmt->audio.i_format,
4169 psz_codec_description );
4172 if( fmt->audio.i_physical_channels &&
4173 fmt->audio.i_physical_channels != p_fmt_es->audio.i_physical_channels )
4174 info_category_AddInfo( p_cat, _("Decoded channels"), "%s",
4175 vlc_gettext( aout_FormatPrintChannels( &fmt->audio ) ) );
4177 if( fmt->audio.i_rate &&
4178 fmt->audio.i_rate != p_fmt_es->audio.i_rate )
4179 info_category_AddInfo( p_cat, _("Decoded sample rate"), _("%u Hz"),
4180 fmt->audio.i_rate );
4182 unsigned i_outbps = fmt->audio.i_bitspersample;
4183 if( i_outbps == 0 )
4184 i_outbps = aout_BitsPerSample( fmt->i_codec );
4185 if( i_outbps != 0 && i_outbps != i_orgbps )
4186 info_category_AddInfo( p_cat, _("Decoded bits per sample"), "%u",
4187 i_outbps );
4189 if( fmt->i_bitrate != 0 )
4191 info_category_AddInfo( p_cat, _("Bitrate"), _("%u kb/s"),
4192 fmt->i_bitrate / 1000 );
4194 for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
4196 const audio_replay_gain_t *p_rg = &fmt->audio_replay_gain;
4197 if( !p_rg->pb_gain[i] )
4198 continue;
4199 const char *psz_name;
4200 if( i == AUDIO_REPLAY_GAIN_TRACK )
4201 psz_name = _("Track replay gain");
4202 else
4203 psz_name = _("Album replay gain");
4204 info_category_AddInfo( p_cat, psz_name, _("%.2f dB"),
4205 p_rg->pf_gain[i] );
4207 break;
4209 case VIDEO_ES:
4210 info_category_AddInfo( p_cat, _("Type"), _("Video") );
4212 if( fmt->video.i_visible_width > 0 &&
4213 fmt->video.i_visible_height > 0 )
4214 info_category_AddInfo( p_cat, _("Video resolution"), "%ux%u",
4215 fmt->video.i_visible_width,
4216 fmt->video.i_visible_height);
4218 if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
4219 info_category_AddInfo( p_cat, _("Buffer dimensions"), "%ux%u",
4220 fmt->video.i_width, fmt->video.i_height );
4222 if( fmt->video.i_frame_rate > 0 &&
4223 fmt->video.i_frame_rate_base > 0 )
4225 if( fmt->video.i_frame_rate_base == 1 )
4226 info_category_AddInfo( p_cat, _("Frame rate"), "%u",
4227 fmt->video.i_frame_rate );
4228 else
4229 info_category_AddInfo( p_cat, _("Frame rate"), "%.6f",
4230 (double)fmt->video.i_frame_rate
4231 / (double)fmt->video.i_frame_rate_base );
4233 if( fmt->i_codec != p_fmt_es->i_codec )
4235 psz_codec_description = vlc_fourcc_GetDescription( VIDEO_ES,
4236 fmt->i_codec );
4237 info_category_AddCodecInfo( p_cat, _("Decoded format"),
4238 fmt->i_codec,
4239 psz_codec_description );
4242 static const char orient_names[][13] = {
4243 N_("Top left"), N_("Left top"),
4244 N_("Right bottom"), N_("Top right"),
4245 N_("Bottom left"), N_("Bottom right"),
4246 N_("Left bottom"), N_("Right top"),
4248 info_category_AddInfo( p_cat, _("Orientation"), "%s",
4249 vlc_gettext(orient_names[fmt->video.orientation]) );
4251 if( fmt->video.primaries != COLOR_PRIMARIES_UNDEF )
4253 static const char primaries_names[][32] = {
4254 [COLOR_PRIMARIES_UNDEF] = N_("Undefined"),
4255 [COLOR_PRIMARIES_BT601_525] =
4256 N_("ITU-R BT.601 (525 lines, 60 Hz)"),
4257 [COLOR_PRIMARIES_BT601_625] =
4258 N_("ITU-R BT.601 (625 lines, 50 Hz)"),
4259 [COLOR_PRIMARIES_BT709] = "ITU-R BT.709",
4260 [COLOR_PRIMARIES_BT2020] = "ITU-R BT.2020",
4261 [COLOR_PRIMARIES_DCI_P3] = "DCI/P3 D65",
4262 [COLOR_PRIMARIES_BT470_M] = "ITU-R BT.470 M",
4264 static_assert(ARRAY_SIZE(primaries_names) == COLOR_PRIMARIES_MAX+1,
4265 "Color primiaries table mismatch");
4266 info_category_AddInfo( p_cat, _("Color primaries"), "%s",
4267 vlc_gettext(primaries_names[fmt->video.primaries]) );
4269 if( fmt->video.transfer != TRANSFER_FUNC_UNDEF )
4271 static const char func_names[][20] = {
4272 [TRANSFER_FUNC_UNDEF] = N_("Undefined"),
4273 [TRANSFER_FUNC_LINEAR] = N_("Linear"),
4274 [TRANSFER_FUNC_SRGB] = "sRGB",
4275 [TRANSFER_FUNC_BT470_BG] = "ITU-R BT.470 BG",
4276 [TRANSFER_FUNC_BT470_M] = "ITU-R BT.470 M",
4277 [TRANSFER_FUNC_BT709] = "ITU-R BT.709",
4278 [TRANSFER_FUNC_SMPTE_ST2084] = "SMPTE ST2084 (PQ)",
4279 [TRANSFER_FUNC_SMPTE_240] = "SMPTE 240M",
4280 [TRANSFER_FUNC_HLG] = N_("Hybrid Log-Gamma"),
4282 static_assert(ARRAY_SIZE(func_names) == TRANSFER_FUNC_MAX+1,
4283 "Transfer functions table mismatch");
4284 info_category_AddInfo( p_cat, _("Color transfer function"), "%s",
4285 vlc_gettext(func_names[fmt->video.transfer]) );
4287 if( fmt->video.space != COLOR_SPACE_UNDEF )
4289 static const char space_names[][16] = {
4290 [COLOR_SPACE_UNDEF] = N_("Undefined"),
4291 [COLOR_SPACE_BT601] = "ITU-R BT.601",
4292 [COLOR_SPACE_BT709] = "ITU-R BT.709",
4293 [COLOR_SPACE_BT2020] = "ITU-R BT.2020",
4295 static_assert(ARRAY_SIZE(space_names) == COLOR_SPACE_MAX+1,
4296 "Color space table mismatch");
4297 info_category_AddInfo( p_cat, _("Color space"), "%s",
4298 vlc_gettext(space_names[fmt->video.space]) );
4300 if( fmt->video.color_range != COLOR_RANGE_UNDEF )
4302 static const char range_names[][16] = {
4303 [COLOR_RANGE_UNDEF] = N_("Undefined"),
4304 [COLOR_RANGE_FULL] = N_("Full"),
4305 [COLOR_RANGE_LIMITED] = N_("Limited"),
4307 static_assert(ARRAY_SIZE(range_names) == COLOR_RANGE_MAX+1,
4308 "Color range table mismatch");
4309 info_category_AddInfo( p_cat, _("Color Range"), "%s",
4310 vlc_gettext(range_names[fmt->video.color_range]) );
4312 if( fmt->video.chroma_location != CHROMA_LOCATION_UNDEF )
4314 static const char c_loc_names[][16] = {
4315 [CHROMA_LOCATION_UNDEF] = N_("Undefined"),
4316 [CHROMA_LOCATION_LEFT] = N_("Left"),
4317 [CHROMA_LOCATION_CENTER] = N_("Center"),
4318 [CHROMA_LOCATION_TOP_LEFT] = N_("Top Left"),
4319 [CHROMA_LOCATION_TOP_CENTER] = N_("Top Center"),
4320 [CHROMA_LOCATION_BOTTOM_LEFT] =N_("Bottom Left"),
4321 [CHROMA_LOCATION_BOTTOM_CENTER] = N_("Bottom Center"),
4323 static_assert(ARRAY_SIZE(c_loc_names) == CHROMA_LOCATION_MAX+1,
4324 "Chroma location table mismatch");
4325 info_category_AddInfo( p_cat, _("Chroma location"), "%s",
4326 vlc_gettext(c_loc_names[fmt->video.chroma_location]) );
4328 if( fmt->video.multiview_mode != MULTIVIEW_2D )
4330 static const char c_multiview_names[][18] = {
4331 [MULTIVIEW_2D] = N_("2D"),
4332 [MULTIVIEW_STEREO_SBS] = N_("Side-By-Side"),
4333 [MULTIVIEW_STEREO_TB] = N_("Top-Bottom"),
4334 [MULTIVIEW_STEREO_ROW] = N_("Row Sequential"),
4335 [MULTIVIEW_STEREO_COL] = N_("Column Sequential"),
4336 [MULTIVIEW_STEREO_FRAME] =N_("Frame Sequential"),
4337 [MULTIVIEW_STEREO_CHECKERBOARD] = N_("Checkboard"),
4339 static_assert(ARRAY_SIZE(c_multiview_names) == MULTIVIEW_STEREO_MAX+1,
4340 "Multiview format table mismatch");
4341 info_category_AddInfo( p_cat, _("Stereo Mode"), "%s",
4342 vlc_gettext(c_multiview_names[fmt->video.multiview_mode]) );
4343 info_category_AddInfo( p_cat, _("First Stereo Eye"),
4344 vlc_gettext(fmt->video.b_multiview_right_eye_first ?
4345 N_("Right") : N_("Left")) );
4347 if( fmt->video.projection_mode != PROJECTION_MODE_RECTANGULAR )
4349 const char *psz_loc_name = NULL;
4350 switch (fmt->video.projection_mode)
4352 case PROJECTION_MODE_RECTANGULAR:
4353 psz_loc_name = N_("Rectangular");
4354 break;
4355 case PROJECTION_MODE_EQUIRECTANGULAR:
4356 psz_loc_name = N_("Equirectangular");
4357 break;
4358 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
4359 psz_loc_name = N_("Cubemap");
4360 break;
4361 default:
4362 vlc_assert_unreachable();
4363 break;
4365 info_category_AddInfo( p_cat, _("Projection"), "%s",
4366 vlc_gettext(psz_loc_name) );
4368 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Yaw"),
4369 "%.2f", fmt->video.pose.yaw );
4370 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Pitch"),
4371 "%.2f", fmt->video.pose.pitch );
4372 info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Roll"),
4373 "%.2f", fmt->video.pose.roll );
4374 info_category_AddInfo( p_cat,
4375 vlc_pgettext("ViewPoint", "Field of view"),
4376 "%.2f", fmt->video.pose.fov );
4378 if ( fmt->video.mastering.max_luminance )
4380 info_category_AddInfo( p_cat, _("Max. mastering luminance"), "%.4f cd/m²",
4381 fmt->video.mastering.max_luminance / 10000.f );
4383 if ( fmt->video.mastering.min_luminance )
4385 info_category_AddInfo( p_cat, _("Min. mastering luminance"), "%.4f cd/m²",
4386 fmt->video.mastering.min_luminance / 10000.f );
4388 if ( fmt->video.mastering.primaries[4] &&
4389 fmt->video.mastering.primaries[5] )
4391 float x = (float)fmt->video.mastering.primaries[4] / 50000.f;
4392 float y = (float)fmt->video.mastering.primaries[5] / 50000.f;
4393 info_category_AddInfo( p_cat, _("Mastering Primary R"), "x=%.4f y=%.4f", x, y );
4395 if ( fmt->video.mastering.primaries[0] &&
4396 fmt->video.mastering.primaries[1] )
4398 float x = (float)fmt->video.mastering.primaries[0] / 50000.f;
4399 float y = (float)fmt->video.mastering.primaries[1] / 50000.f;
4400 info_category_AddInfo( p_cat, _("Mastering Primary G"), "x=%.4f y=%.4f", x, y );
4402 if ( fmt->video.mastering.primaries[2] &&
4403 fmt->video.mastering.primaries[3] )
4405 float x = (float)fmt->video.mastering.primaries[2] / 50000.f;
4406 float y = (float)fmt->video.mastering.primaries[3] / 50000.f;
4407 info_category_AddInfo( p_cat, _("Mastering Primary B"), "x=%.4f y=%.4f", x, y );
4409 if ( fmt->video.mastering.white_point[0] &&
4410 fmt->video.mastering.white_point[1] )
4412 float x = (float)fmt->video.mastering.white_point[0] / 50000.f;
4413 float y = (float)fmt->video.mastering.white_point[1] / 50000.f;
4414 info_category_AddInfo( p_cat, _("Mastering White point"), "x=%.4f y=%.4f", x, y );
4416 if ( fmt->video.lighting.MaxCLL )
4418 info_category_AddInfo( p_cat, "MaxCLL", "%" PRIu16 " cd/m²",
4419 fmt->video.lighting.MaxCLL );
4421 if ( fmt->video.lighting.MaxFALL )
4423 info_category_AddInfo( p_cat, "MaxFALL", "%" PRIu16 " cd/m²",
4424 fmt->video.lighting.MaxFALL );
4426 break;
4428 case SPU_ES:
4429 info_category_AddInfo( p_cat, _("Type"), _("Subtitle") );
4430 break;
4432 default:
4433 break;
4436 /* Append generic meta */
4437 if( p_meta )
4439 char **ppsz_all_keys = vlc_meta_CopyExtraNames( p_meta );
4440 for( int i = 0; ppsz_all_keys && ppsz_all_keys[i]; i++ )
4442 char *psz_key = ppsz_all_keys[i];
4443 const char *psz_value = vlc_meta_GetExtra( p_meta, psz_key );
4445 if( psz_value )
4446 info_category_AddInfo( p_cat, vlc_gettext(psz_key), "%s",
4447 vlc_gettext(psz_value) );
4448 free( psz_key );
4450 free( ppsz_all_keys );
4452 /* */
4453 input_item_ReplaceInfos( p_item, p_cat );
4454 if( !input_priv(p_input)->b_preparsing )
4455 input_SendEventMetaInfo( p_input );
4458 static void EsOutDeleteInfoEs( es_out_t *out, es_out_id_t *es )
4460 es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
4461 input_thread_t *p_input = p_sys->p_input;
4462 input_item_t *p_item = input_priv(p_input)->p_item;
4463 char* psz_info_category;
4465 if( likely( psz_info_category = EsInfoCategoryName( es ) ) )
4467 int ret = input_item_DelInfo( p_item, psz_info_category, NULL );
4468 free( psz_info_category );
4470 if( ret == VLC_SUCCESS && !input_priv(p_input)->b_preparsing )
4471 input_SendEventMetaInfo( p_input );
4475 es_out_id_t *vlc_es_id_get_out(vlc_es_id_t *id)
4477 return container_of(id, es_out_id_t, id);
4480 vlc_es_id_t *
4481 vlc_es_id_Hold(vlc_es_id_t *id)
4483 EsHold(vlc_es_id_get_out(id));
4484 return id;
4487 void
4488 vlc_es_id_Release(vlc_es_id_t *id)
4490 EsRelease(vlc_es_id_get_out(id));
4494 vlc_es_id_GetInputId(vlc_es_id_t *id)
4496 return id->i_id;
4499 bool
4500 vlc_es_id_IsStrIdStable(vlc_es_id_t *id)
4502 return id->stable;
4505 const char *
4506 vlc_es_id_GetStrId(vlc_es_id_t *id)
4508 return id->str_id;
4511 enum es_format_category_e
4512 vlc_es_id_GetCat(vlc_es_id_t *id)
4514 return id->i_cat;
4517 const input_source_t *vlc_es_id_GetSource(vlc_es_id_t *id)
4519 return id->source;