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 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_common.h>
35 #include <vlc_es_out.h>
36 #include <vlc_block.h>
38 #include <vlc_fourcc.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"
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 /*****************************************************************************
62 *****************************************************************************/
66 input_source_t
*source
;
71 /* Number of es for this pgrm */
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
;
90 * Opaque structure representing an ES (Elementary Stream) track.
92 * This structure is propagated via the vlc_input_event_es event
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
;
109 /* weak reference, used by input_decoder_callbacks and vlc_clock_cbs */
113 es_out_pgrm_t
*p_pgrm
;
118 /* Channel in the track type */
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) */
127 char *psz_language_code
;
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 */
138 vlc_tick_t i_pts_level
;
141 /* Fields for Video with CC */
145 uint64_t i_bitmap
; /* channels bitmap */
146 es_out_id_t
*pp_es
[64]; /* a max of 64 chans for CEA708 */
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
;
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
;
174 input_thread_t
*p_input
;
176 input_source_t
*main_source
;
182 struct vlc_list programs
;
183 es_out_pgrm_t
*p_pgrm
; /* Master program */
185 enum vlc_clock_master_source clock_source
;
190 struct vlc_list es_slaves
; /* Dynamically created es on regular es selection */
196 es_out_es_props_t video
, audio
, sub
;
198 /* es/group to select */
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
;
214 vlc_tick_t i_pause_date
;
216 /* Current preroll */
217 vlc_tick_t i_preroll_end
;
219 /* Used for 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
;
226 sout_instance_t
*p_sout_record
;
228 /* Used only to limit debugging output */
229 int i_prev_stream_level
;
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
)
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
);
306 return VLC_CLOCK_MASTER_AUTO
;
309 static inline int EsOutGetClosedCaptionsChannel( const es_format_t
*p_fmt
)
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
;
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 )
326 decoder_on_vout_started(vlc_input_decoder_t
*decoder
, vout_thread_t
*vout
,
327 enum vlc_vout_order order
, void *userdata
)
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
);
338 struct vlc_input_event_vout event
= {
339 .action
= VLC_INPUT_EVENT_VOUT_STARTED
,
345 input_SendEventVout(p_sys
->p_input
, &event
);
349 decoder_on_vout_stopped(vlc_input_decoder_t
*decoder
, vout_thread_t
*vout
, void *userdata
)
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
);
360 struct vlc_input_event_vout event
= {
361 .action
= VLC_INPUT_EVENT_VOUT_STOPPED
,
363 .order
= VLC_VOUT_ORDER_NONE
,
367 input_SendEventVout(p_sys
->p_input
, &event
);
371 decoder_on_thumbnail_ready(vlc_input_decoder_t
*decoder
, picture_t
*pic
, void *userdata
)
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
);
382 struct vlc_input_event event
= {
383 .type
= INPUT_EVENT_THUMBNAIL_READY
,
387 input_SendEvent(p_sys
->p_input
, &event
);
391 decoder_on_new_video_stats(vlc_input_decoder_t
*decoder
, unsigned decoded
, unsigned lost
,
392 unsigned displayed
, unsigned late
, void *userdata
)
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
);
403 struct input_stats
*stats
= input_priv(p_sys
->p_input
)->stats
;
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
);
418 decoder_on_new_audio_stats(vlc_input_decoder_t
*decoder
, unsigned decoded
, unsigned lost
,
419 unsigned played
, void *userdata
)
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
);
430 struct input_stats
*stats
= input_priv(p_sys
->p_input
)->stats
;
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
);
443 decoder_get_attachments(vlc_input_decoder_t
*decoder
,
444 input_attachment_t
***ppp_attachment
,
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
);
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
)
476 return &p_sys
->audio
;
480 return &p_sys
->video
;
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
,
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
] );
527 static const struct es_out_callbacks es_out_cbs
;
529 /*****************************************************************************
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
) );
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
);
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;
567 p_sys
->b_buffering
= true;
568 p_sys
->i_preroll_end
= -1;
569 p_sys
->i_prev_stream_level
= -1;
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
;
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 )
599 title
= strdup( fmt
->psz_description
);
603 if( es
->psz_language
&& *es
->psz_language
)
605 if( asprintf( &title
, "%s %zu - [%s]", _("Track"),
606 es
->i_pos
, es
->psz_language
) == -1 )
611 if( asprintf( &title
, "%s %zu", _("Track"), es
->i_pos
) == -1 )
619 static void EsRelease(es_out_id_t
*es
)
621 if (vlc_atomic_rc_dec(&es
->rc
))
624 free(es
->psz_language
);
625 free(es
->psz_language_code
);
626 es_format_Clean(&es
->fmt
);
627 input_source_Release(es
->id
.source
);
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
);
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
);
658 vlc_meta_Delete( p_pgrm
->p_meta
);
659 input_source_Release( p_pgrm
->source
);
664 static void EsOutTerminate( es_out_t
*out
)
666 es_out_sys_t
*p_sys
= container_of(out
, es_out_sys_t
, out
);
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
);
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
;
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
||
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
);
724 foreach_es_then_es_slaves( es
)
725 if( es
->fmt
.i_cat
== cat
&& EsIsSelected( es
) )
730 static bool EsOutDecodersIsEmpty( es_out_t
*out
)
732 es_out_sys_t
*p_sys
= container_of(out
, es_out_sys_t
, out
);
735 if( p_sys
->b_buffering
&& p_sys
->p_pgrm
)
737 EsOutDecodersStopBuffering( out
, true );
738 if( p_sys
->b_buffering
)
742 foreach_es_then_es_slaves(es
)
744 if( es
->p_dec
&& !vlc_input_decoder_IsEmpty( es
->p_dec
) )
746 if( es
->p_dec_record
&& !vlc_input_decoder_IsEmpty( es
->p_dec_record
) )
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
);
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
);
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
;
791 assert( ( b_record
&& !p_sys
->p_sout_record
) || ( !b_record
&& p_sys
->p_sout_record
) );
795 char *psz_path
= var_CreateGetNonEmptyString( p_input
, "input-record-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
);
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
),
812 INPUT_RECORD_PREFIX
, NULL
);
815 char* psz_file_esc
= config_StringEscape( psz_file
);
818 if( asprintf( &psz_sout
, "#record{dst-prefix='%s'}", psz_file_esc
) < 0 )
820 free( psz_file_esc
);
831 p_sys
->p_sout_record
= sout_NewInstance( p_input
, psz_sout
);
835 if( !p_sys
->p_sout_record
)
838 vlc_list_foreach( p_es
, &p_sys
->es
, node
) /* Only master es */
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
);
855 vlc_list_foreach( p_es
, &p_sys
->es
, node
) /* Only master es */
857 if( !p_es
->p_dec_record
)
860 vlc_input_decoder_Delete( p_es
->p_dec_record
);
861 p_es
->p_dec_record
= NULL
;
864 sout_DeleteInstance( p_sys
->p_sout_record
);
866 p_sys
->p_sout_record
= NULL
;
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 */
878 EsOutDecodersChangePause( out
, true, i_date
);
879 EsOutProgramChangePause( out
, true, i_date
);
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
;
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
);
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
;
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
);
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
);
930 input_SendEventCache( p_sys
->p_input
, 0.0 );
932 foreach_es_then_es_slaves(p_es
)
934 if( p_es
->p_dec
!= NULL
)
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
;
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
);
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
))
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
+
995 p_sys
->i_buffering_extra_stream
- p_sys
->i_buffering_extra_initial
;
997 if( i_stream_duration
<= i_buffering_duration
&& !b_forced
)
1000 if (i_buffering_duration
== 0)
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
;
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 )
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
)
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
);
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
,
1056 input_clock_ChangeSystemOrigin( p_sys
->p_pgrm
->p_input_clock
, true, update
);
1058 foreach_es_then_es_slaves(p_es
)
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
);
1073 /* Pause decoders first */
1074 foreach_es_then_es_slaves(es
)
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
,
1084 static bool EsOutIsExtraBufferingAllowed( es_out_t
*out
)
1086 es_out_sys_t
*p_sys
= container_of(out
, es_out_sys_t
, out
);
1090 foreach_es_then_es_slaves(p_es
)
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 */
1103 const size_t i_level_high
= 10*1024*1024; /* 10 MiB */
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
);
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
;
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'" );
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 */ )
1170 msg_Warn( p_sys
->p_input
, "No video track selected, ignoring 'frame next'" );
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
;
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
);
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
)
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
) )
1234 if( p_sys
->b_buffering
&& p_sys
->i_buffering_extra_initial
<= 0 )
1236 i_delay
= i_stream_duration
;
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
;
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
;
1262 static void EsOutSendEsEvent(es_out_t
*out
, es_out_id_t
*es
, int action
,
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
) {
1271 .title
= es
->psz_title
? es
->psz_title
: "",
1272 .fmt
= es
->fmt_out
.i_cat
!= UNKNOWN_ES
? &es
->fmt_out
: &es
->fmt
,
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
,
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
;
1306 if( p_sys
->p_pgrm
== p_pgrm
)
1307 return; /* Nothing to do */
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
)
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 */
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
);
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,
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 */
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
1400 if( EsOutIsGroupSticky( out
, source
, i_group
) )
1403 es_out_pgrm_t
*p_pgrm
= malloc( sizeof( es_out_pgrm_t
) );
1408 p_pgrm
->source
= input_source_Hold( source
);
1409 p_pgrm
->i_id
= i_group
;
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
)
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
)
1431 msg_Dbg( p_input
, "The input can't pace, selecting the input (PCR) as the "
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
);
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
);
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);
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
);
1475 /* EsOutProgramSearch
1477 static es_out_pgrm_t
*EsOutProgramSearch( es_out_t
*p_out
, input_source_t
*source
,
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
)
1490 /* EsOutProgramInsert
1492 static es_out_pgrm_t
*EsOutProgramInsert( es_out_t
*p_out
, input_source_t
*source
,
1495 es_out_pgrm_t
*pgrm
= EsOutProgramSearch( p_out
, source
, i_group
);
1496 return pgrm
? pgrm
: EsOutProgramAdd( p_out
, source
, i_group
);
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
;
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 */
1520 foreach_es_then_es_slaves(es
)
1522 if (es
->p_pgrm
!= p_pgrm
)
1525 /* The remaining ES tracks are necessary sticky, cf. 'p_pgrm->i_es'
1527 assert(EsOutIsGroupSticky( out
, es
->id
.source
, es
->fmt
.i_group
));
1529 EsOutUnselectEs(out
, es
, true);
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
);
1547 /* EsOutProgramMeta:
1549 static char *EsOutProgramGetMetaName( es_out_pgrm_t
*p_pgrm
)
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 )
1560 if( asprintf( &psz
, "%s %d", _("Program"), p_pgrm
->i_id
) == -1 )
1566 static char *EsOutProgramGetProgramName( es_out_pgrm_t
*p_pgrm
)
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
) );
1575 if( asprintf( &psz
, "%s %d", _("Program"), p_pgrm
->i_id
) == -1 )
1581 static char *EsInfoCategoryName( es_out_id_t
* es
)
1585 if( asprintf( &psz_category
, _("Stream '%s'"), es
->id
.str_id
) == -1 )
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
;
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
) )
1615 EsOutGlobalMeta( out
, p_meta
);
1620 if( !EsOutIsProgramVisible( out
, source
, i_group
) )
1622 p_pgrm
= EsOutProgramInsert( out
, source
, i_group
);
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
);
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
)
1661 if( psz_provider
&& *psz_provider
)
1663 if( asprintf( &psz_text
, "%s [%s]", psz_title
, psz_provider
) < 0 )
1668 psz_text
= strdup( psz_title
);
1673 input_SendEventProgramUpdated( p_input
, i_group
, psz_text
);
1674 if( p_sys
->p_pgrm
== p_pgrm
)
1675 input_SendEventProgramSelect( p_input
, i_group
);
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
);
1688 p_cat
= info_category_New( psz_cat
);
1692 for( i
= 0; ppsz_all_keys
[i
]; i
++ )
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
);
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
);
1709 info_category_AddInfo( p_cat
, vlc_meta_TypeToLocalizedString(vlc_meta_Publisher
),
1710 "%s",psz_provider
);
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
;
1730 if( !EsOutIsProgramVisible( out
, source
, i_group
) )
1732 p_pgrm
= EsOutProgramInsert( out
, source
, i_group
);
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
;
1749 if( !EsOutIsProgramVisible( out
, source
, i_group
) )
1751 p_pgrm
= EsOutProgramInsert( out
, source
, i_group
);
1756 psz_cat
= EsOutProgramGetMetaName( p_pgrm
);
1757 msg_Dbg( p_input
, "EsOutProgramEpg: number=%d name=%s", i_group
, psz_cat
);
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
);
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
);
1806 if( psz_nowplaying
)
1807 ret
= input_item_AddInfo( p_item
, psz_cat
, now_playing_tr
,
1808 "%s", psz_nowplaying
);
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
);
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
;
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
)
1843 if( !p_pgrm
->b_scrambled
== !b_scrambled
)
1846 p_pgrm
->b_scrambled
= b_scrambled
;
1847 char *psz_cat
= EsOutProgramGetMetaName( p_pgrm
);
1851 ret
= input_item_AddInfo( p_item
, psz_cat
, _("Scrambled"), _("Yes") );
1853 ret
= input_item_DelInfo( p_item
, psz_cat
, _("Scrambled") );
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
);
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
);
1897 input_ExtractAttachmentAndCacheArt( p_input
, psz_arturl
+ 13 );
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
)
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
];
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 );
1977 fmt
->i_codec
= vlc_fourcc_GetCodec( fmt
->i_cat
, fmt
->i_codec
);
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
);
1995 vlc_memstream_puts( &ms
, p_master
->id
.str_id
);
1996 vlc_memstream_puts( &ms
, "/cc/" );
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;
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" );
2039 es_out_id_t
*es
= malloc( sizeof( *es
) );
2040 es_out_pgrm_t
*p_pgrm
;
2046 es
->id
.source
= input_source_Hold( source
);
2048 if( es_format_Copy( &es
->fmt
, fmt
) != VLC_SUCCESS
)
2055 if( es
->fmt
.i_id
< 0 )
2057 es
->fmt
.i_id
= input_source_GetNewAutoId( source
);
2063 if( !es
->fmt
.i_original_fourcc
)
2064 es
->fmt
.i_original_fourcc
= es
->fmt
.i_codec
;
2067 EsOutCreateStrId( es
, stable
, input_source_GetStrId(source
), p_master
);
2070 es_format_Clean( &es
->fmt
);
2071 input_source_Release( es
->id
.source
);
2076 if( !EsOutIsGroupSticky( out
, source
, fmt
->i_group
) )
2078 /* Search the program */
2079 p_pgrm
= EsOutProgramInsert( out
, source
, fmt
->i_group
);
2082 es_format_Clean( &es
->fmt
);
2083 input_source_Release( es
->id
.source
);
2088 /* Increase ref count for program */
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
);
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 */
2101 foreach_es_then_es_slaves(it
)
2102 if( it
->fmt
.i_cat
== fmt
->i_cat
&& it
->fmt
.i_group
== fmt
->i_group
)
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
)
2121 es
->i_channel
= p_sys
->audio
.i_count
++;
2125 es
->i_channel
= p_sys
->video
.i_count
++;
2129 es
->i_channel
= p_sys
->sub
.i_count
++;
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
);
2141 es
->p_dec_record
= NULL
;
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 );
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
);
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
);
2181 static bool EsIsSelected( es_out_id_t
*es
)
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
);
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
,
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
;
2230 case VLC_CLOCK_MASTER_MONOTONIC
:
2231 case VLC_CLOCK_MASTER_INPUT
:
2232 clock_source_cat
= UNKNOWN_ES
;
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
,
2249 p_es
->master
= false;
2250 p_es
->p_clock
= vlc_clock_main_CreateSlave( p_es
->p_pgrm
->p_main_clock
,
2255 if( !p_es
->p_clock
)
2257 p_es
->master
= false;
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
);
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
);
2288 vlc_clock_Delete( p_es
->p_clock
);
2289 p_es
->p_clock
= NULL
;
2293 EsOutDecoderChangeDelay( out
, p_es
);
2295 static void EsOutDestroyDecoder( es_out_t
*out
, es_out_id_t
*p_es
)
2302 assert( p_es
->p_pgrm
);
2304 vlc_input_decoder_Delete( p_es
->p_dec
);
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
);
2338 if( !es
->p_master
->p_dec
)
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
,
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. */
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",
2364 else if( es
->fmt
.i_cat
== AUDIO_ES
)
2367 || !var_GetBool( p_input
, b_sout
? "sout-audio" : "audio" ) )
2369 msg_Dbg( p_input
, "audio is disabled, not selecting ES 0x%x",
2374 if( es
->fmt
.i_cat
== SPU_ES
)
2377 || !var_GetBool( p_input
, b_sout
? "sout-spu" : "spu" ) )
2379 msg_Dbg( p_input
, "spu is disabled, not selecting ES 0x%x",
2386 EsOutCreateDecoder( out
, es
);
2388 if( es
->p_dec
== NULL
|| es
->p_pgrm
!= p_sys
->p_pgrm
)
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 */
2400 int vbi_page
= vlc_input_decoder_GetVbiPage( es
->p_dec
, &vbi_opaque
);
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
)
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 )
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
] )
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
,
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
);
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
,
2472 EsDeleteCCChannels( out
, es
);
2473 EsOutDestroyDecoder( out
, es
);
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
);
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
);
2511 for( const char *str_id
= strtok_r( str_ids
, ",", &saveptr
);
2513 str_id
= strtok_r( NULL
, ",", &saveptr
) )
2515 if( strcmp( str_id
, es
->id
.str_id
) == 0 )
2525 /* then channel index */
2526 if( p_esprops
->i_channel
>= 0 )
2528 return ( es
->i_channel
== p_esprops
->i_channel
);
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 ...
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
)
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
;
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" );
2584 for ( const char *prgm
= strtok_r( prgms
, ",", &buf
);
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
);
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
)
2605 if( EsOutSelectHasExplicitParams( p_esprops
) )
2607 if( !EsOutSelectMatchExplicitParams( p_esprops
, 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
) )
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
)
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
) )
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
)
2678 char *buffer
= malloc( strlen( esprops
->str_ids
) + 1);
2682 bool unselect_others
= false;
2684 foreach_es_then_es_slaves( other
)
2686 if( other
->fmt
.i_cat
!= cat
)
2689 bool select
= false;
2690 if( !unselect_others
)
2692 /* strtok_r will modify str_ids */
2693 strcpy( buffer
, esprops
->str_ids
);
2695 for( const char *str_id
= strtok_r( buffer
, ",", &saveptr
);
2697 str_id
= strtok_r( NULL
, ",", &saveptr
) )
2699 if( strcmp( other
->id
.str_id
, str_id
) == 0 )
2709 if( EsIsSelected( other
) )
2710 EsOutUnselectEs( out
, other
, other
->p_pgrm
== p_sys
->p_pgrm
);
2714 if( !EsIsSelected( other
) )
2715 EsOutSelectEs( out
, other
, true );
2716 if( esprops
->e_policy
== ES_OUT_ES_POLICY_EXCLUSIVE
)
2717 unselect_others
= true;
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
);
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
)
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
];
2745 else if( es_id
== &other
->id
)
2754 if( EsIsSelected( other
) )
2755 EsOutUnselectEs( out
, other
, other
->p_pgrm
== p_sys
->p_pgrm
);
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
)
2777 uint64_t i_existingbitmap
= parent
->cc
.i_bitmap
;
2778 for( int i
= 0; i_bitmap
> 0; i
++, i_bitmap
>>= 1, i_existingbitmap
>>= 1 )
2782 if( (i_bitmap
& 1) == 0 || (i_existingbitmap
& 1) )
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
);
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
;
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
;
2868 block_Release( p_block
);
2869 vlc_mutex_unlock( &p_sys
->lock
);
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
;
2888 if( es
->p_dec_record
)
2890 block_t
*p_dup
= block_Duplicate( p_block
);
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
);
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
);
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
) ))
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 /*****************************************************************************
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 */
2962 EsOutDrainDecoder( out
, es
);
2963 EsOutUnselectEs( out
, es
, es
->p_pgrm
== p_sys
->p_pgrm
);
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
);
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
);
2988 if( p_esprops
->p_main_es
== es
)
2991 p_esprops
->p_main_es
= NULL
;
2993 p_esprops
->i_count
--;
2996 /* Re-select another track when needed */
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
;
3011 EsOutSelect(out
, other
, false);
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
, ... )
3031 va_start( args
, i_query
);
3032 int ret
= EsOutVaControlLocked( out
, source
, i_query
, args
);
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
3048 foreach_es_then_es_slaves(es
)
3052 if (es
->delay
!= INT64_MAX
)
3053 tracks_delay
= __MIN(tracks_delay
, es
->delay
);
3054 else if (es
->fmt
.i_cat
== AUDIO_ES
)
3056 else if (es
->fmt
.i_cat
== SPU_ES
)
3061 tracks_delay
= __MIN(tracks_delay
, p_sys
->i_audio_delay
);
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 */
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
);
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
);
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
;
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
;
3129 else if( es
== es_cat
+ AUDIO_ES
)
3131 else if( es
== es_cat
+ VIDEO_ES
)
3133 else if( es
== es_cat
+ SPU_ES
)
3137 if (es
->b_terminated
)
3138 return VLC_EGENERIC
;
3142 foreach_es_then_es_slaves(other
)
3144 if( i_cat
== IGNORE_ES
)
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);
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
);
3173 EsOutUnselectEs(out
, other
, other
->p_pgrm
== p_sys
->p_pgrm
);
3178 EsOutStopFreeVout( out
);
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
)
3190 if (EsIsSelected(other
))
3192 EsOutUnselectEs(out
, other
, other
->p_pgrm
== p_sys
->p_pgrm
);
3193 EsOutStopFreeVout( out
);
3199 return VLC_EGENERIC
;
3202 case ES_OUT_SET_ES_DEFAULT
:
3204 es_out_id_t
*es
= va_arg( args
, es_out_id_t
* );
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;
3226 /*if( es->fmt.i_cat == VIDEO_ES )
3227 p_sys->i_default_video_id = es->fmt.i_id;
3229 if( es->fmt.i_cat == AUDIO_ES )
3230 p_sys->i_default_audio_id = es->fmt.i_id;
3232 if( es
->fmt
.i_cat
== SPU_ES
)
3233 p_sys
->sub
.i_demux_id
= es
->fmt
.i_id
;
3238 case ES_OUT_SET_PCR
:
3239 case ES_OUT_SET_GROUP_PCR
:
3241 es_out_pgrm_t
*p_pgrm
= NULL
;
3245 /* Search program */
3246 if( i_query
== ES_OUT_SET_PCR
)
3248 p_pgrm
= p_sys
->p_pgrm
;
3250 p_pgrm
= EsOutProgramAdd( out
, source
, i_group
); /* Create it */
3254 i_group
= va_arg( args
, int );
3255 p_pgrm
= EsOutProgramInsert( out
, source
, i_group
);
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
)
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
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
;
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
);
3349 case ES_OUT_RESET_PCR
:
3350 msg_Dbg( p_sys
->p_input
, "ES_OUT_RESET_PCR called" );
3351 EsOutChangePosition( out
, true );
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
);
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
)
3383 EsOutFillEsFmt( out
, &es
->fmt
);
3384 EsOutUpdateEsLanguageTitle(es
, &es
->fmt
);
3386 const bool b_was_selected
= EsIsSelected( es
);
3389 EsOutDrainDecoder( out
, es
);
3390 EsDeleteCCChannels( out
, es
);
3391 EsOutDestroyDecoder( out
, es
);
3395 EsOutCreateDecoder( out
, es
);
3397 EsOutSendEsEvent( out
, es
, VLC_INPUT_ES_UPDATED
, false );
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
);
3415 case ES_OUT_SET_NEXT_DISPLAY_TIME
:
3417 const int64_t i_date
= va_arg( args
, int64_t );
3420 return VLC_EGENERIC
;
3422 p_sys
->i_preroll_end
= i_date
;
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
);
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
);
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
);
3450 case ES_OUT_SET_EPG_TIME
:
3452 int64_t i64
= va_arg( args
, int64_t );
3454 EsOutEpgTime( out
, i64
);
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
);
3473 case ES_OUT_GET_EMPTY
:
3475 bool *pb
= va_arg( args
, bool* );
3476 *pb
= EsOutDecodersIsEmpty( out
);
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
;
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
);
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
;
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
);
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
);
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 * );
3531 vlc_input_decoder_SetVoutMouseEvent( p_es
->p_dec
,
3532 p_es
->mouse_event_cb
, p_es
->mouse_event_userdata
);
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
);
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
3587 foreach_es_then_es_slaves(p_es
)
3588 if( p_es
->fmt
.i_cat
== VIDEO_ES
&& !found
/* nested loop */ )
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 */
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
);
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
);
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
);
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
);
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
);
3662 case ES_OUT_PRIV_STOP_ALL_ES
:
3667 foreach_es_then_es_slaves(es
)
3670 vlc_es_id_t
**selected_es
= vlc_alloc(count
+ 1, sizeof(vlc_es_id_t
*));
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
;
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
);
3698 EsOutStopFreeVout( out
);
3701 case ES_OUT_PRIV_GET_BUFFERING
:
3703 bool *pb
= va_arg( args
, bool* );
3704 *pb
= p_sys
->b_buffering
;
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
);
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
);
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
);
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
);
3748 case ES_OUT_PRIV_SET_FRAME_NEXT
:
3749 EsOutFrameNext( out
);
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
)
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
);
3769 if( i_time
!= VLC_TICK_INVALID
)
3772 if( i_time
< VLC_TICK_0
)
3773 i_time
= VLC_TICK_0
;
3777 f_position
-= (double)i_delay
/ i_length
;
3778 if( 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
);
3787 input_SendEventTimes( p_sys
->p_input
, 0.0, VLC_TICK_INVALID
,
3788 i_normal_time
, i_length
);
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
;
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
,
3819 vlc_clock_main_SetInputDejitter(pgrm
->p_main_clock
, i_pts_delay
);
3824 case ES_OUT_PRIV_GET_GROUP_FORCED
:
3826 int *pi_group
= va_arg( args
, int * );
3827 *pi_group
= p_sys
->i_group_id
;
3830 case ES_OUT_PRIV_SET_EOS
:
3833 foreach_es_then_es_slaves(id
)
3834 if (id
->p_dec
!= NULL
)
3835 vlc_input_decoder_Drain(id
->p_dec
);
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
);
3845 return VLC_EGENERIC
;
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
);
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
);
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
);
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
);
3885 static int EsOutPrivControlLocked( es_out_t
*out
, int i_query
, ... )
3889 va_start( args
, i_query
);
3890 int ret
= EsOutVaPrivControlLocked( out
, i_query
, args
);
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
);
3906 static const struct es_out_callbacks es_out_cbs
=
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
);
3942 char *lang
= LanguageGetCode( psz_code
);
3943 pl
= GetLang_1( lang
);
3947 if( !strcmp( pl
->psz_iso639_1
, "??" ) )
3949 return strdup( psz_code
);
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
)
3984 if( psz_langs
== NULL
) return NULL
;
3986 psz_parser
= psz_dup
= strdup(psz_langs
);
3988 while( psz_parser
&& *psz_parser
)
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") );
4006 psz_code
= LanguageGetCode( psz_parser
);
4007 if( strcmp( psz_code
, "??" ) )
4009 TAB_APPEND( i_psz
, ppsz
, psz_code
);
4022 TAB_APPEND( i_psz
, ppsz
, NULL
);
4029 static int LanguageArrayIndex( char **ppsz_langs
, const char *psz_lang
)
4031 if( !ppsz_langs
|| !psz_lang
)
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") ) )
4039 if( !strcasecmp( ppsz_langs
[i
], "none" ) )
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
);
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 /****************************************************************************
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
) )
4119 info_category_t
* p_cat
= info_category_New( psz_cat
);
4123 if( unlikely( !p_cat
) )
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",
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
)
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
;
4157 i_orgbps
= aout_BitsPerSample( p_fmt_es
->i_codec
);
4159 info_category_AddInfo( p_cat
, _("Bits per sample"), "%u",
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
;
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",
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
] )
4199 const char *psz_name
;
4200 if( i
== AUDIO_REPLAY_GAIN_TRACK
)
4201 psz_name
= _("Track replay gain");
4203 psz_name
= _("Album replay gain");
4204 info_category_AddInfo( p_cat
, psz_name
, _("%.2f dB"),
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
);
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
,
4237 info_category_AddCodecInfo( p_cat
, _("Decoded format"),
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");
4355 case PROJECTION_MODE_EQUIRECTANGULAR
:
4356 psz_loc_name
= N_("Equirectangular");
4358 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
:
4359 psz_loc_name
= N_("Cubemap");
4362 vlc_assert_unreachable();
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
);
4429 info_category_AddInfo( p_cat
, _("Type"), _("Subtitle") );
4436 /* Append generic 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
);
4446 info_category_AddInfo( p_cat
, vlc_gettext(psz_key
), "%s",
4447 vlc_gettext(psz_value
) );
4450 free( ppsz_all_keys
);
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
);
4481 vlc_es_id_Hold(vlc_es_id_t
*id
)
4483 EsHold(vlc_es_id_get_out(id
));
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
)
4500 vlc_es_id_IsStrIdStable(vlc_es_id_t
*id
)
4506 vlc_es_id_GetStrId(vlc_es_id_t
*id
)
4511 enum es_format_category_e
4512 vlc_es_id_GetCat(vlc_es_id_t
*id
)
4517 const input_source_t
*vlc_es_id_GetSource(vlc_es_id_t
*id
)