It fixes a lot of unlocked access.
It will allows a better clock algo.
static void AvgReset( average_t * );
static void AvgUpdate( average_t *, mtime_t i_value );
static mtime_t AvgGet( average_t * );
+static void AvgRescale( average_t *, int i_divider );
/* */
typedef struct
/* Current modifiers */
int i_rate;
+ mtime_t i_pts_delay;
bool b_paused;
mtime_t i_pause_date;
};
/*****************************************************************************
* input_clock_New: create a new clock
*****************************************************************************/
-input_clock_t *input_clock_New( int i_cr_average, int i_rate )
+input_clock_t *input_clock_New( int i_rate )
{
input_clock_t *cl = malloc( sizeof(*cl) );
if( !cl )
cl->i_ts_max = 0;
cl->i_next_drift_update = 0;
- AvgInit( &cl->drift, i_cr_average );
+ AvgInit( &cl->drift, 10 );
cl->i_rate = i_rate;
+ cl->i_pts_delay = 0;
cl->b_paused = false;
cl->i_pause_date = 0;
* input_clock_GetTS: manages a PTS or DTS
*****************************************************************************/
mtime_t input_clock_GetTS( input_clock_t *cl, int *pi_rate,
- mtime_t i_pts_delay, mtime_t i_ts )
+ mtime_t i_ts, mtime_t i_ts_bound )
{
mtime_t i_converted_ts;
vlc_mutex_unlock( &cl->lock );
- return i_converted_ts + i_pts_delay;
+ i_converted_ts += cl->i_pts_delay;
+
+ /* Check ts validity */
+ if( i_ts_bound != INT64_MAX &&
+ i_converted_ts >= mdate() + cl->i_pts_delay + i_ts_bound )
+ return 0;
+
+ return i_converted_ts;
}
/*****************************************************************************
* input_clock_GetRate: Return current rate
vlc_mutex_unlock( &cl->lock );
}
+#warning "input_clock_SetJitter needs more work"
+void input_clock_SetJitter( input_clock_t *cl,
+ mtime_t i_pts_delay, int i_cr_average )
+{
+ vlc_mutex_lock( &cl->lock );
+
+ /* TODO always save the value, and when rebuffering use the new one if smaller
+ * TODO when increasing -> force rebuffering
+ */
+ if( cl->i_pts_delay < i_pts_delay )
+ cl->i_pts_delay = i_pts_delay;
+
+ /* */
+ if( i_cr_average < 10 )
+ i_cr_average = 10;
+
+ if( cl->drift.i_divider != i_cr_average )
+ AvgRescale( &cl->drift, i_cr_average );
+
+ vlc_mutex_unlock( &cl->lock );
+}
+
/*****************************************************************************
* ClockStreamToSystem: converts a movie clock to system date
*****************************************************************************/
{
return p_avg->i_value;
}
+static void AvgRescale( average_t *p_avg, int i_divider )
+{
+ const mtime_t i_tmp = p_avg->i_value * p_avg->i_divider + p_avg->i_residue;
+ p_avg->i_divider = i_divider;
+ p_avg->i_value = i_tmp / p_avg->i_divider;
+ p_avg->i_residue = i_tmp % p_avg->i_divider;
+}
* This function creates a new input_clock_t.
* You must use input_clock_Delete to delete it once unused.
*/
-input_clock_t *input_clock_New( int i_cr_average, int i_rate );
+input_clock_t *input_clock_New( int i_rate );
+
/**
* This function destroys a input_clock_t created by input_clock_New.
*/
*
* If pi_rate is provided it will be field with the rate value used for
* the conversion.
+ * If i_ts_bound is not INT64_MAX, the value will be invalidated if not
+ * before mdate() + i_pts_delay + i_ts_bound.
*/
-mtime_t input_clock_GetTS( input_clock_t *, int *pi_rate, mtime_t i_pts_delay, mtime_t );
+mtime_t input_clock_GetTS( input_clock_t *, int *pi_rate, mtime_t i_ts, mtime_t i_ts_bound );
/**
* This function returns the current rate.
mtime_t *pi_stream_start, mtime_t *pi_system_start,
mtime_t *pi_stream_duration, mtime_t *pi_system_duration );
+/**
+ * This function allows the set the minimal configuration for the jitter estimation algo.
+ */
+void input_clock_SetJitter( input_clock_t *,
+ mtime_t i_pts_delay, int i_cr_average );
+
#endif
if( !p_owner->p_clock || !i_ts )
return i_ts;
- return input_clock_GetTS( p_owner->p_clock, NULL, p_owner->p_input->p->i_pts_delay, i_ts );
+ return input_clock_GetTS( p_owner->p_clock, NULL, i_ts, INT64_MAX );
}
static int DecoderGetDisplayRate( decoder_t *p_dec )
{
*pi_preroll = __MIN( *pi_preroll, p->i_dts );
}
-static mtime_t DecoderTeletextFixTs( mtime_t i_ts, mtime_t i_ts_delay )
+static mtime_t DecoderTeletextFixTs( mtime_t i_ts )
{
mtime_t current_date = mdate();
{
/* ETSI EN 300 472 Annex A : do not take into account the PTS
* for teletext streams. */
- return current_date + 400000 + i_ts_delay;
+ return current_date + 400000;
}
return i_ts;
}
static void DecoderFixTs( decoder_t *p_dec, mtime_t *pi_ts0, mtime_t *pi_ts1,
- mtime_t *pi_duration, int *pi_rate, mtime_t *pi_delay, bool b_telx )
+ mtime_t *pi_duration, int *pi_rate, mtime_t i_ts_bound, bool b_telx )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
input_clock_t *p_clock = p_owner->p_clock;
vlc_assert_locked( &p_owner->lock );
- const mtime_t i_ts_delay = p_owner->p_input->p->i_pts_delay;
const mtime_t i_es_delay = p_owner->i_ts_delay;
if( p_clock )
const bool b_ephemere = pi_ts1 && *pi_ts0 == *pi_ts1;
if( *pi_ts0 > 0 )
- *pi_ts0 = input_clock_GetTS( p_clock, &i_rate, i_ts_delay,
- *pi_ts0 + i_es_delay );
+ *pi_ts0 = input_clock_GetTS( p_clock, &i_rate, *pi_ts0 + i_es_delay, i_ts_bound );
if( pi_ts1 && *pi_ts1 > 0 )
- *pi_ts1 = input_clock_GetTS( p_clock, &i_rate, i_ts_delay,
- *pi_ts1 + i_es_delay );
+ {
+ if( *pi_ts0 > 0 )
+ *pi_ts1 = input_clock_GetTS( p_clock, &i_rate, *pi_ts1 + i_es_delay, INT64_MAX );
+ else
+ *pi_ts1 = 0;
+ }
/* Do not create ephemere data because of rounding errors */
if( !b_ephemere && pi_ts1 && *pi_ts0 == *pi_ts1 )
if( b_telx )
{
- *pi_ts0 = DecoderTeletextFixTs( *pi_ts0, i_ts_delay );
+ *pi_ts0 = DecoderTeletextFixTs( *pi_ts0 );
if( pi_ts1 && *pi_ts1 <= 0 )
*pi_ts1 = *pi_ts0;
}
}
- if( pi_delay )
- {
- const int r = i_rate > 0 ? i_rate : INPUT_RATE_DEFAULT;
- *pi_delay = i_ts_delay + i_es_delay * r / INPUT_RATE_DEFAULT;
- }
}
static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio,
}
/* */
+ const bool b_dated = p_audio->start_date > 0;
int i_rate = INPUT_RATE_DEFAULT;
- mtime_t i_delay;
DecoderFixTs( p_dec, &p_audio->start_date, &p_audio->end_date, NULL,
- &i_rate, &i_delay, false );
+ &i_rate, AOUT_MAX_ADVANCE_TIME, false );
vlc_mutex_unlock( &p_owner->lock );
- /* */
- const mtime_t i_max_date = mdate() + i_delay + AOUT_MAX_ADVANCE_TIME;
-
if( !p_aout || !p_aout_input ||
- p_audio->start_date <= 0 || p_audio->start_date > i_max_date ||
+ p_audio->start_date <= 0 ||
i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE ||
i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
b_reject = true;
}
else
{
- if( p_audio->start_date <= 0 )
- {
+ if( b_dated )
+ msg_Warn( p_aout, "received buffer in the future" );
+ else
msg_Warn( p_dec, "non-dated audio buffer received" );
- }
- else if( p_audio->start_date > i_max_date )
- {
- msg_Warn( p_aout, "received buffer in the future (%"PRId64")",
- p_audio->start_date - mdate() );
- }
+
*pi_lost_sum += 1;
aout_BufferFree( p_audio );
}
p_picture->b_force = true;
}
+ const bool b_dated = p_picture->date > 0;
int i_rate = INPUT_RATE_DEFAULT;
- mtime_t i_delay;
DecoderFixTs( p_dec, &p_picture->date, NULL, NULL,
- &i_rate, &i_delay, false );
+ &i_rate, DECODER_BOGUS_VIDEO_DELAY, false );
vlc_mutex_unlock( &p_owner->lock );
/* */
- const mtime_t i_max_date = mdate() + i_delay + DECODER_BOGUS_VIDEO_DELAY;
-
- if( !p_picture->b_force && ( p_picture->date <= 0 || p_picture->date >= i_max_date ) )
+ if( !p_picture->b_force && p_picture->date <= 0 )
b_reject = true;
if( !b_reject )
}
else
{
- if( p_picture->date <= 0 )
- {
- msg_Warn( p_vout, "non-dated video buffer received" );
- }
+ if( b_dated )
+ msg_Warn( p_vout, "early picture skipped" );
else
- {
- msg_Warn( p_vout, "early picture skipped (%"PRId64")",
- p_picture->date - mdate() );
- }
+ msg_Warn( p_vout, "non-dated video buffer received" );
+
*pi_lost_sum += 1;
vout_DropPicture( p_vout, p_picture );
}
/* */
DecoderFixTs( p_dec, &p_subpic->i_start, &p_subpic->i_stop, NULL,
- NULL, NULL, b_telx );
+ NULL, INT64_MAX, b_telx );
vlc_mutex_unlock( &p_owner->lock );
DecoderFixTs( p_dec, &p_sout_block->i_dts, &p_sout_block->i_pts,
&p_sout_block->i_length,
- &p_sout_block->i_rate, NULL, b_telx );
+ &p_sout_block->i_rate, INT64_MAX, b_telx );
vlc_mutex_unlock( &p_owner->lock );
int64_t i_audio_delay;
int64_t i_spu_delay;
- /* Rate used for clock */
+ /* Clock configuration */
+ mtime_t i_pts_delay;
+ int i_cr_average;
int i_rate;
/* */
p_sys->i_pause_date = -1;
p_sys->i_rate = i_rate;
+ p_sys->i_pts_delay = 0;
+ p_sys->i_cr_average = 0;
p_sys->b_buffering = true;
p_sys->i_buffering_extra_initial = 0;
if( p_sys->i_preroll_end >= 0 )
i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
- const mtime_t i_buffering_duration = p_sys->p_input->p->i_pts_delay +
+ const mtime_t i_buffering_duration = p_sys->i_pts_delay +
i_preroll_duration +
p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
if( i_ret )
return;
- p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->p_input->p->i_pts_delay; /* FIXME < 0 ? */
+ p_sys->i_buffering_extra_initial = 1 + i_stream_duration - p_sys->i_pts_delay; /* FIXME < 0 ? */
p_sys->i_buffering_extra_system =
p_sys->i_buffering_extra_stream = p_sys->i_buffering_extra_initial;
}
}
const mtime_t i_consumed = i_system_duration * INPUT_RATE_DEFAULT / p_sys->i_rate - i_stream_duration;
- i_delay = p_sys->p_input->p->i_pts_delay - i_consumed;
+ i_delay = p_sys->i_pts_delay - i_consumed;
}
if( i_delay < 0 )
return 0;
p_pgrm->psz_now_playing = NULL;
p_pgrm->psz_publisher = NULL;
p_pgrm->p_epg = NULL;
- p_pgrm->p_clock = input_clock_New( p_input->p->i_cr_average, p_sys->i_rate );
+ p_pgrm->p_clock = input_clock_New( p_sys->i_rate );
if( !p_pgrm->p_clock )
{
free( p_pgrm );
return NULL;
}
+ input_clock_SetJitter( p_pgrm->p_clock, p_sys->i_pts_delay, p_sys->i_cr_average );
+
/* Append it */
TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
input_SendEventTimes( p_sys->p_input, f_position, i_time, i_length );
return VLC_SUCCESS;
}
+ case ES_OUT_SET_JITTER:
+ {
+ mtime_t i_pts_delay = (mtime_t)va_arg( args, mtime_t );
+ int i_cr_average = (int)va_arg( args, int );
+
+ if( i_pts_delay == p_sys->i_pts_delay &&
+ i_cr_average == p_sys->i_cr_average )
+ return VLC_SUCCESS;
+
+ p_sys->i_pts_delay = i_pts_delay;
+ p_sys->i_cr_average = i_cr_average;
+
+ for( int i = 0; i < p_sys->i_pgrm; i++ )
+ input_clock_SetJitter( p_sys->pgrm[i]->p_clock,
+ i_pts_delay, i_cr_average );
+ return VLC_SUCCESS;
+ }
default:
msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
/* Set position/time/length */
ES_OUT_SET_TIMES, /* arg1=double f_position arg2=mtime_t i_time arg3=mtime_t i_length res=cannot fail */
+
+ /* Set jitter */
+ ES_OUT_SET_JITTER, /* arg1=mtime_t i_pts_delay arg2=int i_cr_average res=cannot fail */
};
static inline mtime_t es_out_GetWakeup( es_out_t *p_out )
int i_ret = es_out_Control( p_out, ES_OUT_SET_TIMES, f_position, i_time, i_length );
assert( !i_ret );
}
+static inline void es_out_SetJitter( es_out_t *p_out, mtime_t i_pts_delay, int i_cr_average )
+{
+ int i_ret = es_out_Control( p_out, ES_OUT_SET_JITTER, i_pts_delay, i_cr_average );
+ assert( !i_ret );
+}
es_out_t *input_EsOutNew( input_thread_t *, int i_rate );
mtime_t i_time;
mtime_t i_length;
} times;
+ struct
+ {
+ mtime_t i_pts_delay;
+ int i_cr_average;
+ } jitter;
};
} ts_cmd_control_t;
case ES_OUT_SET_ES_STATE:
case ES_OUT_SET_ES_FMT:
case ES_OUT_SET_TIMES:
+ case ES_OUT_SET_JITTER:
{
ts_cmd_t cmd;
if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
p_cmd->control.times.i_length = i_length;
break;
}
+ case ES_OUT_SET_JITTER:
+ {
+ mtime_t i_pts_delay = (mtime_t)va_arg( args, mtime_t );
+ int i_cr_average = (int)va_arg( args, int );
+
+ p_cmd->control.jitter.i_pts_delay = i_pts_delay;
+ p_cmd->control.jitter.i_cr_average = i_cr_average;
+ break;
+ }
default:
assert(0);
return es_out_Control( p_out, i_query, p_cmd->control.times.f_position,
p_cmd->control.times.i_time,
p_cmd->control.times.i_length );
+ case ES_OUT_SET_JITTER:
+ return es_out_Control( p_out, i_query, p_cmd->control.jitter.i_pts_delay,
+ p_cmd->control.jitter.i_cr_average );
default:
assert(0);
p_input->p->p_es_out = NULL;
p_input->p->p_sout = NULL;
p_input->p->b_out_pace_control = false;
- p_input->p->i_pts_delay = 0;
- p_input->p->i_cr_average = 0;
vlc_gc_incref( p_item ); /* Released in Destructor() */
p_input->p->p_item = p_item;
input_ControlVarInit( p_input );
/* */
- p_input->p->i_cr_average = var_GetInteger( p_input, "cr-average" );
-
if( !p_input->b_preparsing )
{
var_Get( p_input, "bookmarks", &val );
p_input->p->b_can_pace_control = p_master->b_can_pace_control;
p_input->p->b_can_pause = p_master->b_can_pause;
p_input->p->b_can_rate_control = p_master->b_can_rate_control;
-
- /* Fix pts delay */
- if( p_input->p->i_pts_delay < 0 )
- p_input->p->i_pts_delay = 0;
-
- /* If the desynchronisation requested by the user is < 0, we need to
- * cache more data. */
- const int i_desynch = var_GetInteger( p_input, "audio-desync" );
- if( i_desynch < 0 )
- p_input->p->i_pts_delay -= i_desynch * 1000;
-
- /* Update cr_average depending on the caching */
- p_input->p->i_cr_average *= (10 * p_input->p->i_pts_delay / 200000);
- p_input->p->i_cr_average /= 10;
- if( p_input->p->i_cr_average < 10 )
- p_input->p->i_cr_average = 10;
}
static void StartTitle( input_thread_t * p_input )
free( psz_org );
}
+static void UpdatePtsDelay( input_thread_t *p_input )
+{
+ input_thread_private_t *p_sys = p_input->p;
+
+ /* Get max pts delay from input source */
+ mtime_t i_pts_delay = p_sys->input.i_pts_delay;
+ for( int i = 0; i < p_sys->i_slave; i++ )
+ i_pts_delay = __MAX( i_pts_delay, p_sys->slave[i]->i_pts_delay );
+
+ if( i_pts_delay < 0 )
+ i_pts_delay = 0;
+
+ /* Take care of audio/spu delay */
+ const int i_audio_delay = var_GetTime( p_input, "audio-delay" );
+ const int i_spu_delay = var_GetTime( p_input, "spu-delay" );
+ const int i_extra_delay = __MIN( i_audio_delay, i_spu_delay );
+ if( i_extra_delay < 0 )
+ i_pts_delay -= i_extra_delay * INT64_C(1000);
+
+ /* Update cr_average depending on the caching */
+ const int i_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;
+
+ /* */
+ es_out_SetJitter( p_input->p->p_es_out, i_pts_delay, i_cr_average );
+}
+
static void InitPrograms( input_thread_t * p_input )
{
int i_es_out_mode;
vlc_value_t val;
+ /* Compute correct pts_delay */
+ UpdatePtsDelay( p_input );
+
/* Set up es_out */
es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ACTIVE, true );
i_es_out_mode = ES_OUT_MODE_AUTO;
case INPUT_CONTROL_SET_AUDIO_DELAY:
if( !es_out_SetDelay( p_input->p->p_es_out_display, AUDIO_ES, val.i_time ) )
+ {
input_SendEventAudioDelay( p_input, val.i_time );
+ UpdatePtsDelay( p_input );
+ }
break;
case INPUT_CONTROL_SET_SPU_DELAY:
if( !es_out_SetDelay( p_input->p->p_es_out_display, SPU_ES, val.i_time ) )
+ {
input_SendEventSubtitleDelay( p_input, val.i_time );
+ UpdatePtsDelay( p_input );
+ }
break;
case INPUT_CONTROL_SET_TITLE:
if( in->p_demux )
{
- int64_t i_pts_delay;
-
/* Get infos from access_demux */
demux_Control( in->p_demux,
- DEMUX_GET_PTS_DELAY, &i_pts_delay );
- p_input->p->i_pts_delay = __MAX( p_input->p->i_pts_delay, i_pts_delay );
+ DEMUX_GET_PTS_DELAY, &in->i_pts_delay );
in->b_title_demux = true;
if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO,
}
else
{
- int64_t i_pts_delay;
-
/* Now try a real access */
in->p_access = access_New( p_input, psz_access, psz_demux, psz_path );
if( !p_input->b_preparsing )
{
access_Control( in->p_access,
- ACCESS_GET_PTS_DELAY, &i_pts_delay );
- p_input->p->i_pts_delay = __MAX( p_input->p->i_pts_delay, i_pts_delay );
+ ACCESS_GET_PTS_DELAY, &in->i_pts_delay );
in->b_title_demux = false;
if( access_Control( in->p_access, ACCESS_GET_TITLE_INFO,
bool b_can_stream_record;
bool b_rescale_ts;
+ /* */
+ int64_t i_pts_delay;
+
bool b_eof; /* eof of demuxer */
} input_source_t;
double f_fps;
int i_state;
- /* Internal caching common to all sources */
- mtime_t i_pts_delay;
-
/* Current state */
int i_rate;
bool b_recording;
/* Input item */
input_item_t *p_item;
- /* Clock average variation */
- int i_cr_average;
+
/* Main source */
input_source_t input;
/* Slave sources (subs, and others) */
if( !strcmp( psz_cmd, "audio-delay" ) )
{
- /* Change i_pts_delay to make sure es are decoded in time */
- if( newval.i_int < 0 || oldval.i_int < 0 )
- {
- p_input->p->i_pts_delay -= newval.i_int - oldval.i_int;
- }
input_ControlPush( p_input, INPUT_CONTROL_SET_AUDIO_DELAY, &newval );
}
else if( !strcmp( psz_cmd, "spu-delay" ) )