]> git.sesse.net Git - vlc/commitdiff
Automatically compute the latency(pts_delay) needed after a buffer underflow.
authorLaurent Aimar <fenrir@videolan.org>
Mon, 13 Jul 2009 19:04:44 +0000 (21:04 +0200)
committerLaurent Aimar <fenrir@videolan.org>
Mon, 13 Jul 2009 19:13:13 +0000 (21:13 +0200)
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).

src/input/clock.c
src/input/clock.h
src/input/es_out.c

index 9cad368323f64171534d6dd98e467e898a6a7492..6c0e32700c14600801cf040f7745abe97c5d4333 100644 (file)
@@ -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
  *****************************************************************************/
index b5f97b9ae6a38081d122e89c6045689118438e6d..abe23aaf6f2959c37f5e82b89169e4f7c743f446 100644 (file)
@@ -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
 
index 1f1d50ad3461b045f800ff3ca61de9f62e83b293..f32148af638148d6057fb52eae80c12bb73c0e3c 100644 (file)
@@ -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;