codec: spudec: force osd start time for forced spu overlays
[vlc.git] / src / input / es_out.c
blob7095d8a4773ea8dd354f79d6c1231b0036ea5400
1 /*****************************************************************************
2 * es_out.c: Es Out handler for input.
3 *****************************************************************************
4 * Copyright (C) 2003-2004 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <stdio.h>
33 #include <assert.h>
34 #include <vlc_common.h>
36 #include <vlc_input.h>
37 #include <vlc_es_out.h>
38 #include <vlc_block.h>
39 #include <vlc_aout.h>
40 #include <vlc_fourcc.h>
41 #include <vlc_meta.h>
43 #include "input_internal.h"
44 #include "clock.h"
45 #include "decoder.h"
46 #include "es_out.h"
47 #include "event.h"
48 #include "info.h"
49 #include "item.h"
51 #include "../stream_output/stream_output.h"
53 #include <vlc_iso_lang.h>
54 /* FIXME we should find a better way than including that */
55 #include "../text/iso-639_def.h"
57 /*****************************************************************************
58 * Local prototypes
59 *****************************************************************************/
60 typedef struct
62 /* Program ID */
63 int i_id;
65 /* Number of es for this pgrm */
66 int i_es;
68 bool b_selected;
69 bool b_scrambled;
71 /* Clock for this program */
72 input_clock_t *p_clock;
74 vlc_meta_t *p_meta;
75 } es_out_pgrm_t;
77 struct es_out_id_t
79 /* ES ID */
80 int i_id;
81 es_out_pgrm_t *p_pgrm;
83 /* */
84 bool b_scrambled;
86 /* Channel in the track type */
87 int i_channel;
88 es_format_t fmt;
89 char *psz_language;
90 char *psz_language_code;
92 decoder_t *p_dec;
93 decoder_t *p_dec_record;
95 /* Fields for Video with CC */
96 bool pb_cc_present[4];
97 es_out_id_t *pp_cc_es[4];
99 /* Field for CC track from a master video */
100 es_out_id_t *p_master;
102 /* ID for the meta data */
103 int i_meta_id;
106 typedef struct
108 int i_count; /* es count */
109 es_out_id_t *p_main_es; /* current main es */
110 enum es_out_policy_e e_policy;
112 /* Parameters used for es selection */
113 bool b_autoselect; /* if we want to select an es when no user prefs */
114 int i_id; /* es id as set by es fmt.id */
115 int i_demux_id; /* same as previous, demuxer set default value */
116 int i_channel; /* es number in creation order */
117 char **ppsz_language;
118 } es_out_es_props_t;
120 struct es_out_sys_t
122 input_thread_t *p_input;
124 /* */
125 vlc_mutex_t lock;
127 /* all programs */
128 int i_pgrm;
129 es_out_pgrm_t **pgrm;
130 es_out_pgrm_t *p_pgrm; /* Master program */
132 /* all es */
133 int i_id;
134 int i_es;
135 es_out_id_t **es;
137 /* mode gestion */
138 bool b_active;
139 int i_mode;
141 es_out_es_props_t video, audio, sub;
143 /* es/group to select */
144 int i_group_id;
146 /* delay */
147 int64_t i_audio_delay;
148 int64_t i_spu_delay;
150 /* Clock configuration */
151 mtime_t i_pts_delay;
152 mtime_t i_pts_jitter;
153 int i_cr_average;
154 int i_rate;
156 /* */
157 bool b_paused;
158 mtime_t i_pause_date;
160 /* Current preroll */
161 mtime_t i_preroll_end;
163 /* Used for buffering */
164 bool b_buffering;
165 mtime_t i_buffering_extra_initial;
166 mtime_t i_buffering_extra_stream;
167 mtime_t i_buffering_extra_system;
169 /* Record */
170 sout_instance_t *p_sout_record;
172 /* Used only to limit debugging output */
173 int i_prev_stream_level;
176 static es_out_id_t *EsOutAdd ( es_out_t *, const es_format_t * );
177 static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * );
178 static void EsOutDel ( es_out_t *, es_out_id_t * );
179 static int EsOutControl( es_out_t *, int i_query, va_list );
180 static void EsOutDelete ( es_out_t * );
182 static void EsOutTerminate( es_out_t * );
183 static void EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force );
184 static void EsOutUpdateInfo( es_out_t *, es_out_id_t *es, const es_format_t *, const vlc_meta_t * );
185 static int EsOutSetRecord( es_out_t *, bool b_record );
187 static bool EsIsSelected( es_out_id_t *es );
188 static void EsSelect( es_out_t *out, es_out_id_t *es );
189 static void EsDeleteInfo( es_out_t *, es_out_id_t *es );
190 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
191 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
192 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
193 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
194 static void EsOutProgramsChangeRate( es_out_t *out );
195 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
196 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta );
197 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_progmeta );
199 static char *LanguageGetName( const char *psz_code );
200 static char *LanguageGetCode( const char *psz_lang );
201 static char **LanguageSplit( const char *psz_langs );
202 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang );
204 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
205 static char *EsInfoCategoryName( es_out_id_t* es );
207 static const vlc_fourcc_t EsOutFourccClosedCaptions[4] = {
208 VLC_CODEC_EIA608_1,
209 VLC_CODEC_EIA608_2,
210 VLC_CODEC_EIA608_3,
211 VLC_CODEC_EIA608_4,
213 static inline int EsOutGetClosedCaptionsChannel( vlc_fourcc_t fcc )
215 int i;
216 for( i = 0; i < 4; i++ )
218 if( fcc == EsOutFourccClosedCaptions[i] )
219 return i;
221 return -1;
223 static inline bool EsFmtIsTeletext( const es_format_t *p_fmt )
225 return p_fmt->i_cat == SPU_ES && p_fmt->i_codec == VLC_CODEC_TELETEXT;
228 /*****************************************************************************
229 * Es category specific structs
230 *****************************************************************************/
231 static es_out_es_props_t * GetPropsByCat( es_out_sys_t *p_sys, int i_cat )
233 switch( i_cat )
235 case AUDIO_ES:
236 return &p_sys->audio;
237 case SPU_ES:
238 return &p_sys->sub;
239 case VIDEO_ES:
240 return &p_sys->video;
242 return NULL;
245 static void EsOutPropsCleanup( es_out_es_props_t *p_props )
247 if( p_props->ppsz_language )
249 for( int i = 0; p_props->ppsz_language[i]; i++ )
250 free( p_props->ppsz_language[i] );
251 free( p_props->ppsz_language );
255 static void EsOutPropsInit( es_out_es_props_t *p_props,
256 bool autoselect,
257 input_thread_t *p_input,
258 enum es_out_policy_e e_default_policy,
259 const char *psz_trackidvar,
260 const char *psz_trackvar,
261 const char *psz_langvar,
262 const char *psz_debug )
264 p_props->e_policy = e_default_policy;
265 p_props->i_count = 0;
266 p_props->b_autoselect = autoselect;
267 p_props->i_id = (psz_trackidvar) ? var_GetInteger( p_input, psz_trackidvar ): -1;
268 p_props->i_channel = (psz_trackvar) ? var_GetInteger( p_input, psz_trackvar ): -1;
269 p_props->i_demux_id = -1;
270 p_props->p_main_es = NULL;
272 if( !input_priv(p_input)->b_preparsing && psz_langvar )
274 char *psz_string = var_GetString( p_input, psz_langvar );
275 p_props->ppsz_language = LanguageSplit( psz_string );
276 if( p_props->ppsz_language )
278 for( int i = 0; p_props->ppsz_language[i]; i++ )
279 msg_Dbg( p_input, "selected %s language[%d] %s",
280 psz_debug, i, p_props->ppsz_language[i] );
282 free( psz_string );
286 /*****************************************************************************
287 * input_EsOutNew:
288 *****************************************************************************/
289 es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
291 es_out_t *out = malloc( sizeof( *out ) );
292 if( !out )
293 return NULL;
295 es_out_sys_t *p_sys = calloc( 1, sizeof( *p_sys ) );
296 if( !p_sys )
298 free( out );
299 return NULL;
302 out->pf_add = EsOutAdd;
303 out->pf_send = EsOutSend;
304 out->pf_del = EsOutDel;
305 out->pf_control = EsOutControl;
306 out->pf_destroy = EsOutDelete;
307 out->p_sys = p_sys;
309 vlc_mutex_init_recursive( &p_sys->lock );
310 p_sys->p_input = p_input;
312 p_sys->b_active = false;
313 p_sys->i_mode = ES_OUT_MODE_NONE;
315 TAB_INIT( p_sys->i_pgrm, p_sys->pgrm );
317 TAB_INIT( p_sys->i_es, p_sys->es );
319 /* */
320 EsOutPropsInit( &p_sys->video, true, p_input, ES_OUT_ES_POLICY_SIMULTANEOUS,
321 NULL, NULL, NULL, NULL );
322 EsOutPropsInit( &p_sys->audio, true, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,
323 "audio-track-id", "audio-track", "audio-language", "audio" );
324 EsOutPropsInit( &p_sys->sub, false, p_input, ES_OUT_ES_POLICY_EXCLUSIVE,
325 "sub-track-id", "sub-track", "sub-language", "sub" );
327 p_sys->i_group_id = var_GetInteger( p_input, "program" );
329 p_sys->i_pause_date = -1;
331 p_sys->i_rate = i_rate;
333 p_sys->b_buffering = true;
334 p_sys->i_preroll_end = -1;
335 p_sys->i_prev_stream_level = -1;
337 return out;
340 /*****************************************************************************
342 *****************************************************************************/
343 static void EsOutDelete( es_out_t *out )
345 es_out_sys_t *p_sys = out->p_sys;
347 assert( !p_sys->i_es && !p_sys->i_pgrm && !p_sys->p_pgrm );
348 EsOutPropsCleanup( &p_sys->audio );
349 EsOutPropsCleanup( &p_sys->sub );
351 vlc_mutex_destroy( &p_sys->lock );
353 free( p_sys );
354 free( out );
357 static void EsOutTerminate( es_out_t *out )
359 es_out_sys_t *p_sys = out->p_sys;
361 if( p_sys->p_sout_record )
362 EsOutSetRecord( out, false );
364 for( int i = 0; i < p_sys->i_es; i++ )
366 if( p_sys->es[i]->p_dec )
367 input_DecoderDelete( p_sys->es[i]->p_dec );
369 free( p_sys->es[i]->psz_language );
370 free( p_sys->es[i]->psz_language_code );
371 es_format_Clean( &p_sys->es[i]->fmt );
373 free( p_sys->es[i] );
375 TAB_CLEAN( p_sys->i_es, p_sys->es );
377 /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
378 for( int i = 0; i < p_sys->i_pgrm; i++ )
380 es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
381 input_clock_Delete( p_pgrm->p_clock );
382 if( p_pgrm->p_meta )
383 vlc_meta_Delete( p_pgrm->p_meta );
385 free( p_pgrm );
387 TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
389 p_sys->p_pgrm = NULL;
391 input_item_SetEpgOffline( input_priv(p_sys->p_input)->p_item );
392 input_SendEventMetaEpg( p_sys->p_input );
395 static mtime_t EsOutGetWakeup( es_out_t *out )
397 es_out_sys_t *p_sys = out->p_sys;
398 input_thread_t *p_input = p_sys->p_input;
400 if( !p_sys->p_pgrm )
401 return 0;
403 /* We do not have a wake up date if the input cannot have its speed
404 * controlled or sout is imposing its own or while buffering
406 * FIXME for !input_priv(p_input)->b_can_pace_control a wake-up time is still needed
407 * to avoid too heavy buffering */
408 if( !input_priv(p_input)->b_can_pace_control ||
409 input_priv(p_input)->b_out_pace_control ||
410 p_sys->b_buffering )
411 return 0;
413 return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
416 static es_out_id_t *EsOutGetFromID( es_out_t *out, int i_id )
418 if( i_id < 0 )
420 /* Special HACK, -i_id is the cat of the stream */
421 return (es_out_id_t*)((uint8_t*)NULL-i_id);
424 for( int i = 0; i < out->p_sys->i_es; i++ )
426 if( out->p_sys->es[i]->i_id == i_id )
427 return out->p_sys->es[i];
429 return NULL;
432 static bool EsOutDecodersIsEmpty( es_out_t *out )
434 es_out_sys_t *p_sys = out->p_sys;
436 if( p_sys->b_buffering && p_sys->p_pgrm )
438 EsOutDecodersStopBuffering( out, true );
439 if( p_sys->b_buffering )
440 return true;
443 for( int i = 0; i < p_sys->i_es; i++ )
445 es_out_id_t *es = p_sys->es[i];
447 if( es->p_dec && !input_DecoderIsEmpty( es->p_dec ) )
448 return false;
449 if( es->p_dec_record && !input_DecoderIsEmpty( es->p_dec_record ) )
450 return false;
452 return true;
455 static void EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
457 es_out_sys_t *p_sys = out->p_sys;
459 if( i_cat == AUDIO_ES )
460 p_sys->i_audio_delay = i_delay;
461 else if( i_cat == SPU_ES )
462 p_sys->i_spu_delay = i_delay;
464 for( int i = 0; i < p_sys->i_es; i++ )
465 EsOutDecoderChangeDelay( out, p_sys->es[i] );
468 static int EsOutSetRecord( es_out_t *out, bool b_record )
470 es_out_sys_t *p_sys = out->p_sys;
471 input_thread_t *p_input = p_sys->p_input;
473 assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
475 if( b_record )
477 char *psz_path = var_CreateGetNonEmptyString( p_input, "input-record-path" );
478 if( !psz_path )
480 if( var_CountChoices( p_input, "video-es" ) )
481 psz_path = config_GetUserDir( VLC_VIDEOS_DIR );
482 else if( var_CountChoices( p_input, "audio-es" ) )
483 psz_path = config_GetUserDir( VLC_MUSIC_DIR );
484 else
485 psz_path = config_GetUserDir( VLC_DOWNLOAD_DIR );
488 char *psz_sout = NULL; // TODO conf
490 if( !psz_sout && psz_path )
492 char *psz_file = input_CreateFilename( p_input, psz_path, INPUT_RECORD_PREFIX, NULL );
493 if( psz_file )
495 if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file ) < 0 )
496 psz_sout = NULL;
497 free( psz_file );
500 free( psz_path );
502 if( !psz_sout )
503 return VLC_EGENERIC;
505 #ifdef ENABLE_SOUT
506 p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
507 #endif
508 free( psz_sout );
510 if( !p_sys->p_sout_record )
511 return VLC_EGENERIC;
513 for( int i = 0; i < p_sys->i_es; i++ )
515 es_out_id_t *p_es = p_sys->es[i];
517 if( !p_es->p_dec || p_es->p_master )
518 continue;
520 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
521 if( p_es->p_dec_record && p_sys->b_buffering )
522 input_DecoderStartWait( p_es->p_dec_record );
525 else
527 for( int i = 0; i < p_sys->i_es; i++ )
529 es_out_id_t *p_es = p_sys->es[i];
531 if( !p_es->p_dec_record )
532 continue;
534 input_DecoderDelete( p_es->p_dec_record );
535 p_es->p_dec_record = NULL;
537 #ifdef ENABLE_SOUT
538 sout_DeleteInstance( p_sys->p_sout_record );
539 #endif
540 p_sys->p_sout_record = NULL;
543 return VLC_SUCCESS;
545 static void EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
547 es_out_sys_t *p_sys = out->p_sys;
549 /* XXX the order is important */
550 if( b_paused )
552 EsOutDecodersChangePause( out, true, i_date );
553 EsOutProgramChangePause( out, true, i_date );
555 else
557 if( p_sys->i_buffering_extra_initial > 0 )
559 mtime_t i_stream_start;
560 mtime_t i_system_start;
561 mtime_t i_stream_duration;
562 mtime_t i_system_duration;
563 int i_ret;
564 i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
565 &i_stream_start, &i_system_start,
566 &i_stream_duration, &i_system_duration );
567 if( !i_ret )
569 /* FIXME pcr != exactly what wanted */
570 const mtime_t i_used = /*(i_stream_duration - input_priv(p_sys->p_input)->i_pts_delay)*/ p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
571 i_date -= i_used;
573 p_sys->i_buffering_extra_initial = 0;
574 p_sys->i_buffering_extra_stream = 0;
575 p_sys->i_buffering_extra_system = 0;
577 EsOutProgramChangePause( out, false, i_date );
578 EsOutDecodersChangePause( out, false, i_date );
580 EsOutProgramsChangeRate( out );
582 p_sys->b_paused = b_paused;
583 p_sys->i_pause_date = i_date;
586 static void EsOutChangeRate( es_out_t *out, int i_rate )
588 es_out_sys_t *p_sys = out->p_sys;
590 p_sys->i_rate = i_rate;
591 EsOutProgramsChangeRate( out );
594 static void EsOutChangePosition( es_out_t *out )
596 es_out_sys_t *p_sys = out->p_sys;
598 input_SendEventCache( p_sys->p_input, 0.0 );
600 for( int i = 0; i < p_sys->i_es; i++ )
602 es_out_id_t *p_es = p_sys->es[i];
604 if( p_es->p_dec != NULL )
606 input_DecoderFlush( p_es->p_dec );
607 if( !p_sys->b_buffering )
609 input_DecoderStartWait( p_es->p_dec );
610 if( p_es->p_dec_record != NULL )
611 input_DecoderStartWait( p_es->p_dec_record );
616 for( int i = 0; i < p_sys->i_pgrm; i++ )
617 input_clock_Reset( p_sys->pgrm[i]->p_clock );
619 p_sys->b_buffering = true;
620 p_sys->i_buffering_extra_initial = 0;
621 p_sys->i_buffering_extra_stream = 0;
622 p_sys->i_buffering_extra_system = 0;
623 p_sys->i_preroll_end = -1;
624 p_sys->i_prev_stream_level = -1;
629 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
631 es_out_sys_t *p_sys = out->p_sys;
633 mtime_t i_stream_start;
634 mtime_t i_system_start;
635 mtime_t i_stream_duration;
636 mtime_t i_system_duration;
637 if (input_clock_GetState( p_sys->p_pgrm->p_clock,
638 &i_stream_start, &i_system_start,
639 &i_stream_duration, &i_system_duration ))
640 return;
642 mtime_t i_preroll_duration = 0;
643 if( p_sys->i_preroll_end >= 0 )
644 i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
646 const mtime_t i_buffering_duration = p_sys->i_pts_delay +
647 i_preroll_duration +
648 p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
650 if( i_stream_duration <= i_buffering_duration && !b_forced )
652 double f_level;
653 if (i_buffering_duration == 0)
654 f_level = 0;
655 else
656 f_level = __MAX( (double)i_stream_duration / i_buffering_duration, 0 );
657 input_SendEventCache( p_sys->p_input, f_level );
659 int i_level = (int)(100 * f_level);
660 if( p_sys->i_prev_stream_level != i_level )
662 msg_Dbg( p_sys->p_input, "Buffering %d%%", i_level );
663 p_sys->i_prev_stream_level = i_level;
666 return;
668 input_SendEventCache( p_sys->p_input, 1.0 );
670 msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
671 (int)(i_stream_duration/1000), (int)(i_system_duration/1000) );
672 p_sys->b_buffering = false;
673 p_sys->i_preroll_end = -1;
674 p_sys->i_prev_stream_level = -1;
676 if( p_sys->i_buffering_extra_initial > 0 )
678 /* FIXME wrong ? */
679 return;
682 const mtime_t i_decoder_buffering_start = mdate();
683 for( int i = 0; i < p_sys->i_es; i++ )
685 es_out_id_t *p_es = p_sys->es[i];
687 if( !p_es->p_dec || p_es->fmt.i_cat == SPU_ES )
688 continue;
689 input_DecoderWait( p_es->p_dec );
690 if( p_es->p_dec_record )
691 input_DecoderWait( p_es->p_dec_record );
694 msg_Dbg( p_sys->p_input, "Decoder wait done in %d ms",
695 (int)(mdate() - i_decoder_buffering_start)/1000 );
697 /* Here is a good place to destroy unused vout with every demuxer */
698 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
700 /* */
701 const mtime_t i_wakeup_delay = 10*1000; /* FIXME CLEANUP thread wake up time*/
702 const mtime_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : mdate();
704 input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, true,
705 i_current_date + i_wakeup_delay - i_buffering_duration );
707 for( int i = 0; i < p_sys->i_es; i++ )
709 es_out_id_t *p_es = p_sys->es[i];
711 if( !p_es->p_dec )
712 continue;
714 input_DecoderStopWait( p_es->p_dec );
715 if( p_es->p_dec_record )
716 input_DecoderStopWait( p_es->p_dec_record );
719 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
721 es_out_sys_t *p_sys = out->p_sys;
723 /* Pause decoders first */
724 for( int i = 0; i < p_sys->i_es; i++ )
726 es_out_id_t *es = p_sys->es[i];
728 if( es->p_dec )
730 input_DecoderChangePause( es->p_dec, b_paused, i_date );
731 if( es->p_dec_record )
732 input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
737 static bool EsOutIsExtraBufferingAllowed( es_out_t *out )
739 es_out_sys_t *p_sys = out->p_sys;
741 size_t i_size = 0;
742 for( int i = 0; i < p_sys->i_es; i++ )
744 es_out_id_t *p_es = p_sys->es[i];
746 if( p_es->p_dec )
747 i_size += input_DecoderGetFifoSize( p_es->p_dec );
748 if( p_es->p_dec_record )
749 i_size += input_DecoderGetFifoSize( p_es->p_dec_record );
751 //msg_Info( out, "----- EsOutIsExtraBufferingAllowed =% 5d KiB -- ", i_size / 1024 );
753 /* TODO maybe we want to be able to tune it ? */
754 #if defined(OPTIMIZE_MEMORY)
755 const size_t i_level_high = 512*1024; /* 0.5 MiB */
756 #else
757 const size_t i_level_high = 10*1024*1024; /* 10 MiB */
758 #endif
759 return i_size < i_level_high;
762 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
764 es_out_sys_t *p_sys = out->p_sys;
766 for( int i = 0; i < p_sys->i_pgrm; i++ )
767 input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date );
770 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
772 es_out_sys_t *p_sys = out->p_sys;
774 mtime_t i_delay = 0;
775 if( p_es->fmt.i_cat == AUDIO_ES )
776 i_delay = p_sys->i_audio_delay;
777 else if( p_es->fmt.i_cat == SPU_ES )
778 i_delay = p_sys->i_spu_delay;
779 else
780 return;
782 if( p_es->p_dec )
783 input_DecoderChangeDelay( p_es->p_dec, i_delay );
784 if( p_es->p_dec_record )
785 input_DecoderChangeDelay( p_es->p_dec_record, i_delay );
787 static void EsOutProgramsChangeRate( es_out_t *out )
789 es_out_sys_t *p_sys = out->p_sys;
791 for( int i = 0; i < p_sys->i_pgrm; i++ )
792 input_clock_ChangeRate( p_sys->pgrm[i]->p_clock, p_sys->i_rate );
795 static void EsOutFrameNext( es_out_t *out )
797 es_out_sys_t *p_sys = out->p_sys;
798 es_out_id_t *p_es_video = NULL;
800 if( p_sys->b_buffering )
802 msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" );
803 return;
806 assert( p_sys->b_paused );
808 for( int i = 0; i < p_sys->i_es; i++ )
810 es_out_id_t *p_es = p_sys->es[i];
812 if( p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
814 p_es_video = p_es;
815 break;
819 if( !p_es_video )
821 msg_Warn( p_sys->p_input, "No video track selected, ignoring 'frame next'" );
822 return;
825 mtime_t i_duration;
826 input_DecoderFrameNext( p_es_video->p_dec, &i_duration );
828 msg_Dbg( out->p_sys->p_input, "EsOutFrameNext consummed %d ms", (int)(i_duration/1000) );
830 if( i_duration <= 0 )
831 i_duration = 40*1000;
833 /* FIXME it is not a clean way ? */
834 if( p_sys->i_buffering_extra_initial <= 0 )
836 mtime_t i_stream_start;
837 mtime_t i_system_start;
838 mtime_t i_stream_duration;
839 mtime_t i_system_duration;
840 int i_ret;
842 i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
843 &i_stream_start, &i_system_start,
844 &i_stream_duration, &i_system_duration );
845 if( i_ret )
846 return;
848 p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->i_pts_delay; /* FIXME < 0 ? */
849 p_sys->i_buffering_extra_system =
850 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
853 const int i_rate = input_clock_GetRate( p_sys->p_pgrm->p_clock );
855 p_sys->b_buffering = true;
856 p_sys->i_buffering_extra_system += i_duration;
857 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial +
858 ( p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial ) *
859 INPUT_RATE_DEFAULT / i_rate;
861 p_sys->i_preroll_end = -1;
862 p_sys->i_prev_stream_level = -1;
864 static mtime_t EsOutGetBuffering( es_out_t *out )
866 es_out_sys_t *p_sys = out->p_sys;
867 mtime_t i_stream_duration, i_system_start;
869 if( !p_sys->p_pgrm )
870 return 0;
871 else
873 mtime_t i_stream_start, i_system_duration;
875 if( input_clock_GetState( p_sys->p_pgrm->p_clock,
876 &i_stream_start, &i_system_start,
877 &i_stream_duration, &i_system_duration ) )
878 return 0;
881 mtime_t i_delay;
883 if( p_sys->b_buffering && p_sys->i_buffering_extra_initial <= 0 )
885 i_delay = i_stream_duration;
887 else
889 mtime_t i_system_duration;
891 if( p_sys->b_paused )
893 i_system_duration = p_sys->i_pause_date - i_system_start;
894 if( p_sys->i_buffering_extra_initial > 0 )
895 i_system_duration += p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
897 else
899 i_system_duration = mdate() - i_system_start;
902 const mtime_t i_consumed = i_system_duration * INPUT_RATE_DEFAULT / p_sys->i_rate - i_stream_duration;
903 i_delay = p_sys->i_pts_delay - i_consumed;
905 if( i_delay < 0 )
906 return 0;
907 return i_delay;
910 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id,
911 const es_format_t *fmt, const char *psz_language,
912 bool b_delete )
914 es_out_sys_t *p_sys = out->p_sys;
915 input_thread_t *p_input = p_sys->p_input;
916 vlc_value_t val, text;
918 if( b_delete )
920 if( EsFmtIsTeletext( fmt ) )
921 input_SendEventTeletextDel( p_sys->p_input, i_id );
923 input_SendEventEsDel( p_input, fmt->i_cat, i_id );
924 return;
927 /* Get the number of ES already added */
928 const char *psz_var;
929 if( fmt->i_cat == AUDIO_ES )
930 psz_var = "audio-es";
931 else if( fmt->i_cat == VIDEO_ES )
932 psz_var = "video-es";
933 else
934 psz_var = "spu-es";
936 var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
937 if( val.i_int == 0 )
939 vlc_value_t val2;
941 /* First one, we need to add the "Disable" choice */
942 val2.i_int = -1; text.psz_string = _("Disable");
943 var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
944 val.i_int++;
947 /* Take care of the ES description */
948 if( fmt->psz_description && *fmt->psz_description )
950 if( psz_language && *psz_language )
952 if( asprintf( &text.psz_string, "%s - [%s]", fmt->psz_description,
953 psz_language ) == -1 )
954 text.psz_string = NULL;
956 else text.psz_string = strdup( fmt->psz_description );
958 else
960 if( psz_language && *psz_language )
962 if( asprintf( &text.psz_string, "%s %"PRId64" - [%s]", _( "Track" ), val.i_int, psz_language ) == -1 )
963 text.psz_string = NULL;
965 else
967 if( asprintf( &text.psz_string, "%s %"PRId64, _( "Track" ), val.i_int ) == -1 )
968 text.psz_string = NULL;
972 input_SendEventEsAdd( p_input, fmt->i_cat, i_id, text.psz_string );
973 if( EsFmtIsTeletext( fmt ) )
975 char psz_page[3+1];
976 snprintf( psz_page, sizeof(psz_page), "%d%2.2x",
977 fmt->subs.teletext.i_magazine,
978 fmt->subs.teletext.i_page );
979 input_SendEventTeletextAdd( p_sys->p_input,
980 i_id, fmt->subs.teletext.i_magazine >= 0 ? psz_page : NULL );
983 free( text.psz_string );
986 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
987 bool b_delete )
989 EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete );
992 static bool EsOutIsProgramVisible( es_out_t *out, int i_group )
994 return out->p_sys->i_group_id == 0 || out->p_sys->i_group_id == i_group;
997 /* EsOutProgramSelect:
998 * Select a program and update the object variable
1000 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
1002 es_out_sys_t *p_sys = out->p_sys;
1003 input_thread_t *p_input = p_sys->p_input;
1004 int i;
1006 if( p_sys->p_pgrm == p_pgrm )
1007 return; /* Nothing to do */
1009 if( p_sys->p_pgrm )
1011 es_out_pgrm_t *old = p_sys->p_pgrm;
1012 msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
1014 for( i = 0; i < p_sys->i_es; i++ )
1016 if( p_sys->es[i]->p_pgrm == old && EsIsSelected( p_sys->es[i] ) &&
1017 p_sys->i_mode != ES_OUT_MODE_ALL )
1018 EsUnselect( out, p_sys->es[i], true );
1021 p_sys->audio.p_main_es = NULL;
1022 p_sys->video.p_main_es = NULL;
1023 p_sys->sub.p_main_es = NULL;
1026 msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
1028 /* Mark it selected */
1029 p_pgrm->b_selected = true;
1031 /* Switch master stream */
1032 p_sys->p_pgrm = p_pgrm;
1034 /* Update "program" */
1035 input_SendEventProgramSelect( p_input, p_pgrm->i_id );
1037 /* Update "es-*" */
1038 input_SendEventEsDel( p_input, AUDIO_ES, -1 );
1039 input_SendEventEsDel( p_input, VIDEO_ES, -1 );
1040 input_SendEventEsDel( p_input, SPU_ES, -1 );
1041 input_SendEventTeletextDel( p_input, -1 );
1042 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, p_pgrm->b_scrambled );
1044 /* TODO event */
1045 var_SetInteger( p_input, "teletext-es", -1 );
1047 for( i = 0; i < p_sys->i_es; i++ )
1049 if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
1051 EsOutESVarUpdate( out, p_sys->es[i], false );
1052 EsOutUpdateInfo( out, p_sys->es[i], &p_sys->es[i]->fmt, NULL );
1055 EsOutSelect( out, p_sys->es[i], false );
1058 /* Ensure the correct running EPG table is selected */
1059 input_item_ChangeEPGSource( input_priv(p_input)->p_item, p_pgrm->i_id );
1061 /* Update now playing */
1062 if( p_pgrm->p_meta )
1064 input_item_SetESNowPlaying( input_priv(p_input)->p_item,
1065 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) );
1066 input_item_SetPublisher( input_priv(p_input)->p_item,
1067 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Publisher ) );
1068 input_item_SetTitle( input_priv(p_input)->p_item,
1069 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1070 input_SendEventMeta( p_input );
1071 /* FIXME: we probably want to replace every input meta */
1075 /* EsOutAddProgram:
1076 * Add a program
1078 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
1080 es_out_sys_t *p_sys = out->p_sys;
1081 input_thread_t *p_input = p_sys->p_input;
1083 es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
1084 if( !p_pgrm )
1085 return NULL;
1087 /* Init */
1088 p_pgrm->i_id = i_group;
1089 p_pgrm->i_es = 0;
1090 p_pgrm->b_selected = false;
1091 p_pgrm->b_scrambled = false;
1092 p_pgrm->p_meta = NULL;
1093 p_pgrm->p_clock = input_clock_New( p_sys->i_rate );
1094 if( !p_pgrm->p_clock )
1096 free( p_pgrm );
1097 return NULL;
1099 if( p_sys->b_paused )
1100 input_clock_ChangePause( p_pgrm->p_clock, p_sys->b_paused, p_sys->i_pause_date );
1101 input_clock_SetJitter( p_pgrm->p_clock, p_sys->i_pts_delay, p_sys->i_cr_average );
1103 /* Append it */
1104 TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
1106 /* Update "program" variable */
1107 if( EsOutIsProgramVisible( out, i_group ) )
1108 input_SendEventProgramAdd( p_input, i_group, NULL );
1110 if( i_group == p_sys->i_group_id || ( !p_sys->p_pgrm && p_sys->i_group_id == 0 ) )
1111 EsOutProgramSelect( out, p_pgrm );
1113 return p_pgrm;
1116 /* EsOutDelProgram:
1117 * Delete a program
1119 static int EsOutProgramDel( es_out_t *out, int i_group )
1121 es_out_sys_t *p_sys = out->p_sys;
1122 input_thread_t *p_input = p_sys->p_input;
1123 es_out_pgrm_t *p_pgrm = NULL;
1124 int i;
1126 for( i = 0; i < p_sys->i_pgrm; i++ )
1128 if( p_sys->pgrm[i]->i_id == i_group )
1130 p_pgrm = p_sys->pgrm[i];
1131 break;
1135 if( p_pgrm == NULL )
1136 return VLC_EGENERIC;
1138 if( p_pgrm->i_es )
1140 msg_Dbg( p_input, "can't delete program %d which still has %i ES",
1141 i_group, p_pgrm->i_es );
1142 return VLC_EGENERIC;
1145 TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
1147 /* If program is selected we need to unselect it */
1148 if( p_sys->p_pgrm == p_pgrm )
1149 p_sys->p_pgrm = NULL;
1151 input_clock_Delete( p_pgrm->p_clock );
1153 if( p_pgrm->p_meta )
1154 vlc_meta_Delete( p_pgrm->p_meta );
1155 free( p_pgrm );
1157 /* Update "program" variable */
1158 input_SendEventProgramDel( p_input, i_group );
1160 return VLC_SUCCESS;
1163 /* EsOutProgramFind
1165 static es_out_pgrm_t *EsOutProgramFind( es_out_t *p_out, int i_group )
1167 es_out_sys_t *p_sys = p_out->p_sys;
1169 for( int i = 0; i < p_sys->i_pgrm; i++ )
1171 if( p_sys->pgrm[i]->i_id == i_group )
1172 return p_sys->pgrm[i];
1174 return EsOutProgramAdd( p_out, i_group );
1177 /* EsOutProgramMeta:
1179 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
1181 char *psz = NULL;
1182 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1184 if( asprintf( &psz, _("%s [%s %d]"), vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ),
1185 _("Program"), p_pgrm->i_id ) == -1 )
1186 return NULL;
1188 else
1190 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1191 return NULL;
1193 return psz;
1196 static char *EsOutProgramGetProgramName( es_out_pgrm_t *p_pgrm )
1198 char *psz = NULL;
1199 if( p_pgrm->p_meta && vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) )
1201 return strdup( vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title ) );
1203 else
1205 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1206 return NULL;
1208 return psz;
1211 static char *EsInfoCategoryName( es_out_id_t* es )
1213 char *psz_category;
1215 if( asprintf( &psz_category, _("Stream %d"), es->i_meta_id ) == -1 )
1216 return NULL;
1218 return psz_category;
1221 static void EsOutProgramMeta( es_out_t *out, int i_group, const vlc_meta_t *p_meta )
1223 es_out_sys_t *p_sys = out->p_sys;
1224 es_out_pgrm_t *p_pgrm;
1225 input_thread_t *p_input = p_sys->p_input;
1226 const char *psz_title = NULL;
1227 const char *psz_provider = NULL;
1228 int i;
1230 msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
1232 /* Check against empty meta data (empty for what we handle) */
1233 if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
1234 !vlc_meta_Get( p_meta, vlc_meta_ESNowPlaying) &&
1235 !vlc_meta_Get( p_meta, vlc_meta_Publisher) )
1237 return;
1240 if( i_group < 0 )
1242 EsOutGlobalMeta( out, p_meta );
1243 return;
1246 /* Find program */
1247 if( !EsOutIsProgramVisible( out, i_group ) )
1248 return;
1249 p_pgrm = EsOutProgramFind( out, i_group );
1250 if( !p_pgrm )
1251 return;
1253 if( p_pgrm->p_meta )
1255 const char *psz_current_title = vlc_meta_Get( p_pgrm->p_meta, vlc_meta_Title );
1256 const char *psz_new_title = vlc_meta_Get( p_meta, vlc_meta_Title );
1257 if( !psz_current_title != !psz_new_title ||
1258 ( psz_new_title && psz_new_title && strcmp(psz_new_title, psz_current_title)) )
1260 /* Remove old entries */
1261 char *psz_oldinfokey = EsOutProgramGetMetaName( p_pgrm );
1262 input_Control( p_input, INPUT_DEL_INFO, psz_oldinfokey, NULL );
1263 /* TODO update epg name ?
1264 * TODO update scrambled info name ? */
1265 free( psz_oldinfokey );
1267 vlc_meta_Delete( p_pgrm->p_meta );
1269 p_pgrm->p_meta = vlc_meta_New();
1270 if( p_pgrm->p_meta )
1271 vlc_meta_Merge( p_pgrm->p_meta, p_meta );
1273 if( p_sys->p_pgrm == p_pgrm )
1275 EsOutMeta( out, NULL, p_meta );
1277 /* */
1278 psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
1279 psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
1281 /* Update the description text of the program */
1282 if( psz_title && *psz_title )
1284 char *psz_text;
1285 if( psz_provider && *psz_provider )
1287 if( asprintf( &psz_text, "%s [%s]", psz_title, psz_provider ) < 0 )
1288 psz_text = NULL;
1290 else
1292 psz_text = strdup( psz_title );
1295 /* ugly but it works */
1296 if( psz_text )
1298 input_SendEventProgramDel( p_input, i_group );
1299 input_SendEventProgramAdd( p_input, i_group, psz_text );
1300 if( p_sys->p_pgrm == p_pgrm )
1301 input_SendEventProgramSelect( p_input, i_group );
1302 free( psz_text );
1306 /* */
1307 char **ppsz_all_keys = vlc_meta_CopyExtraNames(p_meta );
1309 info_category_t *p_cat = NULL;
1310 if( psz_provider || ( ppsz_all_keys[0] && *ppsz_all_keys[0] ) )
1312 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1313 if( psz_cat )
1314 p_cat = info_category_New( psz_cat );
1315 free( psz_cat );
1318 for( i = 0; ppsz_all_keys[i]; i++ )
1320 if( p_cat )
1321 info_category_AddInfo( p_cat, vlc_gettext(ppsz_all_keys[i]), "%s",
1322 vlc_meta_GetExtra( p_meta, ppsz_all_keys[i] ) );
1323 free( ppsz_all_keys[i] );
1325 free( ppsz_all_keys );
1327 if( psz_provider )
1329 if( p_sys->p_pgrm == p_pgrm )
1331 input_item_SetPublisher( input_priv(p_input)->p_item, psz_provider );
1332 input_SendEventMeta( p_input );
1334 if( p_cat )
1335 info_category_AddInfo( p_cat, vlc_meta_TypeToLocalizedString(vlc_meta_Publisher),
1336 "%s",psz_provider );
1338 if( p_cat )
1339 input_Control( p_input, INPUT_MERGE_INFOS, p_cat );
1342 static void EsOutProgramEpgEvent( es_out_t *out, int i_group, const vlc_epg_event_t *p_event )
1344 es_out_sys_t *p_sys = out->p_sys;
1345 input_thread_t *p_input = p_sys->p_input;
1346 input_item_t *p_item = input_priv(p_input)->p_item;
1347 es_out_pgrm_t *p_pgrm;
1349 /* Find program */
1350 if( !EsOutIsProgramVisible( out, i_group ) )
1351 return;
1352 p_pgrm = EsOutProgramFind( out, i_group );
1353 if( !p_pgrm )
1354 return;
1356 input_item_SetEpgEvent( p_item, p_event );
1359 static void EsOutProgramEpg( es_out_t *out, int i_group, const vlc_epg_t *p_epg )
1361 es_out_sys_t *p_sys = out->p_sys;
1362 input_thread_t *p_input = p_sys->p_input;
1363 input_item_t *p_item = input_priv(p_input)->p_item;
1364 es_out_pgrm_t *p_pgrm;
1365 char *psz_cat;
1367 /* Find program */
1368 if( !EsOutIsProgramVisible( out, i_group ) )
1369 return;
1370 p_pgrm = EsOutProgramFind( out, i_group );
1371 if( !p_pgrm )
1372 return;
1374 /* Update info */
1375 psz_cat = EsOutProgramGetMetaName( p_pgrm );
1376 msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, psz_cat );
1378 /* Merge EPG */
1379 vlc_epg_t epg;
1381 epg = *p_epg;
1382 epg.psz_name = EsOutProgramGetProgramName( p_pgrm );
1384 input_item_SetEpg( p_item, &epg, p_sys->p_pgrm && (p_epg->i_source_id == p_sys->p_pgrm->i_id) );
1385 input_SendEventMetaEpg( p_sys->p_input );
1387 free( epg.psz_name );
1389 /* Update now playing */
1390 if( p_epg->b_present && p_pgrm->p_meta &&
1391 ( p_epg->p_current || p_epg->i_event == 0 ) )
1393 vlc_meta_SetNowPlaying( p_pgrm->p_meta, NULL );
1396 vlc_mutex_lock( &p_item->lock );
1397 for( int i = 0; i < p_item->i_epg; i++ )
1399 const vlc_epg_t *p_tmp = p_item->pp_epg[i];
1401 if( p_tmp->b_present && p_tmp->i_source_id == p_pgrm->i_id )
1403 const char *psz_name = ( p_tmp->p_current ) ? p_tmp->p_current->psz_name : NULL;
1404 if( !p_pgrm->p_meta )
1405 p_pgrm->p_meta = vlc_meta_New();
1406 if( p_pgrm->p_meta )
1407 vlc_meta_Set( p_pgrm->p_meta, vlc_meta_ESNowPlaying, psz_name );
1408 break;
1411 vlc_mutex_unlock( &p_item->lock );
1413 /* Update selected program input info */
1414 if( p_pgrm == p_sys->p_pgrm )
1416 const char *psz_nowplaying = p_pgrm->p_meta ?
1417 vlc_meta_Get( p_pgrm->p_meta, vlc_meta_ESNowPlaying ) : NULL;
1419 input_item_SetESNowPlaying( input_priv(p_input)->p_item, psz_nowplaying );
1420 input_SendEventMeta( p_input );
1422 if( psz_nowplaying )
1424 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1425 vlc_meta_TypeToLocalizedString(vlc_meta_ESNowPlaying), "%s", psz_nowplaying );
1427 else
1429 input_Control( p_input, INPUT_DEL_INFO, psz_cat,
1430 vlc_meta_TypeToLocalizedString(vlc_meta_ESNowPlaying) );
1434 free( psz_cat );
1437 static void EsOutEpgTime( es_out_t *out, int64_t time )
1439 es_out_sys_t *p_sys = out->p_sys;
1440 input_thread_t *p_input = p_sys->p_input;
1441 input_item_t *p_item = input_priv(p_input)->p_item;
1443 input_item_SetEpgTime( p_item, time );
1446 static void EsOutProgramUpdateScrambled( es_out_t *p_out, es_out_pgrm_t *p_pgrm )
1448 es_out_sys_t *p_sys = p_out->p_sys;
1449 input_thread_t *p_input = p_sys->p_input;
1450 bool b_scrambled = false;
1452 for( int i = 0; i < p_sys->i_es; i++ )
1454 if( p_sys->es[i]->p_pgrm == p_pgrm && p_sys->es[i]->b_scrambled )
1456 b_scrambled = true;
1457 break;
1460 if( !p_pgrm->b_scrambled == !b_scrambled )
1461 return;
1463 p_pgrm->b_scrambled = b_scrambled;
1464 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1466 if( b_scrambled )
1467 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Scrambled"), _("Yes") );
1468 else
1469 input_Control( p_input, INPUT_DEL_INFO, psz_cat, _("Scrambled") );
1470 free( psz_cat );
1472 input_SendEventProgramScrambled( p_input, p_pgrm->i_id, b_scrambled );
1475 static void EsOutMeta( es_out_t *p_out, const vlc_meta_t *p_meta, const vlc_meta_t *p_program_meta )
1477 es_out_sys_t *p_sys = p_out->p_sys;
1478 input_thread_t *p_input = p_sys->p_input;
1479 input_item_t *p_item = input_GetItem( p_input );
1481 vlc_mutex_lock( &p_item->lock );
1482 if( p_meta )
1483 vlc_meta_Merge( p_item->p_meta, p_meta );
1484 vlc_mutex_unlock( &p_item->lock );
1486 /* Check program meta to not override GROUP_META values */
1487 if( (!p_program_meta || vlc_meta_Get( p_program_meta, vlc_meta_Title ) == NULL) &&
1488 vlc_meta_Get( p_meta, vlc_meta_Title ) != NULL )
1489 input_item_SetName( p_item, vlc_meta_Get( p_meta, vlc_meta_Title ) );
1491 const char *psz_arturl = NULL;
1492 char *psz_alloc = NULL;
1494 if( p_program_meta )
1495 psz_arturl = vlc_meta_Get( p_program_meta, vlc_meta_ArtworkURL );
1496 if( psz_arturl == NULL && p_meta )
1497 psz_arturl = vlc_meta_Get( p_meta, vlc_meta_ArtworkURL );
1499 if( psz_arturl == NULL ) /* restore/favor previously set item art URL */
1500 psz_arturl = psz_alloc = input_item_GetArtURL( p_item );
1502 if( psz_arturl != NULL )
1503 input_item_SetArtURL( p_item, psz_arturl );
1505 if( psz_arturl != NULL && !strncmp( psz_arturl, "attachment://", 13 ) )
1506 { /* Clear art cover if streaming out.
1507 * FIXME: Why? Remove this when sout gets meta data support. */
1508 if( input_priv(p_input)->p_sout != NULL )
1509 input_item_SetArtURL( p_item, NULL );
1510 else
1511 input_ExtractAttachmentAndCacheArt( p_input, psz_arturl + 13 );
1513 free( psz_alloc );
1515 input_item_SetPreparsed( p_item, true );
1517 input_SendEventMeta( p_input );
1518 /* TODO handle sout meta ? */
1521 static void EsOutGlobalMeta( es_out_t *p_out, const vlc_meta_t *p_meta )
1523 es_out_sys_t *p_sys = p_out->p_sys;
1524 EsOutMeta( p_out, p_meta,
1525 (p_sys->p_pgrm && p_sys->p_pgrm->p_meta) ? p_sys->p_pgrm->p_meta : NULL );
1528 static es_out_id_t *EsOutAddSlave( es_out_t *out, const es_format_t *fmt, es_out_id_t *p_master )
1530 es_out_sys_t *p_sys = out->p_sys;
1531 input_thread_t *p_input = p_sys->p_input;
1533 if( fmt->i_group < 0 )
1535 msg_Err( p_input, "invalid group number" );
1536 return NULL;
1539 es_out_id_t *es = malloc( sizeof( *es ) );
1540 es_out_pgrm_t *p_pgrm;
1541 int i;
1543 if( !es )
1544 return NULL;
1546 vlc_mutex_lock( &p_sys->lock );
1548 /* Search the program */
1549 p_pgrm = EsOutProgramFind( out, fmt->i_group );
1550 if( !p_pgrm )
1552 vlc_mutex_unlock( &p_sys->lock );
1553 free( es );
1554 return NULL;
1557 /* Increase ref count for program */
1558 p_pgrm->i_es++;
1560 /* Set up ES */
1561 es->p_pgrm = p_pgrm;
1562 es_format_Copy( &es->fmt, fmt );
1563 if( es->fmt.i_id < 0 )
1564 es->fmt.i_id = p_sys->i_id;
1565 if( !es->fmt.i_original_fourcc )
1566 es->fmt.i_original_fourcc = es->fmt.i_codec;
1568 es->i_id = es->fmt.i_id;
1569 es->i_meta_id = p_sys->i_id++; /* always incremented */
1570 es->b_scrambled = false;
1572 switch( es->fmt.i_cat )
1574 case AUDIO_ES:
1576 es->fmt.i_codec = vlc_fourcc_GetCodecAudio( es->fmt.i_codec,
1577 es->fmt.audio.i_bitspersample );
1578 es->i_channel = p_sys->audio.i_count++;
1580 audio_replay_gain_t rg;
1581 memset( &rg, 0, sizeof(rg) );
1582 vlc_mutex_lock( &input_priv(p_input)->p_item->lock );
1583 vlc_audio_replay_gain_MergeFromMeta( &rg, input_priv(p_input)->p_item->p_meta );
1584 vlc_mutex_unlock( &input_priv(p_input)->p_item->lock );
1586 for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
1588 if( !es->fmt.audio_replay_gain.pb_peak[i] )
1590 es->fmt.audio_replay_gain.pb_peak[i] = rg.pb_peak[i];
1591 es->fmt.audio_replay_gain.pf_peak[i] = rg.pf_peak[i];
1593 if( !es->fmt.audio_replay_gain.pb_gain[i] )
1595 es->fmt.audio_replay_gain.pb_gain[i] = rg.pb_gain[i];
1596 es->fmt.audio_replay_gain.pf_gain[i] = rg.pf_gain[i];
1599 break;
1602 case VIDEO_ES:
1603 es->fmt.i_codec = vlc_fourcc_GetCodec( es->fmt.i_cat, es->fmt.i_codec );
1604 es->i_channel = p_sys->video.i_count++;
1606 if( !es->fmt.video.i_visible_width || !es->fmt.video.i_visible_height )
1608 es->fmt.video.i_visible_width = es->fmt.video.i_width;
1609 es->fmt.video.i_visible_height = es->fmt.video.i_height;
1612 if( es->fmt.video.i_frame_rate && es->fmt.video.i_frame_rate_base )
1613 vlc_ureduce( &es->fmt.video.i_frame_rate,
1614 &es->fmt.video.i_frame_rate_base,
1615 es->fmt.video.i_frame_rate,
1616 es->fmt.video.i_frame_rate_base, 0 );
1617 break;
1619 case SPU_ES:
1620 es->fmt.i_codec = vlc_fourcc_GetCodec( es->fmt.i_cat, es->fmt.i_codec );
1621 es->i_channel = p_sys->sub.i_count++;
1622 break;
1624 default:
1625 es->i_channel = 0;
1626 break;
1628 es->psz_language = LanguageGetName( es->fmt.psz_language ); /* remember so we only need to do it once */
1629 es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
1630 es->p_dec = NULL;
1631 es->p_dec_record = NULL;
1632 for( i = 0; i < 4; i++ )
1633 es->pb_cc_present[i] = false;
1634 es->p_master = p_master;
1636 TAB_APPEND( p_sys->i_es, p_sys->es, es );
1638 if( es->p_pgrm == p_sys->p_pgrm )
1639 EsOutESVarUpdate( out, es, false );
1641 EsOutUpdateInfo( out, es, &es->fmt, NULL );
1642 EsOutSelect( out, es, false );
1644 if( es->b_scrambled )
1645 EsOutProgramUpdateScrambled( out, es->p_pgrm );
1647 vlc_mutex_unlock( &p_sys->lock );
1649 return es;
1652 /* EsOutAdd:
1653 * Add an es_out
1655 static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt )
1657 return EsOutAddSlave( out, fmt, NULL );
1660 static bool EsIsSelected( es_out_id_t *es )
1662 if( es->p_master )
1664 bool b_decode = false;
1665 if( es->p_master->p_dec )
1667 int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_original_fourcc );
1668 if( i_channel != -1 )
1669 input_DecoderGetCcState( es->p_master->p_dec, &b_decode, i_channel );
1671 return b_decode;
1673 else
1675 return es->p_dec != NULL;
1678 static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
1680 es_out_sys_t *p_sys = out->p_sys;
1681 input_thread_t *p_input = p_sys->p_input;
1683 p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, input_priv(p_input)->p_sout );
1684 if( p_es->p_dec )
1686 if( p_sys->b_buffering )
1687 input_DecoderStartWait( p_es->p_dec );
1689 if( !p_es->p_master && p_sys->p_sout_record )
1691 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
1692 if( p_es->p_dec_record && p_sys->b_buffering )
1693 input_DecoderStartWait( p_es->p_dec_record );
1697 EsOutDecoderChangeDelay( out, p_es );
1699 static void EsDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
1701 VLC_UNUSED(out);
1703 if( !p_es->p_dec )
1704 return;
1706 input_DecoderDelete( p_es->p_dec );
1707 p_es->p_dec = NULL;
1709 if( p_es->p_dec_record )
1711 input_DecoderDelete( p_es->p_dec_record );
1712 p_es->p_dec_record = NULL;
1716 static void EsSelect( es_out_t *out, es_out_id_t *es )
1718 es_out_sys_t *p_sys = out->p_sys;
1719 input_thread_t *p_input = p_sys->p_input;
1721 if( EsIsSelected( es ) )
1723 msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
1724 return;
1727 if( es->p_master )
1729 int i_channel;
1730 if( !es->p_master->p_dec )
1731 return;
1733 i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_original_fourcc );
1734 if( i_channel == -1 || input_DecoderSetCcState( es->p_master->p_dec, true, i_channel ) )
1735 return;
1737 else
1739 const bool b_sout = input_priv(p_input)->p_sout != NULL;
1740 if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
1742 if( !var_GetBool( p_input, b_sout ? "sout-video" : "video" ) )
1744 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
1745 es->i_id );
1746 return;
1749 else if( es->fmt.i_cat == AUDIO_ES )
1751 if( !var_GetBool( p_input, b_sout ? "sout-audio" : "audio" ) )
1753 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
1754 es->i_id );
1755 return;
1758 if( es->fmt.i_cat == SPU_ES )
1760 if( !var_GetBool( p_input, b_sout ? "sout-spu" : "spu" ) )
1762 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
1763 es->i_id );
1764 return;
1768 EsCreateDecoder( out, es );
1770 if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
1771 return;
1774 /* Mark it as selected */
1775 input_SendEventEsSelect( p_input, es->fmt.i_cat, es->i_id );
1776 input_SendEventTeletextSelect( p_input, EsFmtIsTeletext( &es->fmt ) ? es->i_id : -1 );
1779 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
1781 es_out_sys_t *p_sys = out->p_sys;
1782 input_thread_t *p_input = p_sys->p_input;
1784 if( !EsIsSelected( es ) )
1786 msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
1787 return;
1790 if( es->p_master )
1792 if( es->p_master->p_dec )
1794 int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_original_fourcc );
1795 if( i_channel != -1 )
1796 input_DecoderSetCcState( es->p_master->p_dec, false, i_channel );
1799 else
1801 const int i_spu_id = var_GetInteger( p_input, "spu-es");
1802 int i;
1803 for( i = 0; i < 4; i++ )
1805 if( !es->pb_cc_present[i] || !es->pp_cc_es[i] )
1806 continue;
1808 if( i_spu_id == es->pp_cc_es[i]->i_id )
1810 /* Force unselection of the CC */
1811 input_SendEventEsSelect( p_input, SPU_ES, -1 );
1813 EsOutDel( out, es->pp_cc_es[i] );
1815 es->pb_cc_present[i] = false;
1817 EsDestroyDecoder( out, es );
1820 if( !b_update )
1821 return;
1823 /* Mark it as unselected */
1824 input_SendEventEsSelect( p_input, es->fmt.i_cat, -1 );
1825 if( EsFmtIsTeletext( &es->fmt ) )
1826 input_SendEventTeletextSelect( p_input, -1 );
1830 * Select an ES given the current mode
1831 * XXX: you need to take a the lock before (stream.stream_lock)
1833 * \param out The es_out structure
1834 * \param es es_out_id structure
1835 * \param b_force ...
1836 * \return nothing
1838 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
1840 es_out_sys_t *p_sys = out->p_sys;
1841 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
1843 if( !p_sys->b_active ||
1844 ( !b_force && es->fmt.i_priority < ES_PRIORITY_SELECTABLE_MIN ) )
1846 return;
1849 if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
1851 if( !EsIsSelected( es ) )
1852 EsSelect( out, es );
1854 else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
1856 char *prgms = var_GetNonEmptyString( p_sys->p_input, "programs" );
1857 if( prgms != NULL )
1859 char *buf;
1861 for ( const char *prgm = strtok_r( prgms, ",", &buf );
1862 prgm != NULL;
1863 prgm = strtok_r( NULL, ",", &buf ) )
1865 if( atoi( prgm ) == es->p_pgrm->i_id || b_force )
1867 if( !EsIsSelected( es ) )
1868 EsSelect( out, es );
1869 break;
1872 free( prgms );
1875 else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
1877 const es_out_id_t *wanted_es = NULL;
1879 if( es->p_pgrm != p_sys->p_pgrm || !p_esprops )
1880 return;
1882 /* user designated by ID ES have higher prio than everything */
1883 if ( p_esprops->i_id >= 0 )
1885 if( es->i_id == p_esprops->i_id )
1886 wanted_es = es;
1888 /* then per pos */
1889 else if( p_esprops->i_channel >= 0 )
1891 if( p_esprops->i_channel == es->i_channel )
1892 wanted_es = es;
1894 else if( p_esprops->ppsz_language )
1896 /* If not deactivated */
1897 const int i_stop_idx = LanguageArrayIndex( p_esprops->ppsz_language, "none" );
1899 int current_es_idx = ( p_esprops->p_main_es == NULL ) ? -1 :
1900 LanguageArrayIndex( p_esprops->ppsz_language,
1901 p_esprops->p_main_es->psz_language_code );
1902 int es_idx = LanguageArrayIndex( p_esprops->ppsz_language,
1903 es->psz_language_code );
1904 if( es_idx >= 0 && (i_stop_idx < 0 || i_stop_idx > es_idx) )
1906 /* Only select the language if it's in the list */
1907 if( p_esprops->p_main_es == NULL ||
1908 current_es_idx < 0 || /* current es was not selected by lang prefs */
1909 es_idx < current_es_idx || /* current es has lower lang prio */
1910 ( es_idx == current_es_idx && /* lang is same, but es has higher prio */
1911 p_esprops->p_main_es->fmt.i_priority < es->fmt.i_priority ) )
1913 wanted_es = es;
1916 /* We did not find a language matching our prefs */
1917 else if( i_stop_idx < 0 ) /* If not fallback disabled by 'none' */
1919 /* Select if asked by demuxer */
1920 if( current_es_idx < 0 ) /* No es is currently selected by lang pref */
1922 /* If demux has specified a track */
1923 if( p_esprops->i_demux_id >= 0 && es->i_id == p_esprops->i_demux_id )
1925 wanted_es = es;
1927 /* Otherwise, fallback by priority */
1928 else if( p_esprops->p_main_es == NULL ||
1929 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority )
1931 if( p_esprops->b_autoselect )
1932 wanted_es = es;
1939 /* If there is no user preference, select the default subtitle
1940 * or adapt by ES priority */
1941 else if( p_esprops->i_demux_id >= 0 && es->i_id == p_esprops->i_demux_id )
1943 wanted_es = es;
1945 else if( p_esprops->p_main_es == NULL ||
1946 es->fmt.i_priority > p_esprops->p_main_es->fmt.i_priority )
1948 if( p_esprops->b_autoselect )
1949 wanted_es = es;
1952 if( wanted_es == es && !EsIsSelected( es ) )
1953 EsSelect( out, es );
1956 /* FIXME TODO handle priority here */
1957 if( p_esprops && EsIsSelected( es ) )
1959 if( p_sys->i_mode == ES_OUT_MODE_AUTO )
1961 if( p_esprops->e_policy == ES_OUT_ES_POLICY_EXCLUSIVE &&
1962 p_esprops->p_main_es &&
1963 p_esprops->p_main_es != es )
1965 EsUnselect( out, p_esprops->p_main_es, false );
1967 p_esprops->p_main_es = es;
1973 * Send a block for the given es_out
1975 * \param out the es_out to send from
1976 * \param es the es_out_id
1977 * \param p_block the data block to send
1979 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
1981 es_out_sys_t *p_sys = out->p_sys;
1982 input_thread_t *p_input = p_sys->p_input;
1984 if( libvlc_stats( p_input ) )
1986 uint64_t i_total;
1988 vlc_mutex_lock( &input_priv(p_input)->counters.counters_lock );
1989 stats_Update( input_priv(p_input)->counters.p_demux_read,
1990 p_block->i_buffer, &i_total );
1991 stats_Update( input_priv(p_input)->counters.p_demux_bitrate, i_total, NULL );
1993 /* Update number of corrupted data packats */
1994 if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
1996 stats_Update( input_priv(p_input)->counters.p_demux_corrupted, 1, NULL );
1998 /* Update number of discontinuities */
1999 if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )
2001 stats_Update( input_priv(p_input)->counters.p_demux_discontinuity, 1, NULL );
2003 vlc_mutex_unlock( &input_priv(p_input)->counters.counters_lock );
2006 vlc_mutex_lock( &p_sys->lock );
2008 /* Mark preroll blocks */
2009 if( p_sys->i_preroll_end >= 0 )
2011 int64_t i_date = p_block->i_pts;
2012 if( p_block->i_pts <= VLC_TS_INVALID )
2013 i_date = p_block->i_dts;
2015 if( i_date < p_sys->i_preroll_end )
2016 p_block->i_flags |= BLOCK_FLAG_PREROLL;
2019 if( !es->p_dec )
2021 block_Release( p_block );
2022 vlc_mutex_unlock( &p_sys->lock );
2023 return VLC_SUCCESS;
2026 /* Check for sout mode */
2027 if( input_priv(p_input)->p_sout )
2029 /* FIXME review this, proper lock may be missing */
2030 if( input_priv(p_input)->p_sout->i_out_pace_nocontrol > 0 &&
2031 input_priv(p_input)->b_out_pace_control )
2033 msg_Dbg( p_input, "switching to sync mode" );
2034 input_priv(p_input)->b_out_pace_control = false;
2036 else if( input_priv(p_input)->p_sout->i_out_pace_nocontrol <= 0 &&
2037 !input_priv(p_input)->b_out_pace_control )
2039 msg_Dbg( p_input, "switching to async mode" );
2040 input_priv(p_input)->b_out_pace_control = true;
2044 /* Decode */
2045 if( es->p_dec_record )
2047 block_t *p_dup = block_Duplicate( p_block );
2048 if( p_dup )
2049 input_DecoderDecode( es->p_dec_record, p_dup,
2050 input_priv(p_input)->b_out_pace_control );
2052 input_DecoderDecode( es->p_dec, p_block,
2053 input_priv(p_input)->b_out_pace_control );
2055 es_format_t fmt_dsc;
2056 vlc_meta_t *p_meta_dsc;
2057 if( input_DecoderHasFormatChanged( es->p_dec, &fmt_dsc, &p_meta_dsc ) )
2059 EsOutUpdateInfo( out, es, &fmt_dsc, p_meta_dsc );
2061 es_format_Clean( &fmt_dsc );
2062 if( p_meta_dsc )
2063 vlc_meta_Delete( p_meta_dsc );
2066 /* Check CC status */
2067 bool pb_cc[4];
2069 input_DecoderIsCcPresent( es->p_dec, pb_cc );
2070 for( int i = 0; i < 4; i++ )
2072 es_format_t fmt;
2074 if( es->pb_cc_present[i] || !pb_cc[i] )
2075 continue;
2076 msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id );
2078 es_format_Init( &fmt, SPU_ES, EsOutFourccClosedCaptions[i] );
2079 fmt.i_group = es->fmt.i_group;
2080 if( asprintf( &fmt.psz_description,
2081 _("Closed captions %u"), 1 + i ) == -1 )
2082 fmt.psz_description = NULL;
2083 es->pp_cc_es[i] = EsOutAddSlave( out, &fmt, es );
2084 es_format_Clean( &fmt );
2086 /* */
2087 es->pb_cc_present[i] = true;
2089 /* Enable if user specified on command line */
2090 if (p_sys->sub.i_channel == i)
2091 EsOutSelect(out, es->pp_cc_es[i], true);
2094 vlc_mutex_unlock( &p_sys->lock );
2096 return VLC_SUCCESS;
2099 /*****************************************************************************
2100 * EsOutDel:
2101 *****************************************************************************/
2102 static void EsOutDel( es_out_t *out, es_out_id_t *es )
2104 es_out_sys_t *p_sys = out->p_sys;
2105 bool b_reselect = false;
2106 int i;
2108 vlc_mutex_lock( &p_sys->lock );
2110 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, es->fmt.i_cat );
2112 /* We don't try to reselect */
2113 if( es->p_dec )
2114 { /* FIXME: This might hold the ES output caller (i.e. the demux), and
2115 * the corresponding thread (typically the input thread), for a little
2116 * bit too long if the ES is deleted in the middle of a stream. */
2117 while( !input_Stopped(p_sys->p_input) && !p_sys->b_buffering )
2119 if( input_DecoderIsEmpty( es->p_dec ) &&
2120 ( !es->p_dec_record || input_DecoderIsEmpty( es->p_dec_record ) ))
2121 break;
2122 /* FIXME there should be a way to have auto deleted es, but there will be
2123 * a problem when another codec of the same type is created (mainly video) */
2124 msleep( 20*1000 );
2126 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
2129 if( es->p_pgrm == p_sys->p_pgrm )
2130 EsOutESVarUpdate( out, es, true );
2132 EsDeleteInfo( out, es );
2134 TAB_REMOVE( p_sys->i_es, p_sys->es, es );
2136 /* Update program */
2137 es->p_pgrm->i_es--;
2138 if( es->p_pgrm->i_es == 0 )
2139 msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" );
2141 if( es->b_scrambled )
2142 EsOutProgramUpdateScrambled( out, es->p_pgrm );
2144 /* */
2145 if( p_esprops )
2147 if( p_esprops->p_main_es == es )
2149 b_reselect = true;
2150 p_esprops->p_main_es = NULL;
2152 p_esprops->i_count--;
2155 /* Re-select another track when needed */
2156 if( b_reselect )
2158 for( i = 0; i < p_sys->i_es; i++ )
2160 if( es->fmt.i_cat == p_sys->es[i]->fmt.i_cat )
2161 EsOutSelect( out, p_sys->es[i], false );
2165 free( es->psz_language );
2166 free( es->psz_language_code );
2168 es_format_Clean( &es->fmt );
2170 vlc_mutex_unlock( &p_sys->lock );
2172 free( es );
2176 * Control query handler
2178 * \param out the es_out to control
2179 * \param i_query A es_out query as defined in include/ninput.h
2180 * \param args a variable list of arguments for the query
2181 * \return VLC_SUCCESS or an error code
2183 static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
2185 es_out_sys_t *p_sys = out->p_sys;
2187 switch( i_query )
2189 case ES_OUT_SET_ES_STATE:
2191 es_out_id_t *es = va_arg( args, es_out_id_t * );
2192 bool b = va_arg( args, int );
2193 if( b && !EsIsSelected( es ) )
2195 EsSelect( out, es );
2196 return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC;
2198 else if( !b && EsIsSelected( es ) )
2200 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
2201 return VLC_SUCCESS;
2203 return VLC_SUCCESS;
2206 case ES_OUT_GET_ES_STATE:
2208 es_out_id_t *es = va_arg( args, es_out_id_t * );
2209 bool *pb = va_arg( args, bool * );
2211 *pb = EsIsSelected( es );
2212 return VLC_SUCCESS;
2215 case ES_OUT_SET_ES_CAT_POLICY:
2217 enum es_format_category_e i_cat = va_arg( args, enum es_format_category_e );
2218 enum es_out_policy_e i_pol = va_arg( args, enum es_out_policy_e );
2219 es_out_es_props_t *p_esprops = GetPropsByCat( p_sys, i_cat );
2220 if( p_esprops == NULL )
2221 return VLC_EGENERIC;
2222 p_esprops->e_policy = i_pol;
2223 return VLC_SUCCESS;
2226 case ES_OUT_GET_GROUP_FORCED:
2228 int *pi_group = va_arg( args, int * );
2229 *pi_group = p_sys->i_group_id;
2230 return VLC_SUCCESS;
2233 case ES_OUT_SET_MODE:
2235 const int i_mode = va_arg( args, int );
2236 assert( i_mode == ES_OUT_MODE_NONE || i_mode == ES_OUT_MODE_ALL ||
2237 i_mode == ES_OUT_MODE_AUTO || i_mode == ES_OUT_MODE_PARTIAL ||
2238 i_mode == ES_OUT_MODE_END );
2240 if( i_mode != ES_OUT_MODE_NONE && !p_sys->b_active && p_sys->i_es > 0 )
2242 /* XXX Terminate vout if there are tracks but no video one.
2243 * This one is not mandatory but is he earliest place where it
2244 * can be done */
2245 int i;
2246 for( i = 0; i < p_sys->i_es; i++ )
2248 es_out_id_t *p_es = p_sys->es[i];
2249 if( p_es->fmt.i_cat == VIDEO_ES )
2250 break;
2252 if( i >= p_sys->i_es )
2253 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
2255 p_sys->b_active = i_mode != ES_OUT_MODE_NONE;
2256 p_sys->i_mode = i_mode;
2258 /* Reapply policy mode */
2259 for( int i = 0; i < p_sys->i_es; i++ )
2261 if( EsIsSelected( p_sys->es[i] ) )
2262 EsUnselect( out, p_sys->es[i],
2263 p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2265 for( int i = 0; i < p_sys->i_es; i++ )
2266 EsOutSelect( out, p_sys->es[i], false );
2267 if( i_mode == ES_OUT_MODE_END )
2268 EsOutTerminate( out );
2269 return VLC_SUCCESS;
2272 case ES_OUT_SET_ES:
2273 case ES_OUT_RESTART_ES:
2275 es_out_id_t *es = va_arg( args, es_out_id_t * );
2277 int i_cat;
2278 if( es == NULL )
2279 i_cat = UNKNOWN_ES;
2280 else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
2281 i_cat = AUDIO_ES;
2282 else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
2283 i_cat = VIDEO_ES;
2284 else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
2285 i_cat = SPU_ES;
2286 else
2287 i_cat = -1;
2289 for( int i = 0; i < p_sys->i_es; i++ )
2291 if( i_cat == -1 )
2293 if( es == p_sys->es[i] )
2295 if( i_query == ES_OUT_RESTART_ES && p_sys->es[i]->p_dec )
2297 EsDestroyDecoder( out, p_sys->es[i] );
2298 EsCreateDecoder( out, p_sys->es[i] );
2300 else if( i_query == ES_OUT_SET_ES )
2302 EsOutSelect( out, es, true );
2304 break;
2307 else
2309 if( i_cat == UNKNOWN_ES || p_sys->es[i]->fmt.i_cat == i_cat )
2311 if( EsIsSelected( p_sys->es[i] ) )
2313 if( i_query == ES_OUT_RESTART_ES )
2315 if( p_sys->es[i]->p_dec )
2317 EsDestroyDecoder( out, p_sys->es[i] );
2318 EsCreateDecoder( out, p_sys->es[i] );
2321 else
2323 EsUnselect( out, p_sys->es[i],
2324 p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2330 return VLC_SUCCESS;
2333 case ES_OUT_SET_ES_DEFAULT:
2335 es_out_id_t *es = va_arg( args, es_out_id_t * );
2337 if( es == NULL )
2339 /*p_sys->i_default_video_id = -1;*/
2340 /*p_sys->i_default_audio_id = -1;*/
2341 p_sys->sub.i_demux_id = -1;
2343 else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
2345 /*p_sys->i_default_video_id = -1;*/
2347 else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
2349 /*p_sys->i_default_audio_id = -1;*/
2351 else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
2353 p_sys->sub.i_demux_id = -1;
2355 else
2357 /*if( es->fmt.i_cat == VIDEO_ES )
2358 p_sys->i_default_video_id = es->i_id;
2359 else
2360 if( es->fmt.i_cat == AUDIO_ES )
2361 p_sys->i_default_audio_id = es->i_id;
2362 else*/
2363 if( es->fmt.i_cat == SPU_ES )
2364 p_sys->sub.i_demux_id = es->i_id;
2366 return VLC_SUCCESS;
2369 case ES_OUT_SET_PCR:
2370 case ES_OUT_SET_GROUP_PCR:
2372 es_out_pgrm_t *p_pgrm = NULL;
2373 int i_group = 0;
2374 int64_t i_pcr;
2376 /* Search program */
2377 if( i_query == ES_OUT_SET_PCR )
2379 p_pgrm = p_sys->p_pgrm;
2380 if( !p_pgrm )
2381 p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
2383 else
2385 i_group = va_arg( args, int );
2386 p_pgrm = EsOutProgramFind( out, i_group );
2388 if( !p_pgrm )
2389 return VLC_EGENERIC;
2391 i_pcr = va_arg( args, int64_t );
2392 if( i_pcr <= VLC_TS_INVALID )
2394 msg_Err( p_sys->p_input, "Invalid PCR value in ES_OUT_SET_(GROUP_)PCR !" );
2395 return VLC_EGENERIC;
2398 /* TODO do not use mdate() but proper stream acquisition date */
2399 bool b_late;
2400 input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input),
2401 &b_late,
2402 input_priv(p_sys->p_input)->b_can_pace_control || p_sys->b_buffering,
2403 EsOutIsExtraBufferingAllowed( out ),
2404 i_pcr, mdate() );
2406 if( !p_sys->p_pgrm )
2407 return VLC_SUCCESS;
2409 if( p_sys->b_buffering )
2411 /* Check buffering state on master clock update */
2412 EsOutDecodersStopBuffering( out, false );
2414 else if( p_pgrm == p_sys->p_pgrm )
2416 if( b_late && ( !input_priv(p_sys->p_input)->p_sout ||
2417 !input_priv(p_sys->p_input)->b_out_pace_control ) )
2419 const mtime_t i_pts_delay_base = p_sys->i_pts_delay - p_sys->i_pts_jitter;
2420 mtime_t i_pts_delay = input_clock_GetJitter( p_pgrm->p_clock );
2422 /* Avoid dangerously high value */
2423 const mtime_t i_jitter_max = INT64_C(1000) * var_InheritInteger( p_sys->p_input, "clock-jitter" );
2424 if( i_pts_delay > __MIN( i_pts_delay_base + i_jitter_max, INPUT_PTS_DELAY_MAX ) )
2426 msg_Err( p_sys->p_input,
2427 "ES_OUT_SET_(GROUP_)PCR is called too late (jitter of %d ms ignored)",
2428 (int)(i_pts_delay - i_pts_delay_base) / 1000 );
2429 i_pts_delay = p_sys->i_pts_delay;
2431 /* reset clock */
2432 for( int i = 0; i < p_sys->i_pgrm; i++ )
2433 input_clock_Reset( p_sys->pgrm[i]->p_clock );
2435 else
2437 msg_Err( p_sys->p_input,
2438 "ES_OUT_SET_(GROUP_)PCR is called too late (pts_delay increased to %d ms)",
2439 (int)(i_pts_delay/1000) );
2441 /* Force a rebufferization when we are too late */
2443 /* It is not really good, as we throw away already buffered data
2444 * TODO have a mean to correctly reenter bufferization */
2445 es_out_Control( out, ES_OUT_RESET_PCR );
2448 es_out_SetJitter( out, i_pts_delay_base, i_pts_delay - i_pts_delay_base, p_sys->i_cr_average );
2451 return VLC_SUCCESS;
2454 case ES_OUT_RESET_PCR:
2455 msg_Dbg( p_sys->p_input, "ES_OUT_RESET_PCR called" );
2456 EsOutChangePosition( out );
2457 return VLC_SUCCESS;
2459 case ES_OUT_SET_GROUP:
2461 int i = va_arg( args, int );
2462 for( int j = 0; j < p_sys->i_pgrm; j++ )
2464 es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
2465 if( p_pgrm->i_id == i )
2467 EsOutProgramSelect( out, p_pgrm );
2468 return VLC_SUCCESS;
2471 return VLC_EGENERIC;
2474 case ES_OUT_SET_ES_FMT:
2476 /* This ain't pretty but is need by some demuxers (eg. Ogg )
2477 * to update the p_extra data */
2478 es_out_id_t *es = va_arg( args, es_out_id_t * );
2479 es_format_t *p_fmt = va_arg( args, es_format_t * );
2480 if( es == NULL )
2481 return VLC_EGENERIC;
2483 es_format_Clean( &es->fmt );
2484 es_format_Copy( &es->fmt, p_fmt );
2486 if( es->p_dec )
2488 EsDestroyDecoder( out, es );
2489 EsCreateDecoder( out, es );
2492 return VLC_SUCCESS;
2495 case ES_OUT_SET_ES_SCRAMBLED_STATE:
2497 es_out_id_t *es = va_arg( args, es_out_id_t * );
2498 bool b_scrambled = (bool)va_arg( args, int );
2500 if( !es->b_scrambled != !b_scrambled )
2502 es->b_scrambled = b_scrambled;
2503 EsOutProgramUpdateScrambled( out, es->p_pgrm );
2505 return VLC_SUCCESS;
2508 case ES_OUT_SET_NEXT_DISPLAY_TIME:
2510 const int64_t i_date = va_arg( args, int64_t );
2512 if( i_date < 0 )
2513 return VLC_EGENERIC;
2515 p_sys->i_preroll_end = i_date;
2517 return VLC_SUCCESS;
2519 case ES_OUT_SET_GROUP_META:
2521 int i_group = va_arg( args, int );
2522 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
2524 EsOutProgramMeta( out, i_group, p_meta );
2525 return VLC_SUCCESS;
2527 case ES_OUT_SET_GROUP_EPG:
2529 int i_group = va_arg( args, int );
2530 const vlc_epg_t *p_epg = va_arg( args, const vlc_epg_t * );
2532 EsOutProgramEpg( out, i_group, p_epg );
2533 return VLC_SUCCESS;
2535 case ES_OUT_SET_GROUP_EPG_EVENT:
2537 int i_group = va_arg( args, int );
2538 const vlc_epg_event_t *p_evt = va_arg( args, const vlc_epg_event_t * );
2540 EsOutProgramEpgEvent( out, i_group, p_evt );
2541 return VLC_SUCCESS;
2543 case ES_OUT_SET_EPG_TIME:
2545 int64_t i64 = va_arg( args, int64_t );
2547 EsOutEpgTime( out, i64 );
2548 return VLC_SUCCESS;
2551 case ES_OUT_DEL_GROUP:
2553 int i_group = va_arg( args, int );
2555 return EsOutProgramDel( out, i_group );
2558 case ES_OUT_SET_META:
2560 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
2562 EsOutGlobalMeta( out, p_meta );
2563 return VLC_SUCCESS;
2566 case ES_OUT_GET_WAKE_UP:
2568 mtime_t *pi_wakeup = va_arg( args, mtime_t* );
2569 *pi_wakeup = EsOutGetWakeup( out );
2570 return VLC_SUCCESS;
2573 case ES_OUT_SET_ES_BY_ID:
2574 case ES_OUT_RESTART_ES_BY_ID:
2575 case ES_OUT_SET_ES_DEFAULT_BY_ID:
2577 const int i_id = va_arg( args, int );
2578 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2579 int i_new_query = 0;
2581 switch( i_query )
2583 case ES_OUT_SET_ES_BY_ID: i_new_query = ES_OUT_SET_ES; break;
2584 case ES_OUT_RESTART_ES_BY_ID: i_new_query = ES_OUT_RESTART_ES; break;
2585 case ES_OUT_SET_ES_DEFAULT_BY_ID: i_new_query = ES_OUT_SET_ES_DEFAULT; break;
2586 default:
2587 vlc_assert_unreachable();
2589 /* TODO if the lock is made non recursive it should be changed */
2590 int i_ret = es_out_Control( out, i_new_query, p_es );
2592 /* Clean up vout after user action (in active mode only).
2593 * FIXME it does not work well with multiple video windows */
2594 if( p_sys->b_active )
2595 input_resource_TerminateVout( input_priv(p_sys->p_input)->p_resource );
2596 return i_ret;
2599 case ES_OUT_GET_ES_OBJECTS_BY_ID:
2601 const int i_id = va_arg( args, int );
2602 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2603 if( !p_es )
2604 return VLC_EGENERIC;
2606 vlc_object_t **pp_decoder = va_arg( args, vlc_object_t ** );
2607 vout_thread_t **pp_vout = va_arg( args, vout_thread_t ** );
2608 audio_output_t **pp_aout = va_arg( args, audio_output_t ** );
2609 if( p_es->p_dec )
2611 if( pp_decoder )
2612 *pp_decoder = vlc_object_hold( p_es->p_dec );
2613 input_DecoderGetObjects( p_es->p_dec, pp_vout, pp_aout );
2615 else
2617 if( pp_decoder )
2618 *pp_decoder = NULL;
2619 if( pp_vout )
2620 *pp_vout = NULL;
2621 if( pp_aout )
2622 *pp_aout = NULL;
2624 return VLC_SUCCESS;
2627 case ES_OUT_GET_BUFFERING:
2629 bool *pb = va_arg( args, bool* );
2630 *pb = p_sys->b_buffering;
2631 return VLC_SUCCESS;
2634 case ES_OUT_GET_EMPTY:
2636 bool *pb = va_arg( args, bool* );
2637 *pb = EsOutDecodersIsEmpty( out );
2638 return VLC_SUCCESS;
2641 case ES_OUT_SET_DELAY:
2643 const int i_cat = va_arg( args, int );
2644 const mtime_t i_delay = va_arg( args, mtime_t );
2645 EsOutSetDelay( out, i_cat, i_delay );
2646 return VLC_SUCCESS;
2649 case ES_OUT_SET_RECORD_STATE:
2651 bool b = va_arg( args, int );
2652 return EsOutSetRecord( out, b );
2655 case ES_OUT_SET_PAUSE_STATE:
2657 const bool b_source_paused = (bool)va_arg( args, int );
2658 const bool b_paused = (bool)va_arg( args, int );
2659 const mtime_t i_date = va_arg( args, mtime_t );
2661 assert( !b_source_paused == !b_paused );
2662 EsOutChangePause( out, b_paused, i_date );
2664 return VLC_SUCCESS;
2667 case ES_OUT_SET_RATE:
2669 const int i_src_rate = va_arg( args, int );
2670 const int i_rate = va_arg( args, int );
2672 assert( i_src_rate == i_rate );
2673 EsOutChangeRate( out, i_rate );
2675 return VLC_SUCCESS;
2678 case ES_OUT_SET_TIME:
2680 const mtime_t i_date = va_arg( args, mtime_t );
2682 assert( i_date == -1 );
2683 EsOutChangePosition( out );
2685 return VLC_SUCCESS;
2688 case ES_OUT_SET_FRAME_NEXT:
2689 EsOutFrameNext( out );
2690 return VLC_SUCCESS;
2692 case ES_OUT_SET_TIMES:
2694 double f_position = va_arg( args, double );
2695 mtime_t i_time = va_arg( args, mtime_t );
2696 mtime_t i_length = va_arg( args, mtime_t );
2698 input_SendEventLength( p_sys->p_input, i_length );
2700 if( !p_sys->b_buffering )
2702 mtime_t i_delay;
2704 /* Fix for buffering delay */
2705 if( !input_priv(p_sys->p_input)->p_sout ||
2706 !input_priv(p_sys->p_input)->b_out_pace_control )
2707 i_delay = EsOutGetBuffering( out );
2708 else
2709 i_delay = 0;
2711 i_time -= i_delay;
2712 if( i_time < 0 )
2713 i_time = 0;
2715 if( i_length > 0 )
2716 f_position -= (double)i_delay / i_length;
2717 if( f_position < 0 )
2718 f_position = 0;
2720 input_SendEventPosition( p_sys->p_input, f_position, i_time );
2722 return VLC_SUCCESS;
2724 case ES_OUT_SET_JITTER:
2726 mtime_t i_pts_delay = va_arg( args, mtime_t );
2727 mtime_t i_pts_jitter = va_arg( args, mtime_t );
2728 int i_cr_average = va_arg( args, int );
2730 bool b_change_clock =
2731 i_pts_delay + i_pts_jitter != p_sys->i_pts_delay ||
2732 i_cr_average != p_sys->i_cr_average;
2734 assert( i_pts_jitter >= 0 );
2735 p_sys->i_pts_delay = i_pts_delay + i_pts_jitter;
2736 p_sys->i_pts_jitter = i_pts_jitter;
2737 p_sys->i_cr_average = i_cr_average;
2739 for( int i = 0; i < p_sys->i_pgrm && b_change_clock; i++ )
2740 input_clock_SetJitter( p_sys->pgrm[i]->p_clock,
2741 i_pts_delay + i_pts_jitter, i_cr_average );
2742 return VLC_SUCCESS;
2745 case ES_OUT_GET_PCR_SYSTEM:
2747 if( p_sys->b_buffering )
2748 return VLC_EGENERIC;
2750 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
2751 if( !p_pgrm )
2752 return VLC_EGENERIC;
2754 mtime_t *pi_system = va_arg( args, mtime_t *);
2755 mtime_t *pi_delay = va_arg( args, mtime_t *);
2756 input_clock_GetSystemOrigin( p_pgrm->p_clock, pi_system, pi_delay );
2757 return VLC_SUCCESS;
2760 case ES_OUT_MODIFY_PCR_SYSTEM:
2762 if( p_sys->b_buffering )
2763 return VLC_EGENERIC;
2765 es_out_pgrm_t *p_pgrm = p_sys->p_pgrm;
2766 if( !p_pgrm )
2767 return VLC_EGENERIC;
2769 const bool b_absolute = va_arg( args, int );
2770 const mtime_t i_system = va_arg( args, mtime_t );
2771 input_clock_ChangeSystemOrigin( p_pgrm->p_clock, b_absolute, i_system );
2772 return VLC_SUCCESS;
2774 case ES_OUT_SET_EOS:
2776 for (int i = 0; i < p_sys->i_es; i++) {
2777 es_out_id_t *id = p_sys->es[i];
2778 if (id->p_dec != NULL)
2779 input_DecoderDrain(id->p_dec);
2781 return VLC_SUCCESS;
2784 case ES_OUT_POST_SUBNODE:
2786 input_item_node_t *node = va_arg(args, input_item_node_t *);
2787 input_item_node_PostAndDelete(node);
2788 return VLC_SUCCESS;
2791 default:
2792 msg_Err( p_sys->p_input, "unknown query 0x%x in %s", i_query,
2793 __func__ );
2794 return VLC_EGENERIC;
2797 static int EsOutControl( es_out_t *out, int i_query, va_list args )
2799 es_out_sys_t *p_sys = out->p_sys;
2800 int i_ret;
2802 vlc_mutex_lock( &p_sys->lock );
2803 i_ret = EsOutControlLocked( out, i_query, args );
2804 vlc_mutex_unlock( &p_sys->lock );
2806 return i_ret;
2809 /****************************************************************************
2810 * LanguageGetName: try to expend iso639 into plain name
2811 ****************************************************************************/
2812 static char *LanguageGetName( const char *psz_code )
2814 const iso639_lang_t *pl;
2816 if( psz_code == NULL || !strcmp( psz_code, "und" ) )
2818 return strdup( "" );
2821 if( strlen( psz_code ) == 2 )
2823 pl = GetLang_1( psz_code );
2825 else if( strlen( psz_code ) == 3 )
2827 pl = GetLang_2B( psz_code );
2828 if( !strcmp( pl->psz_iso639_1, "??" ) )
2830 pl = GetLang_2T( psz_code );
2833 else
2835 char *lang = LanguageGetCode( psz_code );
2836 pl = GetLang_1( lang );
2837 free( lang );
2840 if( !strcmp( pl->psz_iso639_1, "??" ) )
2842 return strdup( psz_code );
2844 else
2846 return strdup( vlc_gettext(pl->psz_eng_name) );
2850 /* Get a 2 char code */
2851 static char *LanguageGetCode( const char *psz_lang )
2853 const iso639_lang_t *pl;
2855 if( psz_lang == NULL || *psz_lang == '\0' )
2856 return strdup("??");
2858 for( pl = p_languages; pl->psz_eng_name != NULL; pl++ )
2860 if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
2861 !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
2862 !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
2863 !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
2864 return strdup( pl->psz_iso639_1 );
2867 return strdup("??");
2870 static char **LanguageSplit( const char *psz_langs )
2872 char *psz_dup;
2873 char *psz_parser;
2874 char **ppsz = NULL;
2875 int i_psz = 0;
2877 if( psz_langs == NULL ) return NULL;
2879 psz_parser = psz_dup = strdup(psz_langs);
2881 while( psz_parser && *psz_parser )
2883 char *psz;
2884 char *psz_code;
2886 psz = strchr(psz_parser, ',' );
2887 if( psz ) *psz++ = '\0';
2889 if( !strcmp( psz_parser, "any" ) )
2891 TAB_APPEND( i_psz, ppsz, strdup("any") );
2893 else if( !strcmp( psz_parser, "none" ) )
2895 TAB_APPEND( i_psz, ppsz, strdup("none") );
2897 else
2899 psz_code = LanguageGetCode( psz_parser );
2900 if( strcmp( psz_code, "??" ) )
2902 TAB_APPEND( i_psz, ppsz, psz_code );
2904 else
2906 free( psz_code );
2910 psz_parser = psz;
2913 if( i_psz )
2915 TAB_APPEND( i_psz, ppsz, NULL );
2918 free( psz_dup );
2919 return ppsz;
2922 static int LanguageArrayIndex( char **ppsz_langs, const char *psz_lang )
2924 if( !ppsz_langs || !psz_lang )
2925 return -1;
2927 for( int i = 0; ppsz_langs[i]; i++ )
2929 if( !strcasecmp( ppsz_langs[i], psz_lang ) ||
2930 ( !strcasecmp( ppsz_langs[i], "any" ) && strcasecmp( psz_lang, "none") ) )
2931 return i;
2932 if( !strcasecmp( ppsz_langs[i], "none" ) )
2933 break;
2936 return -1;
2939 /****************************************************************************
2940 * EsOutUpdateInfo:
2941 * - add meta info to the playlist item
2942 ****************************************************************************/
2943 static void EsOutUpdateInfo( es_out_t *out, es_out_id_t *es, const es_format_t *fmt, const vlc_meta_t *p_meta )
2945 es_out_sys_t *p_sys = out->p_sys;
2946 input_thread_t *p_input = p_sys->p_input;
2947 const es_format_t *p_fmt_es = &es->fmt;
2948 lldiv_t div;
2950 if( es->fmt.i_cat == fmt->i_cat )
2952 es_format_t update = *fmt;
2953 update.i_id = es->i_meta_id;
2954 update.i_codec = es->fmt.i_codec;
2955 update.i_original_fourcc = es->fmt.i_original_fourcc;
2957 /* Update infos that could have been lost by the decoder (no need to
2958 * dup them since input_item_UpdateTracksInfo() will do it). */
2959 if (update.psz_language == NULL)
2960 update.psz_language = es->fmt.psz_language;
2961 if (update.psz_description == NULL)
2962 update.psz_description = es->fmt.psz_description;
2963 if (update.subs.psz_encoding == NULL)
2964 update.subs.psz_encoding = es->fmt.subs.psz_encoding;
2965 if (update.subs.p_style == NULL)
2966 update.subs.p_style = es->fmt.subs.p_style;
2967 if (update.i_extra_languages == 0)
2969 assert(update.p_extra_languages == NULL);
2970 update.i_extra_languages = es->fmt.i_extra_languages;
2971 update.p_extra_languages = es->fmt.p_extra_languages;
2974 /* No need to update codec specific data */
2975 update.i_extra = 0;
2976 update.p_extra = NULL;
2978 input_item_UpdateTracksInfo(input_GetItem(p_input), &update);
2981 /* Create category */
2982 char* psz_cat = EsInfoCategoryName( es );
2984 if( unlikely( !psz_cat ) )
2985 return;
2987 info_category_t* p_cat = info_category_New( psz_cat );
2989 free( psz_cat );
2991 if( unlikely( !p_cat ) )
2992 return;
2994 /* Add information */
2995 const char *psz_type;
2996 switch( fmt->i_cat )
2998 case AUDIO_ES:
2999 psz_type = _("Audio");
3000 break;
3001 case VIDEO_ES:
3002 psz_type = _("Video");
3003 break;
3004 case SPU_ES:
3005 psz_type = _("Subtitle");
3006 break;
3007 default:
3008 psz_type = NULL;
3009 break;
3012 if( psz_type )
3013 info_category_AddInfo( p_cat, _("Type"), "%s", psz_type );
3015 if( es->i_meta_id != es->i_id )
3016 info_category_AddInfo( p_cat, _("Original ID"),
3017 "%d", es->i_id );
3019 const char *psz_codec_description =
3020 vlc_fourcc_GetDescription( p_fmt_es->i_cat, p_fmt_es->i_codec );
3021 const vlc_fourcc_t i_codec_fourcc = ( p_fmt_es->i_original_fourcc )?
3022 p_fmt_es->i_original_fourcc : p_fmt_es->i_codec;
3023 if( psz_codec_description && *psz_codec_description )
3024 info_category_AddInfo( p_cat, _("Codec"), "%s (%.4s)",
3025 psz_codec_description, (char*)&i_codec_fourcc );
3026 else if ( i_codec_fourcc != VLC_FOURCC(0,0,0,0) )
3027 info_category_AddInfo( p_cat, _("Codec"), "%.4s",
3028 (char*)&i_codec_fourcc );
3030 if( es->psz_language && *es->psz_language )
3031 info_category_AddInfo( p_cat, _("Language"), "%s",
3032 es->psz_language );
3033 if( fmt->psz_description && *fmt->psz_description )
3034 info_category_AddInfo( p_cat, _("Description"), "%s",
3035 fmt->psz_description );
3037 switch( fmt->i_cat )
3039 case AUDIO_ES:
3040 info_category_AddInfo( p_cat, _("Type"), _("Audio") );
3042 if( fmt->audio.i_physical_channels )
3043 info_category_AddInfo( p_cat, _("Channels"), "%s",
3044 _( aout_FormatPrintChannels( &fmt->audio ) ) );
3046 if( fmt->audio.i_rate != 0 )
3048 info_category_AddInfo( p_cat, _("Sample rate"), _("%u Hz"),
3049 fmt->audio.i_rate );
3050 /* FIXME that should be removed or improved ! (used by text/strings.c) */
3051 var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate );
3054 unsigned int i_bitspersample = fmt->audio.i_bitspersample;
3055 if( i_bitspersample == 0 )
3056 i_bitspersample = aout_BitsPerSample( p_fmt_es->i_codec );
3057 if( i_bitspersample != 0 )
3058 info_category_AddInfo( p_cat, _("Bits per sample"), "%u",
3059 i_bitspersample );
3061 if( fmt->i_bitrate != 0 )
3063 info_category_AddInfo( p_cat, _("Bitrate"), _("%u kb/s"),
3064 fmt->i_bitrate / 1000 );
3065 /* FIXME that should be removed or improved ! (used by text/strings.c) */
3066 var_SetInteger( p_input, "bit-rate", fmt->i_bitrate );
3068 for( int i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
3070 const audio_replay_gain_t *p_rg = &fmt->audio_replay_gain;
3071 if( !p_rg->pb_gain[i] )
3072 continue;
3073 const char *psz_name;
3074 if( i == AUDIO_REPLAY_GAIN_TRACK )
3075 psz_name = _("Track replay gain");
3076 else
3077 psz_name = _("Album replay gain");
3078 info_category_AddInfo( p_cat, psz_name, _("%.2f dB"),
3079 p_rg->pf_gain[i] );
3081 break;
3083 case VIDEO_ES:
3084 info_category_AddInfo( p_cat, _("Type"), _("Video") );
3086 if( fmt->video.i_visible_width > 0 &&
3087 fmt->video.i_visible_height > 0 )
3088 info_category_AddInfo( p_cat, _("Video resolution"), "%ux%u",
3089 fmt->video.i_visible_width,
3090 fmt->video.i_visible_height);
3092 if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
3093 info_category_AddInfo( p_cat, _("Buffer dimensions"), "%ux%u",
3094 fmt->video.i_width, fmt->video.i_height );
3096 if( fmt->video.i_frame_rate > 0 &&
3097 fmt->video.i_frame_rate_base > 0 )
3099 div = lldiv( (float)fmt->video.i_frame_rate /
3100 fmt->video.i_frame_rate_base * 1000000,
3101 1000000 );
3102 if( div.rem > 0 )
3103 info_category_AddInfo( p_cat, _("Frame rate"), "%"PRId64".%06u",
3104 div.quot, (unsigned int )div.rem );
3105 else
3106 info_category_AddInfo( p_cat, _("Frame rate"), "%"PRId64,
3107 div.quot );
3109 if( fmt->i_codec != p_fmt_es->i_codec )
3111 const char *psz_chroma_description =
3112 vlc_fourcc_GetDescription( VIDEO_ES, fmt->i_codec );
3113 if( psz_chroma_description )
3114 info_category_AddInfo( p_cat, _("Decoded format"), "%s",
3115 psz_chroma_description );
3118 static const char orient_names[][13] = {
3119 N_("Top left"), N_("Left top"),
3120 N_("Right bottom"), N_("Top right"),
3121 N_("Bottom left"), N_("Bottom right"),
3122 N_("Left bottom"), N_("Right top"),
3124 info_category_AddInfo( p_cat, _("Orientation"), "%s",
3125 _(orient_names[fmt->video.orientation]) );
3127 if( fmt->video.primaries != COLOR_PRIMARIES_UNDEF )
3129 static const char *primaries_names[] = { N_("Undefined"),
3130 N_("ITU-R BT.601 (525 lines, 60 Hz)"),
3131 N_("ITU-R BT.601 (625 lines, 50 Hz)"),
3132 "ITU-R BT.709",
3133 "ITU-R BT.2020",
3134 "DCI/P3 D65",
3135 "ITU-R BT.470 M",
3137 if( fmt->video.primaries < ARRAY_SIZE(primaries_names) )
3138 info_category_AddInfo( p_cat, _("Color primaries"), "%s",
3139 _(primaries_names[fmt->video.primaries]) );
3141 if( fmt->video.transfer != TRANSFER_FUNC_UNDEF )
3143 static const char *func_names[] = { N_("Undefined"),
3144 N_("Linear"),
3145 "sRGB",
3146 "ITU-R BT.470 BG",
3147 "ITU-R BT.470 M",
3148 "ITU-R BT.709, ITU-R BT.2020",
3149 "SMPTE ST2084",
3150 "SMPTE 240M",
3151 "Hybrid Log-Gamma",
3153 if( fmt->video.transfer < ARRAY_SIZE(func_names) )
3154 info_category_AddInfo( p_cat, _("Color transfer function"), "%s",
3155 _(func_names[fmt->video.transfer]) );
3157 if( fmt->video.space != COLOR_SPACE_UNDEF )
3159 static const char *space_names[] = { N_("Undefined"),
3160 "ITU-R BT.601",
3161 "ITU-R BT.709",
3162 "ITU-R BT.2020",
3164 static const char *range_names[] = {
3165 N_("Limited Range"),
3166 N_("Full Range"),
3168 if( fmt->video.space < ARRAY_SIZE(space_names) )
3169 info_category_AddInfo( p_cat, _("Color space"), "%s %s",
3170 _(space_names[fmt->video.space]),
3171 _(range_names[fmt->video.b_color_range_full]) );
3173 if( fmt->video.chroma_location != CHROMA_LOCATION_UNDEF )
3175 static const char *c_loc_names[] = { N_("Undefined"),
3176 N_("Left"),
3177 N_("Center"),
3178 N_("Top Left"),
3179 N_("Top Center"),
3180 N_("Bottom Left"),
3181 N_("Bottom Center"),
3183 info_category_AddInfo( p_cat, _("Chroma location"), "%s",
3184 _(c_loc_names[fmt->video.chroma_location]) );
3186 if( fmt->video.projection_mode != PROJECTION_MODE_RECTANGULAR )
3188 const char *psz_loc_name = NULL;
3189 switch (fmt->video.projection_mode)
3191 case PROJECTION_MODE_RECTANGULAR:
3192 psz_loc_name = N_("Rectangular");
3193 break;
3194 case PROJECTION_MODE_EQUIRECTANGULAR:
3195 psz_loc_name = N_("Equirectangular");
3196 break;
3197 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
3198 psz_loc_name = N_("Cubemap");
3199 break;
3200 default:
3201 vlc_assert_unreachable();
3202 break;
3204 info_category_AddInfo( p_cat, _("Projection"), "%s", _(psz_loc_name) );
3206 info_category_AddInfo( p_cat, _("Yaw"), "%.2f",
3207 fmt->video.pose.f_yaw_degrees );
3208 info_category_AddInfo( p_cat, _("Pitch"), "%.2f",
3209 fmt->video.pose.f_pitch_degrees );
3210 info_category_AddInfo( p_cat, _("Roll"), "%.2f",
3211 fmt->video.pose.f_roll_degrees );
3212 info_category_AddInfo( p_cat, _("Field of view"), "%.2f",
3213 fmt->video.pose.f_fov_degrees );
3215 if ( fmt->video.mastering.max_luminance )
3217 info_category_AddInfo( p_cat, _("Max luminance"), "%.4f cd/m²",
3218 fmt->video.mastering.max_luminance / 10000.f );
3220 if ( fmt->video.mastering.min_luminance )
3222 info_category_AddInfo( p_cat, _("Min luminance"), "%.4f cd/m²",
3223 fmt->video.mastering.min_luminance / 10000.f );
3225 if ( fmt->video.mastering.primaries[4] &&
3226 fmt->video.mastering.primaries[5] )
3228 float x = (float)fmt->video.mastering.primaries[4] / 50000.f;
3229 float y = (float)fmt->video.mastering.primaries[5] / 50000.f;
3230 info_category_AddInfo( p_cat, _("Primary R"), "x=%.4f y=%.4f", x, y );
3232 if ( fmt->video.mastering.primaries[0] &&
3233 fmt->video.mastering.primaries[1] )
3235 float x = (float)fmt->video.mastering.primaries[0] / 50000.f;
3236 float y = (float)fmt->video.mastering.primaries[1] / 50000.f;
3237 info_category_AddInfo( p_cat, _("Primary G"), "x=%.4f y=%.4f", x, y );
3239 if ( fmt->video.mastering.primaries[2] &&
3240 fmt->video.mastering.primaries[3] )
3242 float x = (float)fmt->video.mastering.primaries[2] / 50000.f;
3243 float y = (float)fmt->video.mastering.primaries[3] / 50000.f;
3244 info_category_AddInfo( p_cat, _("Primary B"), "x=%.4f y=%.4f", x, y );
3246 if ( fmt->video.mastering.white_point[0] &&
3247 fmt->video.mastering.white_point[1] )
3249 float x = (float)fmt->video.mastering.white_point[0] / 50000.f;
3250 float y = (float)fmt->video.mastering.white_point[1] / 50000.f;
3251 info_category_AddInfo( p_cat, _("White point"), "x=%.4f y=%.4f", x, y );
3253 if ( fmt->video.lighting.MaxCLL )
3255 info_category_AddInfo( p_cat, _("MaxCLL"), "%d cd/m²",
3256 fmt->video.lighting.MaxCLL );
3258 if ( fmt->video.lighting.MaxFALL )
3260 info_category_AddInfo( p_cat, _("MaxFALL"), "%d cd/m²",
3261 fmt->video.lighting.MaxFALL );
3263 break;
3265 case SPU_ES:
3266 info_category_AddInfo( p_cat, _("Type"), _("Subtitle") );
3267 break;
3269 default:
3270 break;
3273 /* Append generic meta */
3274 if( p_meta )
3276 char **ppsz_all_keys = vlc_meta_CopyExtraNames( p_meta );
3277 for( int i = 0; ppsz_all_keys && ppsz_all_keys[i]; i++ )
3279 char *psz_key = ppsz_all_keys[i];
3280 const char *psz_value = vlc_meta_GetExtra( p_meta, psz_key );
3282 if( psz_value )
3283 info_category_AddInfo( p_cat, vlc_gettext(psz_key), "%s",
3284 vlc_gettext(psz_value) );
3285 free( psz_key );
3287 free( ppsz_all_keys );
3289 /* */
3290 input_Control( p_input, INPUT_REPLACE_INFOS, p_cat );
3293 static void EsDeleteInfo( es_out_t *out, es_out_id_t *es )
3295 char* psz_info_category;
3297 if( likely( psz_info_category = EsInfoCategoryName( es ) ) )
3299 input_Control( out->p_sys->p_input, INPUT_DEL_INFO,
3300 psz_info_category, NULL );
3302 free( psz_info_category );