1 /*****************************************************************************
2 * es_out.c: Es Out handler for input.
3 *****************************************************************************
4 * Copyright (C) 2003-2004 the VideoLAN team
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
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
34 #include <vlc_common.h>
36 #include <vlc_input.h>
37 #include <vlc_es_out.h>
38 #include <vlc_block.h>
41 #include "input_internal.h"
46 #include "../stream_output/stream_output.h"
48 #include <vlc_iso_lang.h>
49 /* FIXME we should find a better way than including that */
50 #include "../text/iso-639_def.h"
52 /*****************************************************************************
54 *****************************************************************************/
60 /* Number of es for this pgrm */
65 /* Clock for this program */
66 input_clock_t *p_clock;
69 char *psz_now_playing;
79 es_out_pgrm_t *p_pgrm;
81 /* Channel in the track type */
85 char *psz_language_code;
88 decoder_t *p_dec_record;
90 /* Fields for Video with CC */
91 bool pb_cc_present[4];
92 es_out_id_t *pp_cc_es[4];
94 /* Field for CC track from a master video */
95 es_out_id_t *p_master;
100 input_thread_t *p_input;
107 es_out_pgrm_t **pgrm;
108 es_out_pgrm_t **pp_selected_pgrm; /* --programs */
109 es_out_pgrm_t *p_pgrm; /* Master program */
126 int i_audio_last, i_audio_id;
127 int i_sub_last, i_sub_id;
128 int i_default_sub_id; /* As specified in container; if applicable */
129 char **ppsz_audio_language;
130 char **ppsz_sub_language;
132 /* current main es */
133 es_out_id_t *p_es_audio;
134 es_out_id_t *p_es_video;
135 es_out_id_t *p_es_sub;
138 int64_t i_audio_delay;
141 /* Rate used for clock */
147 /* Current preroll */
148 mtime_t i_preroll_end;
150 /* Used for buffering */
152 mtime_t i_buffering_extra_initial;
153 mtime_t i_buffering_extra_stream;
154 mtime_t i_buffering_extra_system;
157 sout_instance_t *p_sout_record;
160 static es_out_id_t *EsOutAdd ( es_out_t *, const es_format_t * );
161 static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * );
162 static void EsOutDel ( es_out_t *, es_out_id_t * );
163 static int EsOutControl( es_out_t *, int i_query, va_list );
164 static void EsOutDelete ( es_out_t * );
166 static void EsOutSelect( es_out_t *, es_out_id_t *es, bool b_force );
167 static void EsOutAddInfo( es_out_t *, es_out_id_t *es );
168 static int EsOutSetRecord( es_out_t *, bool b_record );
170 static bool EsIsSelected( es_out_id_t *es );
171 static void EsSelect( es_out_t *out, es_out_id_t *es );
172 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
173 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
174 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
175 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
176 static void EsOutProgramsChangeRate( es_out_t *out );
177 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
178 static char *LanguageGetName( const char *psz_code );
179 static char *LanguageGetCode( const char *psz_lang );
180 static char **LanguageSplit( const char *psz_langs );
181 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );
183 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm );
185 static const vlc_fourcc_t EsOutFourccClosedCaptions[4] = {
186 VLC_FOURCC('c', 'c', '1', ' '),
187 VLC_FOURCC('c', 'c', '2', ' '),
188 VLC_FOURCC('c', 'c', '3', ' '),
189 VLC_FOURCC('c', 'c', '4', ' '),
191 static inline int EsOutGetClosedCaptionsChannel( vlc_fourcc_t fcc )
194 for( i = 0; i < 4; i++ )
196 if( fcc == EsOutFourccClosedCaptions[i] )
203 /*****************************************************************************
205 *****************************************************************************/
206 es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
211 es_out_t *out = malloc( sizeof( *out ) );
215 es_out_sys_t *p_sys = malloc( sizeof( *p_sys ) );
222 out->pf_add = EsOutAdd;
223 out->pf_send = EsOutSend;
224 out->pf_del = EsOutDel;
225 out->pf_control = EsOutControl;
226 out->pf_destroy = EsOutDelete;
228 out->b_sout = p_input->p->p_sout != NULL;
231 vlc_mutex_init_recursive( &p_sys->lock );
232 p_sys->p_input = p_input;
234 p_sys->b_active = false;
235 p_sys->i_mode = ES_OUT_MODE_AUTO;
238 TAB_INIT( p_sys->i_pgrm, p_sys->pgrm );
239 p_sys->p_pgrm = NULL;
243 TAB_INIT( p_sys->i_es, p_sys->es );
250 var_Get( p_input, "audio-track", &val );
251 p_sys->i_audio_last = val.i_int;
253 var_Get( p_input, "sub-track", &val );
254 p_sys->i_sub_last = val.i_int;
256 p_sys->i_default_sub_id = -1;
258 if( !p_input->b_preparsing )
260 var_Get( p_input, "audio-language", &val );
261 p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);
262 if( p_sys->ppsz_audio_language )
264 for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
265 msg_Dbg( p_input, "selected audio language[%d] %s",
266 i, p_sys->ppsz_audio_language[i] );
268 free( val.psz_string );
270 var_Get( p_input, "sub-language", &val );
271 p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);
272 if( p_sys->ppsz_sub_language )
274 for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
275 msg_Dbg( p_input, "selected subtitle language[%d] %s",
276 i, p_sys->ppsz_sub_language[i] );
278 free( val.psz_string );
282 p_sys->ppsz_sub_language = NULL;
283 p_sys->ppsz_audio_language = NULL;
286 var_Get( p_input, "audio-track-id", &val );
287 p_sys->i_audio_id = val.i_int;
289 var_Get( p_input, "sub-track-id", &val );
290 p_sys->i_sub_id = val.i_int;
292 p_sys->p_es_audio = NULL;
293 p_sys->p_es_video = NULL;
294 p_sys->p_es_sub = NULL;
296 p_sys->i_audio_delay= 0;
297 p_sys->i_spu_delay = 0;
299 p_sys->b_paused = false;
301 p_sys->i_rate = i_rate;
303 p_sys->b_buffering = true;
304 p_sys->i_buffering_extra_initial = 0;
305 p_sys->i_buffering_extra_stream = 0;
306 p_sys->i_buffering_extra_system = 0;
307 p_sys->i_preroll_end = -1;
309 p_sys->p_sout_record = NULL;
314 /*****************************************************************************
316 *****************************************************************************/
317 static void EsOutDelete( es_out_t *out )
319 es_out_sys_t *p_sys = out->p_sys;
322 if( p_sys->p_sout_record )
323 EsOutSetRecord( out, false );
325 for( i = 0; i < p_sys->i_es; i++ )
327 if( p_sys->es[i]->p_dec )
328 input_DecoderDelete( p_sys->es[i]->p_dec );
330 free( p_sys->es[i]->psz_language );
331 free( p_sys->es[i]->psz_language_code );
332 es_format_Clean( &p_sys->es[i]->fmt );
334 free( p_sys->es[i] );
336 if( p_sys->ppsz_audio_language )
338 for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
339 free( p_sys->ppsz_audio_language[i] );
340 free( p_sys->ppsz_audio_language );
342 if( p_sys->ppsz_sub_language )
344 for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
345 free( p_sys->ppsz_sub_language[i] );
346 free( p_sys->ppsz_sub_language );
350 /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
351 for( i = 0; i < p_sys->i_pgrm; i++ )
353 es_out_pgrm_t *p_pgrm = p_sys->pgrm[i];
354 input_clock_Delete( p_pgrm->p_clock );
355 free( p_pgrm->psz_now_playing );
356 free( p_pgrm->psz_publisher );
357 free( p_pgrm->psz_name );
359 vlc_epg_Delete( p_pgrm->p_epg );
363 TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm );
364 vlc_mutex_destroy( &p_sys->lock );
370 static mtime_t EsOutGetWakeup( es_out_t *out )
372 es_out_sys_t *p_sys = out->p_sys;
373 input_thread_t *p_input = p_sys->p_input;
378 /* We do not have a wake up date if the input cannot have its speed
379 * controlled or sout is imposing its own or while buffering
381 * FIXME for !p_input->b_can_pace_control a wkeup time is still needed to avoid too strong buffering */
382 if( !p_input->b_can_pace_control ||
383 p_input->p->b_out_pace_control ||
387 return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
390 static es_out_id_t *EsOutGetFromID( es_out_t *out, int i_id )
395 /* Special HACK, -i_id is the cat of the stream */
396 return (es_out_id_t*)((uint8_t*)NULL-i_id);
399 for( i = 0; i < out->p_sys->i_es; i++ )
401 if( out->p_sys->es[i]->i_id == i_id )
402 return out->p_sys->es[i];
407 static bool EsOutDecodersIsEmpty( es_out_t *out )
409 es_out_sys_t *p_sys = out->p_sys;
412 if( p_sys->b_buffering && p_sys->p_pgrm )
414 EsOutDecodersStopBuffering( out, true );
415 if( p_sys->b_buffering )
419 for( i = 0; i < p_sys->i_es; i++ )
421 es_out_id_t *es = p_sys->es[i];
423 if( es->p_dec && !input_DecoderIsEmpty( es->p_dec ) )
425 if( es->p_dec_record && !input_DecoderIsEmpty( es->p_dec_record ) )
431 static void EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
433 es_out_sys_t *p_sys = out->p_sys;
435 if( i_cat == AUDIO_ES )
436 p_sys->i_audio_delay = i_delay;
437 else if( i_cat == SPU_ES )
438 p_sys->i_spu_delay = i_delay;
440 for( int i = 0; i < p_sys->i_es; i++ )
441 EsOutDecoderChangeDelay( out, p_sys->es[i] );
444 static int EsOutSetRecord( es_out_t *out, bool b_record )
446 es_out_sys_t *p_sys = out->p_sys;
447 input_thread_t *p_input = p_sys->p_input;
449 assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
453 char *psz_path = var_CreateGetString( p_input, "input-record-path" );
454 if( !psz_path || *psz_path == '\0' )
457 psz_path = strdup( config_GetHomeDir() );
460 char *psz_sout = NULL; // TODO conf
462 if( !psz_sout && psz_path )
464 char *psz_file = input_CreateFilename( VLC_OBJECT(p_input), psz_path, INPUT_RECORD_PREFIX, NULL );
467 if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file ) < 0 )
478 p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
482 if( !p_sys->p_sout_record )
485 for( int i = 0; i < p_sys->i_es; i++ )
487 es_out_id_t *p_es = p_sys->es[i];
489 if( !p_es->p_dec || p_es->p_master )
492 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
493 if( p_es->p_dec_record && p_sys->b_buffering )
494 input_DecoderStartBuffering( p_es->p_dec_record );
499 for( int i = 0; i < p_sys->i_es; i++ )
501 es_out_id_t *p_es = p_sys->es[i];
503 if( !p_es->p_dec_record )
506 input_DecoderDelete( p_es->p_dec_record );
507 p_es->p_dec_record = NULL;
510 sout_DeleteInstance( p_sys->p_sout_record );
512 p_sys->p_sout_record = NULL;
517 static void EsOutChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
519 es_out_sys_t *p_sys = out->p_sys;
521 /* XXX the order is important */
524 EsOutDecodersChangePause( out, true, i_date );
525 EsOutProgramChangePause( out, true, i_date );
529 if( p_sys->i_buffering_extra_initial > 0 )
531 mtime_t i_stream_start;
532 mtime_t i_system_start;
533 mtime_t i_stream_duration;
534 mtime_t i_system_duration;
536 i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
537 &i_stream_start, &i_system_start,
538 &i_stream_duration, &i_system_duration );
541 /* FIXME pcr != exactly what wanted */
542 const mtime_t i_used = /*(i_stream_duration - p_sys->p_input->i_pts_delay)*/ p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
545 p_sys->i_buffering_extra_initial = 0;
546 p_sys->i_buffering_extra_stream = 0;
547 p_sys->i_buffering_extra_system = 0;
549 EsOutProgramChangePause( out, false, i_date );
550 EsOutDecodersChangePause( out, false, i_date );
552 EsOutProgramsChangeRate( out );
554 p_sys->b_paused = b_paused;
557 static void EsOutChangeRate( es_out_t *out, int i_rate )
559 es_out_sys_t *p_sys = out->p_sys;
561 p_sys->i_rate = i_rate;
563 if( !p_sys->b_paused )
564 EsOutProgramsChangeRate( out );
567 static void EsOutChangePosition( es_out_t *out )
569 es_out_sys_t *p_sys = out->p_sys;
571 for( int i = 0; i < p_sys->i_es; i++ )
573 es_out_id_t *p_es = p_sys->es[i];
578 input_DecoderStartBuffering( p_es->p_dec );
580 if( p_es->p_dec_record )
581 input_DecoderStartBuffering( p_es->p_dec );
584 for( int i = 0; i < p_sys->i_pgrm; i++ )
585 input_clock_Reset( p_sys->pgrm[i]->p_clock );
587 p_sys->b_buffering = true;
588 p_sys->i_buffering_extra_initial = 0;
589 p_sys->i_buffering_extra_stream = 0;
590 p_sys->i_buffering_extra_system = 0;
591 p_sys->i_preroll_end = -1;
596 static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
598 es_out_sys_t *p_sys = out->p_sys;
601 mtime_t i_stream_start;
602 mtime_t i_system_start;
603 mtime_t i_stream_duration;
604 mtime_t i_system_duration;
605 i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
606 &i_stream_start, &i_system_start,
607 &i_stream_duration, &i_system_duration );
608 assert( !i_ret || b_forced );
612 mtime_t i_preroll_duration = 0;
613 if( p_sys->i_preroll_end >= 0 )
614 i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
616 const mtime_t i_buffering_duration = p_sys->p_input->i_pts_delay +
618 p_sys->i_buffering_extra_stream;
620 if( i_stream_duration <= i_buffering_duration && !b_forced )
622 msg_Dbg( p_sys->p_input, "Buffering %d%%",
623 (int)(100 * i_stream_duration / i_buffering_duration ) );
627 msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
628 (int)(i_stream_duration/1000), (int)(i_system_duration/1000) );
629 p_sys->b_buffering = false;
630 p_sys->i_preroll_end = -1;
632 if( p_sys->i_buffering_extra_initial > 0 )
638 const mtime_t i_decoder_buffering_start = mdate();
639 for( int i = 0; i < p_sys->i_es; i++ )
641 es_out_id_t *p_es = p_sys->es[i];
645 input_DecoderWaitBuffering( p_es->p_dec );
646 if( p_es->p_dec_record )
647 input_DecoderWaitBuffering( p_es->p_dec_record );
650 msg_Dbg( p_sys->p_input, "Decoder buffering done in %d ms",
651 (int)(mdate() - i_decoder_buffering_start)/1000 );
653 const mtime_t i_ts_delay = 10*1000 + /* FIXME CLEANUP thread wake up time*/
655 //msg_Dbg( p_sys->p_input, "==> %lld", i_ts_delay - p_sys->p_input->i_pts_delay );
656 input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, i_ts_delay - i_buffering_duration );
658 for( int i = 0; i < p_sys->i_es; i++ )
660 es_out_id_t *p_es = p_sys->es[i];
665 input_DecoderStopBuffering( p_es->p_dec );
666 if( p_es->p_dec_record )
667 input_DecoderStopBuffering( p_es->p_dec );
670 static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
672 es_out_sys_t *p_sys = out->p_sys;
674 /* Pause decoders first */
675 for( int i = 0; i < p_sys->i_es; i++ )
677 es_out_id_t *es = p_sys->es[i];
681 input_DecoderChangePause( es->p_dec, b_paused, i_date );
682 if( es->p_dec_record )
683 input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
687 static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
689 es_out_sys_t *p_sys = out->p_sys;
691 for( int i = 0; i < p_sys->i_pgrm; i++ )
692 input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date );
695 static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
697 es_out_sys_t *p_sys = out->p_sys;
700 if( p_es->fmt.i_cat == AUDIO_ES )
701 i_delay = p_sys->i_audio_delay;
702 else if( p_es->fmt.i_cat == SPU_ES )
703 i_delay = p_sys->i_spu_delay;
708 input_DecoderChangeDelay( p_es->p_dec, i_delay );
709 if( p_es->p_dec_record )
710 input_DecoderChangeDelay( p_es->p_dec_record, i_delay );
713 static void EsOutProgramsChangeRate( es_out_t *out )
715 es_out_sys_t *p_sys = out->p_sys;
717 for( int i = 0; i < p_sys->i_pgrm; i++ )
718 input_clock_ChangeRate( p_sys->pgrm[i]->p_clock, p_sys->i_rate );
721 static void EsOutFrameNext( es_out_t *out )
723 es_out_sys_t *p_sys = out->p_sys;
724 es_out_id_t *p_es_video = NULL;
726 if( p_sys->b_buffering )
728 msg_Warn( p_sys->p_input, "buffering, ignoring 'frame next'" );
732 assert( p_sys->b_paused );
734 for( int i = 0; i < p_sys->i_es; i++ )
736 es_out_id_t *p_es = p_sys->es[i];
738 if( p_es->fmt.i_cat == VIDEO_ES && p_es->p_dec )
747 msg_Warn( p_sys->p_input, "No video track selected, ignoring 'frame next'" );
752 input_DecoderFrameNext( p_es_video->p_dec, &i_duration );
754 msg_Dbg( out->p_sys->p_input, "EsOutFrameNext consummed %d ms", (int)(i_duration/1000) );
756 if( i_duration <= 0 )
757 i_duration = 40*1000;
759 /* FIXME it is not a clean way ? */
760 if( p_sys->i_buffering_extra_initial <= 0 )
762 mtime_t i_stream_start;
763 mtime_t i_system_start;
764 mtime_t i_stream_duration;
765 mtime_t i_system_duration;
768 i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
769 &i_stream_start, &i_system_start,
770 &i_stream_duration, &i_system_duration );
774 p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->p_input->i_pts_delay; /* FIXME < 0 ? */
775 p_sys->i_buffering_extra_system =
776 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
779 const int i_rate = input_clock_GetRate( p_sys->p_pgrm->p_clock );
781 p_sys->b_buffering = true;
782 p_sys->i_buffering_extra_system += i_duration;
783 p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial +
784 ( p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial ) *
785 INPUT_RATE_DEFAULT / i_rate;
787 p_sys->i_preroll_end = -1;
792 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
795 es_out_sys_t *p_sys = out->p_sys;
796 input_thread_t *p_input = p_sys->p_input;
797 const bool b_teletext = fmt->i_cat == SPU_ES && fmt->i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' );
798 vlc_value_t val, text;
802 if( fmt->i_cat == AUDIO_ES )
803 psz_var = "audio-es";
804 else if( fmt->i_cat == VIDEO_ES )
805 psz_var = "video-es";
806 else if( fmt->i_cat == SPU_ES )
814 var_SetInteger( p_sys->p_input, "teletext-es", -1 );
817 var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
819 var_SetBool( p_sys->p_input, "intf-change", true );
823 /* Get the number of ES already added */
824 var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
829 /* First one, we need to add the "Disable" choice */
830 val2.i_int = -1; text.psz_string = _("Disable");
831 var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
835 /* Take care of the ES description */
836 if( fmt->psz_description && *fmt->psz_description )
838 if( psz_language && *psz_language )
840 text.psz_string = malloc( strlen( fmt->psz_description) +
841 strlen( psz_language ) + 10 );
842 sprintf( text.psz_string, "%s - [%s]", fmt->psz_description,
845 else text.psz_string = strdup( fmt->psz_description );
849 if( psz_language && *psz_language )
851 if( asprintf( &text.psz_string, "%s %i - [%s]", _( "Track" ), val.i_int, psz_language ) == -1 )
852 text.psz_string = NULL;
856 if( asprintf( &text.psz_string, "%s %i", _( "Track" ), val.i_int ) == -1 )
857 text.psz_string = NULL;
862 var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
864 free( text.psz_string );
868 if( var_GetInteger( p_sys->p_input, "teletext-es" ) < 0 )
869 var_SetInteger( p_sys->p_input, "teletext-es", i_id );
872 var_SetBool( p_sys->p_input, "intf-change", true );
875 static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
878 EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete );
881 /* EsOutProgramSelect:
882 * Select a program and update the object variable
884 static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
886 es_out_sys_t *p_sys = out->p_sys;
887 input_thread_t *p_input = p_sys->p_input;
891 if( p_sys->p_pgrm == p_pgrm )
892 return; /* Nothing to do */
896 es_out_pgrm_t *old = p_sys->p_pgrm;
897 msg_Dbg( p_input, "unselecting program id=%d", old->i_id );
899 for( i = 0; i < p_sys->i_es; i++ )
901 if( p_sys->es[i]->p_pgrm == old && EsIsSelected( p_sys->es[i] ) &&
902 p_sys->i_mode != ES_OUT_MODE_ALL )
903 EsUnselect( out, p_sys->es[i], true );
906 p_sys->p_es_audio = NULL;
907 p_sys->p_es_sub = NULL;
908 p_sys->p_es_video = NULL;
911 msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id );
913 /* Mark it selected */
914 p_pgrm->b_selected = true;
916 /* Switch master stream */
917 p_sys->p_pgrm = p_pgrm;
919 /* Update "program" */
920 val.i_int = p_pgrm->i_id;
921 var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
924 var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
925 var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
926 var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
927 var_SetInteger( p_input, "teletext-es", -1 );
928 for( i = 0; i < p_sys->i_es; i++ )
930 if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
931 EsOutESVarUpdate( out, p_sys->es[i], false );
932 EsOutSelect( out, p_sys->es[i], false );
935 /* Update now playing */
936 input_item_SetNowPlaying( p_input->p->input.p_item,
937 p_pgrm->psz_now_playing );
938 input_item_SetPublisher( p_input->p->input.p_item,
939 p_pgrm->psz_publisher );
941 var_SetBool( p_sys->p_input, "intf-change", true );
947 static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
949 es_out_sys_t *p_sys = out->p_sys;
950 input_thread_t *p_input = p_sys->p_input;
953 es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
958 p_pgrm->i_id = i_group;
960 p_pgrm->b_selected = false;
961 p_pgrm->psz_name = NULL;
962 p_pgrm->psz_now_playing = NULL;
963 p_pgrm->psz_publisher = NULL;
964 p_pgrm->p_epg = NULL;
965 p_pgrm->p_clock = input_clock_New( p_input->p->input.i_cr_average, p_sys->i_rate );
966 if( !p_pgrm->p_clock )
973 TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
975 /* Update "program" variable */
977 var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
979 if( i_group == var_GetInteger( p_input, "program" ) )
981 EsOutProgramSelect( out, p_pgrm );
985 var_SetBool( p_sys->p_input, "intf-change", true );
993 static int EsOutProgramDel( es_out_t *out, int i_group )
995 es_out_sys_t *p_sys = out->p_sys;
996 input_thread_t *p_input = p_sys->p_input;
997 es_out_pgrm_t *p_pgrm = NULL;
1001 for( i = 0; i < p_sys->i_pgrm; i++ )
1003 if( p_sys->pgrm[i]->i_id == i_group )
1005 p_pgrm = p_sys->pgrm[i];
1010 if( p_pgrm == NULL )
1011 return VLC_EGENERIC;
1015 msg_Dbg( p_input, "can't delete program %d which still has %i ES",
1016 i_group, p_pgrm->i_es );
1017 return VLC_EGENERIC;
1020 TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
1022 /* If program is selected we need to unselect it */
1023 if( p_sys->p_pgrm == p_pgrm )
1024 p_sys->p_pgrm = NULL;
1026 input_clock_Delete( p_pgrm->p_clock );
1028 free( p_pgrm->psz_name );
1029 free( p_pgrm->psz_now_playing );
1030 free( p_pgrm->psz_publisher );
1032 vlc_epg_Delete( p_pgrm->p_epg );
1035 /* Update "program" variable */
1036 val.i_int = i_group;
1037 var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
1039 var_SetBool( p_sys->p_input, "intf-change", true );
1044 /* EsOutProgramMeta:
1046 static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm )
1049 if( p_pgrm->psz_name )
1051 if( asprintf( &psz, _("%s [%s %d]"), p_pgrm->psz_name, _("Program"), p_pgrm->i_id ) == -1 )
1056 if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 )
1062 static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta )
1064 es_out_sys_t *p_sys = out->p_sys;
1065 es_out_pgrm_t *p_pgrm = NULL;
1066 input_thread_t *p_input = p_sys->p_input;
1068 const char *psz_title = NULL;
1069 const char *psz_provider = NULL;
1072 msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group );
1074 /* Check against empty meta data (empty for what we handle) */
1075 if( !vlc_meta_Get( p_meta, vlc_meta_Title) &&
1076 !vlc_meta_Get( p_meta, vlc_meta_NowPlaying) &&
1077 !vlc_meta_Get( p_meta, vlc_meta_Publisher) &&
1078 vlc_dictionary_keys_count( &p_meta->extra_tags ) <= 0 )
1083 for( i = 0; i < p_sys->i_pgrm; i++ )
1085 if( p_sys->pgrm[i]->i_id == i_group )
1087 p_pgrm = p_sys->pgrm[i];
1091 if( p_pgrm == NULL )
1092 p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
1095 psz_title = vlc_meta_Get( p_meta, vlc_meta_Title);
1096 psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher);
1098 /* Update the description text of the program */
1099 if( psz_title && *psz_title )
1104 if( !p_pgrm->psz_name || strcmp( p_pgrm->psz_name, psz_title ) )
1106 char *psz_cat = EsOutProgramGetMetaName( p_pgrm );
1108 /* Remove old entries */
1109 input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL );
1110 /* TODO update epg name */
1113 free( p_pgrm->psz_name );
1114 p_pgrm->psz_name = strdup( psz_title );
1116 /* ugly but it works */
1117 val.i_int = i_group;
1118 var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
1120 if( psz_provider && *psz_provider )
1122 if( asprintf( &text.psz_string, "%s [%s]", psz_title, psz_provider ) != -1 )
1124 var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
1125 free( text.psz_string );
1130 text.psz_string = (char *)psz_title;
1131 var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text );
1135 psz_cat = EsOutProgramGetMetaName( p_pgrm );
1138 if( p_sys->p_pgrm == p_pgrm )
1139 input_item_SetPublisher( p_input->p->input.p_item, psz_provider );
1140 input_Control( p_input, INPUT_ADD_INFO, psz_cat, input_MetaTypeToLocalizedString(vlc_meta_Publisher), psz_provider );
1142 char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags );
1143 for( i = 0; ppsz_all_keys[i]; i++ )
1145 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(ppsz_all_keys[i]),
1146 vlc_dictionary_value_for_key( &p_meta->extra_tags, ppsz_all_keys[i] ) );
1147 free( ppsz_all_keys[i] );
1149 free( ppsz_all_keys );
1154 static void vlc_epg_Merge( vlc_epg_t *p_dst, const vlc_epg_t *p_src )
1159 for( i = 0; i < p_src->i_event; i++ )
1161 vlc_epg_event_t *p_evt = p_src->pp_event[i];
1165 for( j = 0; j < p_dst->i_event; j++ )
1167 if( p_dst->pp_event[j]->i_start == p_evt->i_start && p_dst->pp_event[j]->i_duration == p_evt->i_duration )
1172 if( p_dst->pp_event[j]->i_start > p_evt->i_start )
1177 vlc_epg_event_t *p_copy = malloc( sizeof(vlc_epg_event_t) );
1180 memset( p_copy, 0, sizeof(vlc_epg_event_t) );
1181 p_copy->i_start = p_evt->i_start;
1182 p_copy->i_duration = p_evt->i_duration;
1183 p_copy->psz_name = p_evt->psz_name ? strdup( p_evt->psz_name ) : NULL;
1184 p_copy->psz_short_description = p_evt->psz_short_description ? strdup( p_evt->psz_short_description ) : NULL;
1185 p_copy->psz_description = p_evt->psz_description ? strdup( p_evt->psz_description ) : NULL;
1186 TAB_INSERT( p_dst->i_event, p_dst->pp_event, p_copy, j );
1189 /* Update current */
1190 vlc_epg_SetCurrent( p_dst, p_src->p_current ? p_src->p_current->i_start : -1 );
1192 /* Keep only 1 old event */
1193 if( p_dst->p_current )
1195 while( p_dst->i_event > 1 && p_dst->pp_event[0] != p_dst->p_current && p_dst->pp_event[1] != p_dst->p_current )
1196 TAB_REMOVE( p_dst->i_event, p_dst->pp_event, p_dst->pp_event[0] );
1200 static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg )
1202 es_out_sys_t *p_sys = out->p_sys;
1203 input_thread_t *p_input = p_sys->p_input;
1204 es_out_pgrm_t *p_pgrm = NULL;
1209 for( i = 0; i < p_sys->i_pgrm; i++ )
1211 if( p_sys->pgrm[i]->i_id == i_group )
1213 p_pgrm = p_sys->pgrm[i];
1217 if( p_pgrm == NULL )
1218 p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
1221 if( !p_pgrm->p_epg )
1222 p_pgrm->p_epg = vlc_epg_New( p_pgrm->psz_name );
1223 vlc_epg_Merge( p_pgrm->p_epg, p_epg );
1226 psz_cat = EsOutProgramGetMetaName( p_pgrm );
1227 #ifdef HAVE_LOCALTIME_R
1229 if( asprintf( &psz_epg, "EPG %s", psz_cat ) == -1 )
1231 input_Control( p_input, INPUT_DEL_INFO, psz_epg, NULL );
1232 msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, p_pgrm->p_epg->psz_name );
1233 for( i = 0; i < p_pgrm->p_epg->i_event; i++ )
1235 const vlc_epg_event_t *p_evt = p_pgrm->p_epg->pp_event[i];
1236 time_t t_start = (time_t)p_evt->i_start;
1238 char psz_start[128];
1240 localtime_r( &t_start, &tm_start );
1242 snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d:%2.2d", tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
1243 if( p_evt->psz_short_description || p_evt->psz_description )
1244 input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s",
1246 p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
1247 p_evt->psz_short_description ? p_evt->psz_short_description : p_evt->psz_description );
1249 input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
1251 p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
1255 /* Update now playing */
1256 free( p_pgrm->psz_now_playing );
1257 p_pgrm->psz_now_playing = NULL;
1258 if( p_epg->p_current && p_epg->p_current->psz_name && *p_epg->p_current->psz_name )
1259 p_pgrm->psz_now_playing = strdup( p_epg->p_current->psz_name );
1261 if( p_pgrm == p_sys->p_pgrm )
1262 input_item_SetNowPlaying( p_input->p->input.p_item, p_pgrm->psz_now_playing );
1264 if( p_pgrm->psz_now_playing )
1266 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
1267 input_MetaTypeToLocalizedString(vlc_meta_NowPlaying),
1268 p_pgrm->psz_now_playing );
1272 input_Control( p_input, INPUT_DEL_INFO, psz_cat,
1273 input_MetaTypeToLocalizedString(vlc_meta_NowPlaying) );
1282 static es_out_id_t *EsOutAdd( es_out_t *out, const es_format_t *fmt )
1284 es_out_sys_t *p_sys = out->p_sys;
1285 input_thread_t *p_input = p_sys->p_input;
1287 if( fmt->i_group < 0 )
1289 msg_Err( p_input, "invalid group number" );
1293 es_out_id_t *es = malloc( sizeof( *es ) );
1294 es_out_pgrm_t *p_pgrm = NULL;
1300 vlc_mutex_lock( &p_sys->lock );
1302 /* Search the program */
1303 for( i = 0; i < p_sys->i_pgrm; i++ )
1305 if( fmt->i_group == p_sys->pgrm[i]->i_id )
1307 p_pgrm = p_sys->pgrm[i];
1311 if( p_pgrm == NULL )
1313 /* Create a new one */
1314 p_pgrm = EsOutProgramAdd( out, fmt->i_group );
1317 /* Increase ref count for program */
1321 es->p_pgrm = p_pgrm;
1322 es_format_Copy( &es->fmt, fmt );
1323 if( es->fmt.i_id < 0 )
1324 es->fmt.i_id = out->p_sys->i_id;
1325 es->i_id = fmt->i_id;
1327 switch( es->fmt.i_cat )
1331 audio_replay_gain_t rg;
1333 es->i_channel = p_sys->i_audio;
1335 memset( &rg, 0, sizeof(rg) );
1336 vlc_mutex_lock( &p_input->p->input.p_item->lock );
1337 vlc_audio_replay_gain_MergeFromMeta( &rg, p_input->p->input.p_item->p_meta );
1338 vlc_mutex_unlock( &p_input->p->input.p_item->lock );
1340 for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ )
1342 if( !es->fmt.audio_replay_gain.pb_peak[i] )
1344 es->fmt.audio_replay_gain.pb_peak[i] = rg.pb_peak[i];
1345 es->fmt.audio_replay_gain.pf_peak[i] = rg.pf_peak[i];
1347 if( !es->fmt.audio_replay_gain.pb_gain[i] )
1349 es->fmt.audio_replay_gain.pb_gain[i] = rg.pb_gain[i];
1350 es->fmt.audio_replay_gain.pf_gain[i] = rg.pf_gain[i];
1357 es->i_channel = p_sys->i_video;
1358 if( es->fmt.video.i_frame_rate && es->fmt.video.i_frame_rate_base )
1359 vlc_ureduce( &es->fmt.video.i_frame_rate,
1360 &es->fmt.video.i_frame_rate_base,
1361 fmt->video.i_frame_rate,
1362 fmt->video.i_frame_rate_base, 0 );
1366 es->i_channel = p_sys->i_sub;
1373 es->psz_language = LanguageGetName( es->fmt.psz_language ); /* remember so we only need to do it once */
1374 es->psz_language_code = LanguageGetCode( es->fmt.psz_language );
1376 es->p_dec_record = NULL;
1377 for( i = 0; i < 4; i++ )
1378 es->pb_cc_present[i] = false;
1379 es->p_master = NULL;
1381 if( es->p_pgrm == p_sys->p_pgrm )
1382 EsOutESVarUpdate( out, es, false );
1384 /* Select it if needed */
1385 EsOutSelect( out, es, false );
1388 TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
1389 p_sys->i_id++; /* always incremented */
1390 switch( es->fmt.i_cat )
1403 EsOutAddInfo( out, es );
1405 vlc_mutex_unlock( &p_sys->lock );
1410 static bool EsIsSelected( es_out_id_t *es )
1414 bool b_decode = false;
1415 if( es->p_master->p_dec )
1417 int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1418 if( i_channel != -1 )
1419 input_DecoderGetCcState( es->p_master->p_dec, &b_decode, i_channel );
1425 return es->p_dec != NULL;
1428 static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
1430 es_out_sys_t *p_sys = out->p_sys;
1431 input_thread_t *p_input = p_sys->p_input;
1433 p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_input->p->p_sout );
1436 if( p_sys->b_buffering )
1437 input_DecoderStartBuffering( p_es->p_dec );
1439 if( !p_es->p_master && p_sys->p_sout_record )
1441 p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
1442 if( p_es->p_dec_record && p_sys->b_buffering )
1443 input_DecoderStartBuffering( p_es->p_dec_record );
1447 EsOutDecoderChangeDelay( out, p_es );
1449 static void EsDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
1456 input_DecoderDelete( p_es->p_dec );
1459 if( p_es->p_dec_record )
1461 input_DecoderDelete( p_es->p_dec_record );
1462 p_es->p_dec_record = NULL;
1466 static void EsSelect( es_out_t *out, es_out_id_t *es )
1468 es_out_sys_t *p_sys = out->p_sys;
1469 input_thread_t *p_input = p_sys->p_input;
1471 const char *psz_var;
1473 if( EsIsSelected( es ) )
1475 msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
1482 if( !es->p_master->p_dec )
1485 i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1486 if( i_channel == -1 || input_DecoderSetCcState( es->p_master->p_dec, true, i_channel ) )
1491 if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
1493 if( !var_GetBool( p_input, out->b_sout ? "sout-video" : "video" ) )
1495 msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
1500 else if( es->fmt.i_cat == AUDIO_ES )
1502 var_Get( p_input, "audio", &val );
1503 if( !var_GetBool( p_input, out->b_sout ? "sout-audio" : "audio" ) )
1505 msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
1510 if( es->fmt.i_cat == SPU_ES )
1512 var_Get( p_input, "spu", &val );
1513 if( !var_GetBool( p_input, out->b_sout ? "sout-spu" : "spu" ) )
1515 msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x",
1521 EsCreateDecoder( out, es );
1523 if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
1527 if( es->fmt.i_cat == VIDEO_ES )
1528 psz_var = "video-es";
1529 else if( es->fmt.i_cat == AUDIO_ES )
1530 psz_var = "audio-es";
1531 else if( es->fmt.i_cat == SPU_ES )
1536 /* Mark it as selected */
1537 val.i_int = es->i_id;
1538 var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
1540 var_SetBool( p_sys->p_input, "intf-change", true );
1543 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
1545 es_out_sys_t *p_sys = out->p_sys;
1546 input_thread_t *p_input = p_sys->p_input;
1548 const char *psz_var;
1550 if( !EsIsSelected( es ) )
1552 msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
1558 if( es->p_master->p_dec )
1560 int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec );
1561 if( i_channel != -1 )
1562 input_DecoderSetCcState( es->p_master->p_dec, false, i_channel );
1567 const int i_spu_id = var_GetInteger( p_input, "spu-es");
1569 for( i = 0; i < 4; i++ )
1571 if( !es->pb_cc_present[i] || !es->pp_cc_es[i] )
1574 if( i_spu_id == es->pp_cc_es[i]->i_id )
1576 /* Force unselection of the CC */
1578 var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL );
1580 var_SetBool( p_sys->p_input, "intf-change", true );
1582 EsOutDel( out, es->pp_cc_es[i] );
1584 es->pb_cc_present[i] = false;
1586 EsDestroyDecoder( out, es );
1593 if( es->p_dec == NULL )
1595 if( es->fmt.i_cat == VIDEO_ES )
1596 psz_var = "video-es";
1597 else if( es->fmt.i_cat == AUDIO_ES )
1598 psz_var = "audio-es";
1599 else if( es->fmt.i_cat == SPU_ES )
1604 /* Mark it as unselected */
1606 var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
1608 var_SetBool( p_sys->p_input, "intf-change", true );
1612 * Select an ES given the current mode
1613 * XXX: you need to take a the lock before (stream.stream_lock)
1615 * \param out The es_out structure
1616 * \param es es_out_id structure
1617 * \param b_force ...
1620 static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force )
1622 es_out_sys_t *p_sys = out->p_sys;
1624 int i_cat = es->fmt.i_cat;
1626 if( !p_sys->b_active ||
1627 ( !b_force && es->fmt.i_priority < 0 ) )
1632 if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
1634 if( !EsIsSelected( es ) )
1635 EsSelect( out, es );
1637 else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
1641 var_Get( p_sys->p_input, "programs", &val );
1642 for ( i = 0; i < val.p_list->i_count; i++ )
1644 if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )
1646 if( !EsIsSelected( es ) )
1647 EsSelect( out, es );
1651 var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );
1653 else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
1657 if( es->p_pgrm != p_sys->p_pgrm )
1660 if( i_cat == AUDIO_ES )
1662 int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,
1663 es->psz_language_code );
1665 if( p_sys->p_es_audio &&
1666 p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
1668 int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,
1669 p_sys->p_es_audio->psz_language_code );
1671 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
1673 i_wanted = es->i_channel;
1677 /* Select audio if (no audio selected yet)
1678 * - no audio-language
1679 * - no audio code for the ES
1680 * - audio code in the requested list */
1682 !strcmp( es->psz_language_code, "??" ) ||
1683 !p_sys->ppsz_audio_language )
1684 i_wanted = es->i_channel;
1687 if( p_sys->i_audio_last >= 0 )
1688 i_wanted = p_sys->i_audio_last;
1690 if( p_sys->i_audio_id >= 0 )
1692 if( es->i_id == p_sys->i_audio_id )
1693 i_wanted = es->i_channel;
1698 else if( i_cat == SPU_ES )
1700 int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,
1701 es->psz_language_code );
1703 if( p_sys->p_es_sub &&
1704 p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )
1706 int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,
1707 p_sys->p_es_sub->psz_language_code );
1709 msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",
1710 idx1, es->psz_language_code, idx2,
1711 p_sys->p_es_sub->psz_language_code );
1713 if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
1715 /* We found a SPU that matches our language request */
1716 i_wanted = es->i_channel;
1718 else if( idx1 >= 0 )
1720 msg_Dbg( p_sys->p_input, "idx1=%d(%s)",
1721 idx1, es->psz_language_code );
1723 i_wanted = es->i_channel;
1725 else if( p_sys->i_default_sub_id >= 0 )
1727 if( es->i_id == p_sys->i_default_sub_id )
1728 i_wanted = es->i_channel;
1731 if( p_sys->i_sub_last >= 0 )
1732 i_wanted = p_sys->i_sub_last;
1734 if( p_sys->i_sub_id >= 0 )
1736 if( es->i_id == p_sys->i_sub_id )
1737 i_wanted = es->i_channel;
1742 else if( i_cat == VIDEO_ES )
1744 i_wanted = es->i_channel;
1747 if( i_wanted == es->i_channel && !EsIsSelected( es ) )
1748 EsSelect( out, es );
1751 /* FIXME TODO handle priority here */
1752 if( EsIsSelected( es ) )
1754 if( i_cat == AUDIO_ES )
1756 if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
1757 p_sys->p_es_audio &&
1758 p_sys->p_es_audio != es &&
1759 EsIsSelected( p_sys->p_es_audio ) )
1761 EsUnselect( out, p_sys->p_es_audio, false );
1763 p_sys->p_es_audio = es;
1765 else if( i_cat == SPU_ES )
1767 if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
1769 p_sys->p_es_sub != es &&
1770 EsIsSelected( p_sys->p_es_sub ) )
1772 EsUnselect( out, p_sys->p_es_sub, false );
1774 p_sys->p_es_sub = es;
1776 else if( i_cat == VIDEO_ES )
1778 p_sys->p_es_video = es;
1784 * Send a block for the given es_out
1786 * \param out the es_out to send from
1787 * \param es the es_out_id
1788 * \param p_block the data block to send
1790 static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
1792 es_out_sys_t *p_sys = out->p_sys;
1793 input_thread_t *p_input = p_sys->p_input;
1796 if( libvlc_stats( p_input ) )
1798 vlc_mutex_lock( &p_input->p->counters.counters_lock );
1799 stats_UpdateInteger( p_input, p_input->p->counters.p_demux_read,
1800 p_block->i_buffer, &i_total );
1801 stats_UpdateFloat( p_input , p_input->p->counters.p_demux_bitrate,
1802 (float)i_total, NULL );
1803 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
1806 vlc_mutex_lock( &p_sys->lock );
1808 /* Mark preroll blocks */
1809 if( p_sys->i_preroll_end >= 0 )
1811 int64_t i_date = p_block->i_pts;
1813 i_date = p_block->i_dts;
1815 if( i_date < p_sys->i_preroll_end )
1816 p_block->i_flags |= BLOCK_FLAG_PREROLL;
1819 p_block->i_rate = 0;
1823 block_Release( p_block );
1824 vlc_mutex_unlock( &p_sys->lock );
1829 if( es->p_dec_record )
1831 block_t *p_dup = block_Duplicate( p_block );
1833 input_DecoderDecode( es->p_dec_record, p_dup );
1835 input_DecoderDecode( es->p_dec, p_block );
1837 /* Check CC status */
1839 bool b_cc_new = false;
1841 input_DecoderIsCcPresent( es->p_dec, pb_cc );
1842 for( int i = 0; i < 4; i++ )
1844 static const vlc_fourcc_t fcc[4] = {
1845 VLC_FOURCC('c', 'c', '1', ' '),
1846 VLC_FOURCC('c', 'c', '2', ' '),
1847 VLC_FOURCC('c', 'c', '3', ' '),
1848 VLC_FOURCC('c', 'c', '4', ' '),
1852 if( es->pb_cc_present[i] || !pb_cc[i] )
1854 msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id );
1856 es_format_Init( &fmt, SPU_ES, fcc[i] );
1857 fmt.i_group = es->fmt.i_group;
1858 if( asprintf( &fmt.psz_description,
1859 _("Closed captions %u"), 1 + i ) == -1 )
1860 fmt.psz_description = NULL;
1861 es->pp_cc_es[i] = EsOutAdd( out, &fmt );
1862 es->pp_cc_es[i]->p_master = es;
1863 es_format_Clean( &fmt );
1866 es->pb_cc_present[i] = true;
1870 var_SetBool( p_sys->p_input, "intf-change", true );
1872 vlc_mutex_unlock( &p_sys->lock );
1877 /*****************************************************************************
1879 *****************************************************************************/
1880 static void EsOutDel( es_out_t *out, es_out_id_t *es )
1882 es_out_sys_t *p_sys = out->p_sys;
1883 bool b_reselect = false;
1886 vlc_mutex_lock( &p_sys->lock );
1888 /* We don't try to reselect */
1891 while( !p_sys->p_input->b_die && !p_sys->b_buffering && es->p_dec )
1893 if( input_DecoderIsEmpty( es->p_dec ) &&
1894 ( !es->p_dec_record || input_DecoderIsEmpty( es->p_dec_record ) ))
1896 /* FIXME there should be a way to have auto deleted es, but there will be
1897 * a problem when another codec of the same type is created (mainly video) */
1900 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
1903 if( es->p_pgrm == p_sys->p_pgrm )
1904 EsOutESVarUpdate( out, es, true );
1906 TAB_REMOVE( p_sys->i_es, p_sys->es, es );
1909 if( es->p_pgrm->i_es == 0 )
1911 msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" );
1914 if( p_sys->p_es_audio == es || p_sys->p_es_video == es ||
1915 p_sys->p_es_sub == es ) b_reselect = true;
1917 if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
1918 if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
1919 if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL;
1921 switch( es->fmt.i_cat )
1934 /* Re-select another track when needed */
1937 for( i = 0; i < p_sys->i_es; i++ )
1939 if( es->fmt.i_cat == p_sys->es[i]->fmt.i_cat )
1940 EsOutSelect( out, p_sys->es[i], false );
1944 free( es->psz_language );
1945 free( es->psz_language_code );
1947 es_format_Clean( &es->fmt );
1949 vlc_mutex_unlock( &p_sys->lock );
1955 * Control query handler
1957 * \param out the es_out to control
1958 * \param i_query A es_out query as defined in include/ninput.h
1959 * \param args a variable list of arguments for the query
1960 * \return VLC_SUCCESS or an error code
1962 static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
1964 es_out_sys_t *p_sys = out->p_sys;
1972 case ES_OUT_SET_ES_STATE:
1973 es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1974 b = (bool) va_arg( args, int );
1975 if( b && !EsIsSelected( es ) )
1977 EsSelect( out, es );
1978 return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC;
1980 else if( !b && EsIsSelected( es ) )
1982 EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
1987 case ES_OUT_GET_ES_STATE:
1988 es = (es_out_id_t*) va_arg( args, es_out_id_t * );
1989 pb = (bool*) va_arg( args, bool * );
1991 *pb = EsIsSelected( es );
1994 case ES_OUT_SET_ACTIVE:
1996 b = (bool) va_arg( args, int );
1997 p_sys->b_active = b;
2000 var_SetBool( p_sys->p_input, "intf-change", true );
2004 case ES_OUT_SET_MODE:
2005 i = (int) va_arg( args, int );
2006 if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
2007 i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )
2011 /* Reapply policy mode */
2012 for( i = 0; i < p_sys->i_es; i++ )
2014 if( EsIsSelected( p_sys->es[i] ) )
2016 EsUnselect( out, p_sys->es[i],
2017 p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2020 for( i = 0; i < p_sys->i_es; i++ )
2022 EsOutSelect( out, p_sys->es[i], false );
2026 return VLC_EGENERIC;
2029 case ES_OUT_RESTART_ES:
2033 es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2037 else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
2039 else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
2041 else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
2046 for( i = 0; i < p_sys->i_es; i++ )
2050 if( es == p_sys->es[i] )
2052 EsOutSelect( out, es, true );
2058 if( i_cat == UNKNOWN_ES || p_sys->es[i]->fmt.i_cat == i_cat )
2060 if( EsIsSelected( p_sys->es[i] ) )
2062 if( i_query == ES_OUT_RESTART_ES )
2064 if( p_sys->es[i]->p_dec )
2066 EsDestroyDecoder( out, p_sys->es[i] );
2067 EsCreateDecoder( out, p_sys->es[i] );
2072 EsUnselect( out, p_sys->es[i],
2073 p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
2079 if( i_query == ES_OUT_SET_ES )
2082 event.type = vlc_InputSelectedStreamChanged;
2083 vlc_event_send( &p_sys->p_input->p->event_manager, &event );
2088 case ES_OUT_SET_ES_DEFAULT:
2090 es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2094 /*p_sys->i_default_video_id = -1;*/
2095 /*p_sys->i_default_audio_id = -1;*/
2096 p_sys->i_default_sub_id = -1;
2098 else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
2100 /*p_sys->i_default_video_id = -1;*/
2102 else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
2104 /*p_sys->i_default_audio_id = -1;*/
2106 else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
2108 p_sys->i_default_sub_id = -1;
2112 /*if( es->fmt.i_cat == VIDEO_ES )
2113 p_sys->i_default_video_id = es->i_id;
2115 if( es->fmt.i_cat == AUDIO_ES )
2116 p_sys->i_default_audio_id = es->i_id;
2118 if( es->fmt.i_cat == SPU_ES )
2119 p_sys->i_default_sub_id = es->i_id;
2124 case ES_OUT_SET_PCR:
2125 case ES_OUT_SET_GROUP_PCR:
2127 es_out_pgrm_t *p_pgrm = NULL;
2131 if( i_query == ES_OUT_SET_PCR )
2133 p_pgrm = p_sys->p_pgrm;
2138 i_group = (int)va_arg( args, int );
2139 for( i = 0; i < p_sys->i_pgrm; i++ )
2141 if( p_sys->pgrm[i]->i_id == i_group )
2143 p_pgrm = p_sys->pgrm[i];
2148 if( p_pgrm == NULL )
2149 p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
2151 i_pcr = (int64_t)va_arg( args, int64_t );
2153 * TODO do not use mdate() but proper stream acquisition date */
2154 input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input),
2155 p_sys->p_input->b_can_pace_control || p_sys->b_buffering, i_pcr, mdate() );
2156 /* Check buffering state on master clock update */
2157 if( p_sys->b_buffering && p_pgrm == p_sys->p_pgrm )
2158 EsOutDecodersStopBuffering( out, false );
2163 case ES_OUT_RESET_PCR:
2164 msg_Err( p_sys->p_input, "ES_OUT_RESET_PCR called" );
2165 EsOutChangePosition( out );
2171 int64_t i_ts = (int64_t)va_arg( args, int64_t );
2172 int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * );
2173 *pi_ts = input_clock_GetTS( p_sys->p_pgrm->p_clock, NULL,
2174 p_sys->p_input->i_pts_delay, i_ts );
2177 return VLC_EGENERIC;
2179 case ES_OUT_SET_GROUP:
2182 i = (int) va_arg( args, int );
2183 for( j = 0; j < p_sys->i_pgrm; j++ )
2185 es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
2186 if( p_pgrm->i_id == i )
2188 EsOutProgramSelect( out, p_pgrm );
2192 return VLC_EGENERIC;
2195 case ES_OUT_SET_ES_FMT:
2197 /* This ain't pretty but is need by some demuxers (eg. Ogg )
2198 * to update the p_extra data */
2200 es = (es_out_id_t*) va_arg( args, es_out_id_t * );
2201 p_fmt = (es_format_t*) va_arg( args, es_format_t * );
2203 return VLC_EGENERIC;
2205 if( p_fmt->i_extra )
2207 es->fmt.i_extra = p_fmt->i_extra;
2208 es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );
2209 memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );
2214 EsDestroyDecoder( out, es );
2216 EsCreateDecoder( out, es );
2218 es->p_dec->fmt_in.i_extra = p_fmt->i_extra;
2219 es->p_dec->fmt_in.p_extra =
2220 realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );
2221 memcpy( es->p_dec->fmt_in.p_extra,
2222 p_fmt->p_extra, p_fmt->i_extra );
2229 case ES_OUT_SET_NEXT_DISPLAY_TIME:
2231 const int64_t i_date = (int64_t)va_arg( args, int64_t );
2234 return VLC_EGENERIC;
2236 p_sys->i_preroll_end = i_date;
2240 case ES_OUT_SET_GROUP_META:
2242 int i_group = (int)va_arg( args, int );
2243 vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
2245 EsOutProgramMeta( out, i_group, p_meta );
2248 case ES_OUT_SET_GROUP_EPG:
2250 int i_group = (int)va_arg( args, int );
2251 vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
2253 EsOutProgramEpg( out, i_group, p_epg );
2256 case ES_OUT_DEL_GROUP:
2258 int i_group = (int)va_arg( args, int );
2260 return EsOutProgramDel( out, i_group );
2263 case ES_OUT_GET_WAKE_UP:
2265 mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
2266 *pi_wakeup = EsOutGetWakeup( out );
2270 case ES_OUT_SET_ES_BY_ID:
2271 case ES_OUT_RESTART_ES_BY_ID:
2272 case ES_OUT_SET_ES_DEFAULT_BY_ID:
2274 const int i_id = (int)va_arg( args, int );
2275 es_out_id_t *p_es = EsOutGetFromID( out, i_id );
2280 case ES_OUT_SET_ES_BY_ID: i_new_query = ES_OUT_SET_ES; break;
2281 case ES_OUT_RESTART_ES_BY_ID: i_new_query = ES_OUT_RESTART_ES; break;
2282 case ES_OUT_SET_ES_DEFAULT_BY_ID: i_new_query = ES_OUT_SET_ES_DEFAULT; break;
2286 /* TODO if the lock is made non recursive it should be changed */
2287 return es_out_Control( out, i_new_query, p_es );
2290 case ES_OUT_GET_BUFFERING:
2291 pb = (bool *)va_arg( args, bool* );
2292 *pb = p_sys->b_buffering;
2295 case ES_OUT_GET_EMPTY:
2296 pb = (bool *)va_arg( args, bool* );
2297 *pb = EsOutDecodersIsEmpty( out );
2300 case ES_OUT_SET_DELAY:
2302 const int i_cat = (int)va_arg( args, int );
2303 const mtime_t i_delay = (mtime_t)va_arg( args, mtime_t );
2304 EsOutSetDelay( out, i_cat, i_delay );
2308 case ES_OUT_SET_RECORD_STATE:
2309 b = (bool) va_arg( args, int );
2310 return EsOutSetRecord( out, b );
2312 case ES_OUT_SET_PAUSE_STATE:
2314 const bool b_source_paused = (bool)va_arg( args, int );
2315 const bool b_paused = (bool)va_arg( args, int );
2316 const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
2318 assert( !b_source_paused == !b_paused );
2319 EsOutChangePause( out, b_paused, i_date );
2324 case ES_OUT_SET_RATE:
2326 const int i_src_rate = (int)va_arg( args, int );
2327 const int i_rate = (int)va_arg( args, int );
2329 assert( i_src_rate == i_rate );
2330 EsOutChangeRate( out, i_rate );
2335 case ES_OUT_SET_TIME:
2337 const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
2339 assert( i_date == -1 );
2340 EsOutChangePosition( out );
2345 case ES_OUT_SET_FRAME_NEXT:
2346 EsOutFrameNext( out );
2350 msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
2351 return VLC_EGENERIC;
2354 static int EsOutControl( es_out_t *out, int i_query, va_list args )
2356 es_out_sys_t *p_sys = out->p_sys;
2359 vlc_mutex_lock( &p_sys->lock );
2360 i_ret = EsOutControlLocked( out, i_query, args );
2361 vlc_mutex_unlock( &p_sys->lock );
2366 /****************************************************************************
2367 * LanguageGetName: try to expend iso639 into plain name
2368 ****************************************************************************/
2369 static char *LanguageGetName( const char *psz_code )
2371 const iso639_lang_t *pl;
2373 if( psz_code == NULL )
2375 return strdup( "" );
2378 if( strlen( psz_code ) == 2 )
2380 pl = GetLang_1( psz_code );
2382 else if( strlen( psz_code ) == 3 )
2384 pl = GetLang_2B( psz_code );
2385 if( !strcmp( pl->psz_iso639_1, "??" ) )
2387 pl = GetLang_2T( psz_code );
2392 return strdup( psz_code );
2395 if( !strcmp( pl->psz_iso639_1, "??" ) )
2397 return strdup( psz_code );
2401 if( *pl->psz_native_name )
2403 return strdup( pl->psz_native_name );
2405 return strdup( pl->psz_eng_name );
2409 /* Get a 2 char code */
2410 static char *LanguageGetCode( const char *psz_lang )
2412 const iso639_lang_t *pl;
2414 if( psz_lang == NULL || *psz_lang == '\0' )
2415 return strdup("??");
2417 for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
2419 if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
2420 !strcasecmp( pl->psz_native_name, psz_lang ) ||
2421 !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
2422 !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
2423 !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
2427 if( pl->psz_iso639_1 != NULL )
2428 return strdup( pl->psz_iso639_1 );
2430 return strdup("??");
2433 static char **LanguageSplit( const char *psz_langs )
2440 if( psz_langs == NULL ) return NULL;
2442 psz_parser = psz_dup = strdup(psz_langs);
2444 while( psz_parser && *psz_parser )
2449 psz = strchr(psz_parser, ',' );
2450 if( psz ) *psz++ = '\0';
2452 if( !strcmp( psz_parser, "any" ) )
2454 TAB_APPEND( i_psz, ppsz, strdup("any") );
2458 psz_code = LanguageGetCode( psz_parser );
2459 if( strcmp( psz_code, "??" ) )
2461 TAB_APPEND( i_psz, ppsz, psz_code );
2474 TAB_APPEND( i_psz, ppsz, NULL );
2481 static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )
2485 if( !ppsz_langs || !psz_lang ) return -1;
2487 for( i = 0; ppsz_langs[i]; i++ )
2489 if( !strcasecmp( ppsz_langs[i], psz_lang ) ||
2490 !strcasecmp( ppsz_langs[i], "any" ) )
2499 /****************************************************************************
2501 * - add meta info to the playlist item
2502 ****************************************************************************/
2503 static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )
2505 es_out_sys_t *p_sys = out->p_sys;
2506 input_thread_t *p_input = p_sys->p_input;
2507 es_format_t *fmt = &es->fmt;
2511 /* Add stream info */
2512 if( asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 ) == -1 )
2515 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),
2516 "%.4s", (char*)&fmt->i_codec );
2518 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),
2519 "%s", es->psz_language );
2521 /* Add information */
2522 switch( fmt->i_cat )
2525 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2526 _("Type"), _("Audio") );
2528 if( fmt->audio.i_channels > 0 )
2529 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),
2530 "%u", fmt->audio.i_channels );
2532 if( fmt->audio.i_rate > 0 )
2534 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),
2535 _("%u Hz"), fmt->audio.i_rate );
2536 var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate );
2539 if( fmt->audio.i_bitspersample > 0 )
2540 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2541 _("Bits per sample"), "%u",
2542 fmt->audio.i_bitspersample );
2544 if( fmt->i_bitrate > 0 )
2546 input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),
2547 _("%u kb/s"), fmt->i_bitrate / 1000 );
2548 var_SetInteger( p_input, "bit-rate", fmt->i_bitrate );
2553 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2554 _("Type"), _("Video") );
2556 if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
2557 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2558 _("Resolution"), "%ux%u",
2559 fmt->video.i_width, fmt->video.i_height );
2561 if( fmt->video.i_visible_width > 0 &&
2562 fmt->video.i_visible_height > 0 )
2563 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2564 _("Display resolution"), "%ux%u",
2565 fmt->video.i_visible_width,
2566 fmt->video.i_visible_height);
2567 if( fmt->video.i_frame_rate > 0 &&
2568 fmt->video.i_frame_rate_base > 0 )
2570 div = lldiv( (float)fmt->video.i_frame_rate /
2571 fmt->video.i_frame_rate_base * 1000000,
2573 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2574 _("Frame rate"), "%"PRId64".%06u",
2575 div.quot, (unsigned int )div.rem );
2580 input_Control( p_input, INPUT_ADD_INFO, psz_cat,
2581 _("Type"), _("Subtitle") );