Commit dc5be237 authored by Laurent Aimar's avatar Laurent Aimar

Automatically compute the latency(pts_delay) needed after a buffer underflow.

The latency is increased by monitoring the last buffer underflows and by
filtering the values obtained (median 3).
It is simple and pretty robust (tested with http and sshfs over wireless).
parent a48799e7
......@@ -122,6 +122,9 @@ static inline clock_point_t clock_point_Create( mtime_t i_stream, mtime_t i_syst
return p;
}
/* */
#define INPUT_CLOCK_LATE_COUNT (3)
/* */
struct input_clock_t
{
......@@ -143,6 +146,13 @@ struct input_clock_t
mtime_t i_next_drift_update;
average_t drift;
/* Late statistics */
struct
{
mtime_t pi_value[INPUT_CLOCK_LATE_COUNT];
unsigned i_index;
} late;
/* Current modifiers */
int i_rate;
mtime_t i_pts_delay;
......@@ -173,6 +183,10 @@ input_clock_t *input_clock_New( int i_rate )
cl->i_next_drift_update = VLC_TS_INVALID;
AvgInit( &cl->drift, 10 );
cl->late.i_index = 0;
for( int i = 0; i < INPUT_CLOCK_LATE_COUNT; i++ )
cl->late.pi_value[i] = 0;
cl->i_rate = i_rate;
cl->i_pts_delay = 0;
cl->b_paused = false;
......@@ -251,7 +265,13 @@ void input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
/* It does not take the decoder latency into account but it is not really
* the goal of the clock here */
const mtime_t i_system_expected = ClockStreamToSystem( cl, i_ck_stream + AvgGet( &cl->drift ) );
*pb_late = i_system_expected < i_ck_system - cl->i_pts_delay;
const mtime_t i_late = ( i_ck_system - cl->i_pts_delay ) - i_system_expected;
*pb_late = i_late > 0;
if( i_late > 0 )
{
cl->late.pi_value[cl->late.i_index] = i_late;
cl->late.i_index = ( cl->late.i_index + 1 ) % INPUT_CLOCK_LATE_COUNT;
}
vlc_mutex_unlock( &cl->lock );
}
......@@ -437,6 +457,14 @@ void input_clock_SetJitter( input_clock_t *cl,
{
vlc_mutex_lock( &cl->lock );
/* Update late observations */
const mtime_t i_delay_delta = i_pts_delay - cl->i_pts_delay;
for( int i = 0; i < INPUT_CLOCK_LATE_COUNT; i++ )
{
if( cl->late.pi_value[i] > 0 )
cl->late.pi_value[i] = __MAX( cl->late.pi_value[i] - i_delay_delta, 0 );
}
/* TODO always save the value, and when rebuffering use the new one if smaller
* TODO when increasing -> force rebuffering
*/
......@@ -453,6 +481,28 @@ void input_clock_SetJitter( input_clock_t *cl,
vlc_mutex_unlock( &cl->lock );
}
mtime_t input_clock_GetJitter( input_clock_t *cl )
{
vlc_mutex_lock( &cl->lock );
#if INPUT_CLOCK_LATE_COUNT != 3
# error "unsupported INPUT_CLOCK_LATE_COUNT"
#endif
/* Find the median of the last late values
* It works pretty well at rejecting bad values
*
* XXX we only increase pts_delay over time, decreasing it is
* not that easy if we want to be robust.
*/
const mtime_t *p = cl->late.pi_value;
mtime_t i_late_median = p[0] + p[1] + p[2] - __MIN(__MIN(p[0],p[1]),p[2]) - __MAX(__MAX(p[0],p[1]),p[2]);
mtime_t i_pts_delay = cl->i_pts_delay ;
vlc_mutex_unlock( &cl->lock );
return i_pts_delay + i_late_median;
}
/*****************************************************************************
* ClockStreamToSystem: converts a movie clock to system date
*****************************************************************************/
......
......@@ -121,5 +121,11 @@ int input_clock_GetState( input_clock_t *,
void input_clock_SetJitter( input_clock_t *,
mtime_t i_pts_delay, int i_cr_average );
/**
* This function returns an estimation of the pts_delay needed to avoid rebufferization.
* XXX in the current implementation, the pts_delay will never be decreased.
*/
mtime_t input_clock_GetJitter( input_clock_t * );
#endif
......@@ -2249,11 +2249,23 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
}
else if( b_late )
{
mtime_t i_pts_delay = input_clock_GetJitter( p_pgrm->p_clock );
/* Avoid dangerously high value */
const mtime_t i_pts_delay_max = 30000000;
if( i_pts_delay > i_pts_delay_max )
i_pts_delay = __MAX( i_pts_delay_max, p_sys->i_pts_delay );
/* Force a rebufferization when we are too late */
msg_Err( p_sys->p_input, "ES_OUT_SET_(GROUP_)PCR is called too late" );
msg_Err( p_sys->p_input,
"ES_OUT_SET_(GROUP_)PCR is called too late, increasing pts_delay to %d ms",
(int)(i_pts_delay/1000) );
/* It is not really good, as we throw away already buffered data
* TODO have a mean to correctly reenter bufferization */
EsOutChangePosition( out );
es_out_Control( out, ES_OUT_RESET_PCR );
es_out_Control( out, ES_OUT_SET_JITTER, i_pts_delay, p_sys->i_cr_average );
}
}
return VLC_SUCCESS;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment