]> git.sesse.net Git - vlc/commitdiff
Implemented buffering manipulation at the es_out level.
authorLaurent Aimar <fenrir@videolan.org>
Wed, 8 Oct 2008 19:20:05 +0000 (21:20 +0200)
committerLaurent Aimar <fenrir@videolan.org>
Wed, 8 Oct 2008 22:11:16 +0000 (00:11 +0200)
It allows faster seek and start up time when the access pace can be controlled.

src/input/clock.c
src/input/decoder.c
src/input/es_out.c
src/input/input_clock.h
src/input/input_decoder.h

index 50ec932f05a2bc0964d155ae826fdc9460150853..e06917a6c5b18db0a23449ba2ff61bf64546d7a7 100644 (file)
@@ -361,6 +361,42 @@ int input_clock_GetRate( input_clock_t *cl )
     return i_rate;
 }
 
+int input_clock_GetState( input_clock_t *cl,
+                          mtime_t *pi_stream_start, mtime_t *pi_system_start,
+                          mtime_t *pi_stream_duration, mtime_t *pi_system_duration )
+{
+    vlc_mutex_lock( &cl->lock );
+
+    if( !cl->b_has_reference )
+    {
+        vlc_mutex_unlock( &cl->lock );
+        return VLC_EGENERIC;
+    }
+
+    *pi_stream_start = cl->ref.i_stream;
+    *pi_system_start = cl->ref.i_system;
+
+    *pi_stream_duration = cl->last.i_stream - cl->ref.i_stream;
+    *pi_system_duration = cl->last.i_system - cl->ref.i_system;
+
+    vlc_mutex_unlock( &cl->lock );
+
+    return VLC_SUCCESS;
+}
+
+void input_clock_ChangeSystemOrigin( input_clock_t *cl, mtime_t i_system )
+{
+    vlc_mutex_lock( &cl->lock );
+
+    assert( cl->b_has_reference );
+    const mtime_t i_offset = i_system - cl->ref.i_system;
+
+    cl->ref.i_system += i_offset;
+    cl->last.i_system += i_offset;
+
+    vlc_mutex_unlock( &cl->lock );
+}
+
 /*****************************************************************************
  * ClockStreamToSystem: converts a movie clock to system date
  *****************************************************************************/
index c67117ae43d2901af830fa5b28091d04b2354412..41f79b0d09a3c324f2a889ab4b15ee83a4c29e14 100644 (file)
@@ -56,6 +56,8 @@ static void      *DecoderThread( vlc_object_t * );
 static int        DecoderProcess( decoder_t *, block_t * );
 static void       DecoderOutputChangePause( decoder_t *, bool b_paused, mtime_t i_date );
 static void       DecoderFlush( decoder_t * );
+static void       DecoderSignalBuffering( decoder_t *, bool );
+static void       DecoderFlushBuffering( decoder_t * );
 
 static void       DecoderUnsupportedCodec( decoder_t *, vlc_fourcc_t );
 
@@ -152,6 +154,10 @@ struct decoder_owner_sys_t
     mtime_t i_ts_delay;
 };
 
+#define DECODER_MAX_BUFFERING_COUNT (4)
+#define DECODER_MAX_BUFFERING_AUDIO_DURATION (AOUT_MAX_PREPARE_TIME)
+#define DECODER_MAX_BUFFERING_VIDEO_DURATION (1*CLOCK_FREQ)
+
 /*****************************************************************************
  * Public functions
  *****************************************************************************/
@@ -388,6 +394,8 @@ void input_DecoderDecode( decoder_t * p_dec, block_t *p_block )
 
 bool input_DecoderIsEmpty( decoder_t * p_dec )
 {
+    /* FIXME it is buggy if the decoder is buffering FIXME
+     * -> "deadlock" */
     if( p_dec->p_owner->b_own_thread &&
         block_FifoCount( p_dec->p_owner->p_fifo ) > 0 )
     {
@@ -548,6 +556,21 @@ void input_DecoderStopBuffering( decoder_t *p_dec )
     vlc_mutex_unlock( &p_owner->lock );
 }
 
+void input_DecoderWaitBuffering( decoder_t *p_dec )
+{
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
+    vlc_mutex_lock( &p_owner->lock );
+
+    while( vlc_object_alive( p_dec ) && p_owner->b_buffering && !p_owner->buffer.b_full )
+    {
+        block_FifoWake( p_owner->p_fifo );
+        vlc_cond_wait( &p_owner->wait, &p_owner->lock );
+    }
+
+    vlc_mutex_unlock( &p_owner->lock );
+}
+
 /*****************************************************************************
  * Internal functions
  *****************************************************************************/
@@ -735,26 +758,30 @@ static void *DecoderThread( vlc_object_t *p_this )
     decoder_t *p_dec = (decoder_t *)p_this;
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
-    block_t *p_block;
     int canc = vlc_savecancel();
 
     /* The decoder's main loop */
     while( vlc_object_alive( p_dec ) && !p_dec->b_error )
     {
-        if( ( p_block = block_FifoGet( p_owner->p_fifo ) ) == NULL )
-            continue;
+        block_t *p_block = block_FifoGet( p_owner->p_fifo );
 
-        if( DecoderProcess( p_dec, p_block ) != VLC_SUCCESS )
+        DecoderSignalBuffering( p_dec, p_block == NULL );
+
+        if( p_block && DecoderProcess( p_dec, p_block ) != VLC_SUCCESS )
             break;
     }
 
     while( vlc_object_alive( p_dec ) )
     {
+        block_t *p_block = block_FifoGet( p_owner->p_fifo );
+
+        DecoderSignalBuffering( p_dec, p_block == NULL );
+
         /* Trash all received PES packets */
-        p_block = block_FifoGet( p_owner->p_fifo );
         if( p_block )
             block_Release( p_block );
     }
+    DecoderSignalBuffering( p_dec, true );
 
     /* We do it here because of the dll loader that wants close() in the
      * same thread than open()/decode() */
@@ -815,6 +842,22 @@ static void DecoderSignalFlushed( decoder_t *p_dec )
     vlc_mutex_unlock( &p_owner->lock );
 }
 
+static void DecoderSignalBuffering( decoder_t *p_dec, bool b_full )
+{
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
+    vlc_mutex_lock( &p_owner->lock );
+
+    if( p_owner->b_buffering )
+    {
+        if( b_full )
+            p_owner->buffer.b_full = true;
+        vlc_cond_signal( &p_owner->wait );
+    }
+
+    vlc_mutex_unlock( &p_owner->lock );
+}
+
 static bool DecoderIsFlushing( decoder_t *p_dec )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
@@ -943,58 +986,111 @@ static void DecoderFixTs( decoder_t *p_dec, mtime_t *pi_ts0, mtime_t *pi_ts1,
     }
 }
 
-static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio, int i_block_rate,
+static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio,
                               int *pi_played_sum, int *pi_lost_sum )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
     aout_instance_t *p_aout = p_owner->p_aout;
     aout_input_t    *p_aout_input = p_owner->p_aout_input;
 
+    /* */
+    if( p_audio->start_date <= 0 )
+    {
+        msg_Warn( p_dec, "non-dated audio buffer received" );
+        *pi_lost_sum += 1;
+        aout_BufferFree( p_audio );
+        return;
+    }
+
+    /* */
     vlc_mutex_lock( &p_owner->lock );
 
-    bool b_reject;
-    DecoderWaitUnblock( p_dec, &b_reject );
+    if( p_owner->b_buffering || p_owner->buffer.p_audio )
+    {
+        p_audio->p_next = NULL;
 
-    int i_rate = i_block_rate > 0 ? i_block_rate : INPUT_RATE_DEFAULT;
-    mtime_t i_delay;
+        *p_owner->buffer.pp_audio_next = p_audio;
+        p_owner->buffer.pp_audio_next = &p_audio->p_next;
 
-    DecoderFixTs( p_dec, &p_audio->start_date, &p_audio->end_date, NULL,
-                  &i_rate, &i_delay, false );
+        p_owner->buffer.i_count++;
+        if( p_owner->buffer.i_count > DECODER_MAX_BUFFERING_COUNT ||
+            p_audio->start_date - p_owner->buffer.p_audio->start_date > DECODER_MAX_BUFFERING_AUDIO_DURATION )
+        {
+            p_owner->buffer.b_full = true;
+            vlc_cond_signal( &p_owner->wait );
+        }
+    }
 
-    vlc_mutex_unlock( &p_owner->lock );
+    for( ;; )
+    {
+        bool b_has_more = false;
+        bool b_reject;
+        DecoderWaitUnblock( p_dec, &b_reject );
 
-    /* */
-    const mtime_t i_max_date = mdate() + i_delay + AOUT_MAX_ADVANCE_TIME;
+        if( p_owner->b_buffering )
+        {
+            vlc_mutex_unlock( &p_owner->lock );
+            return;
+        }
 
-    if( !p_aout || !p_aout_input ||
-        p_audio->start_date <= 0 || p_audio->start_date > i_max_date ||
-        i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE ||
-        i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
-        b_reject = true;
+        /* */
+        if( p_owner->buffer.p_audio )
+        {
+            p_audio = p_owner->buffer.p_audio;
 
-    if( !b_reject )
-    {
-        /* Wait if we are too early
-         * FIXME that's plain ugly to do it here */
-        mwait( p_audio->start_date - AOUT_MAX_PREPARE_TIME );
+            p_owner->buffer.p_audio = p_audio->p_next;
 
-        if( !aout_DecPlay( p_aout, p_aout_input, p_audio, i_rate ) )
-            *pi_played_sum += 1;
-        *pi_lost_sum += aout_DecGetResetLost( p_aout, p_aout_input );
-    }
-    else
-    {
-        if( p_audio->start_date <= 0 )
+            b_has_more = p_owner->buffer.p_audio != NULL;
+            if( !b_has_more )
+                p_owner->buffer.pp_audio_next = &p_owner->buffer.p_audio;
+        }
+
+        /* */
+        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 );
+
+        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 ||
+            i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE ||
+            i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
+            b_reject = true;
+
+        if( !b_reject )
         {
-            msg_Warn( p_dec, "non-dated audio buffer received" );
+            /* Wait if we are too early
+             * FIXME that's plain ugly to do it here */
+            mwait( p_audio->start_date - AOUT_MAX_PREPARE_TIME );
+
+            if( !aout_DecPlay( p_aout, p_aout_input, p_audio, i_rate ) )
+                *pi_played_sum += 1;
+            *pi_lost_sum += aout_DecGetResetLost( p_aout, p_aout_input );
         }
-        else if( p_audio->start_date > i_max_date )
+        else
         {
-            msg_Warn( p_aout, "received buffer in the future (%"PRId64")",
-                      p_audio->start_date - mdate() );
+            if( p_audio->start_date <= 0 )
+            {
+                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 );
         }
-        *pi_lost_sum += 1;
-        aout_BufferFree( p_audio );
+
+        if( !b_has_more )
+            break;
+        vlc_mutex_lock( &p_owner->lock );
     }
 }
 
@@ -1039,8 +1135,7 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block )
             p_owner->i_preroll_end = -1;
         }
 
-        DecoderPlayAudio( p_dec, p_aout_buf, p_block ? p_block->i_rate : 0,
-                          &i_played, &i_lost );
+        DecoderPlayAudio( p_dec, p_aout_buf, &i_played, &i_lost );
     }
 
     /* Update ugly stat */
@@ -1145,58 +1240,111 @@ static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture,
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
     vout_thread_t  *p_vout = p_owner->p_vout;
 
+    if( p_picture->date <= 0 )
+    {
+        msg_Warn( p_vout, "non-dated video buffer received" );
+        *pi_lost_sum += 1;
+        VoutDisplayedPicture( p_vout, p_picture );
+        return;
+    }
+
+    /* */
     vlc_mutex_lock( &p_owner->lock );
 
-    bool b_reject;
-    DecoderWaitUnblock( p_dec, &b_reject );
+    if( p_owner->b_buffering || p_owner->buffer.p_picture )
+    {
+        p_picture->p_next = NULL;
 
-    int i_rate = INPUT_RATE_DEFAULT;
-    mtime_t i_delay;
+        *p_owner->buffer.pp_picture_next = p_picture;
+        p_owner->buffer.pp_picture_next = &p_picture->p_next;
 
-    DecoderFixTs( p_dec, &p_picture->date, NULL, NULL,
-                  &i_rate, &i_delay, false );
+        p_owner->buffer.i_count++;
+        if( p_owner->buffer.i_count > DECODER_MAX_BUFFERING_COUNT ||
+            p_picture->date - p_owner->buffer.p_picture->date > DECODER_MAX_BUFFERING_VIDEO_DURATION )
+        {
+            p_owner->buffer.b_full = true;
+            vlc_cond_signal( &p_owner->wait );
+        }
+    }
 
-    vlc_mutex_unlock( &p_owner->lock );
+    for( ;; )
+    {
+        bool b_has_more = false;
 
-    /* */
-    const mtime_t i_max_date = mdate() + i_delay + VOUT_BOGUS_DELAY;
+        bool b_reject;
+        DecoderWaitUnblock( p_dec, &b_reject );
 
-    if( p_picture->date <= 0 || p_picture->date >= i_max_date )
-        b_reject = true;
+        if( p_owner->b_buffering )
+        {
+            vlc_mutex_unlock( &p_owner->lock );
+            return;
+        }
 
-    if( !b_reject )
-    {
-        if( i_rate != p_owner->i_last_rate  )
+        /* */
+        if( p_owner->buffer.p_picture )
         {
-            /* Be sure to not display old picture after our own */
-            VoutFlushPicture( p_vout, p_picture->date );
-            p_owner->i_last_rate = i_rate;
+            p_picture = p_owner->buffer.p_picture;
+
+            p_owner->buffer.p_picture = p_picture->p_next;
+
+            b_has_more = p_owner->buffer.p_picture != NULL;
+            if( !b_has_more )
+                p_owner->buffer.pp_picture_next = &p_owner->buffer.p_picture;
         }
 
-        vout_DatePicture( p_vout, p_picture, p_picture->date );
+        /* */
+        int i_rate = INPUT_RATE_DEFAULT;
+        mtime_t i_delay;
 
-        vout_DisplayPicture( p_vout, p_picture );
-    }
-    else
-    {
-        if( p_picture->date <= 0 )
+        DecoderFixTs( p_dec, &p_picture->date, NULL, NULL,
+                      &i_rate, &i_delay, false );
+
+        vlc_mutex_unlock( &p_owner->lock );
+
+        /* */
+        const mtime_t i_max_date = mdate() + i_delay + VOUT_BOGUS_DELAY;
+
+        if( p_picture->date <= 0 || p_picture->date >= i_max_date )
+            b_reject = true;
+
+        if( !b_reject )
         {
-            msg_Warn( p_vout, "non-dated video buffer received" );
+            if( i_rate != p_owner->i_last_rate  )
+            {
+                /* Be sure to not display old picture after our own */
+                VoutFlushPicture( p_vout, p_picture->date );
+                p_owner->i_last_rate = i_rate;
+            }
+
+            vout_DatePicture( p_vout, p_picture, p_picture->date );
+
+            vout_DisplayPicture( p_vout, p_picture );
         }
         else
         {
-            msg_Warn( p_vout, "early picture skipped (%"PRId64")",
-                      p_picture->date - mdate() );
+            if( p_picture->date <= 0 )
+            {
+                msg_Warn( p_vout, "non-dated video buffer received" );
+            }
+            else
+            {
+                msg_Warn( p_vout, "early picture skipped (%"PRId64")",
+                          p_picture->date - mdate() );
+            }
+            *pi_lost_sum += 1;
+            VoutDisplayedPicture( p_vout, p_picture );
         }
-        *pi_lost_sum += 1;
-        VoutDisplayedPicture( p_vout, p_picture );
-    }
-    int i_tmp_display;
-    int i_tmp_lost;
-    vout_GetResetStatistic( p_vout, &i_tmp_display, &i_tmp_lost );
+        int i_tmp_display;
+        int i_tmp_lost;
+        vout_GetResetStatistic( p_vout, &i_tmp_display, &i_tmp_lost );
 
-    *pi_played_sum += i_tmp_display;
-    *pi_lost_sum += i_tmp_lost;
+        *pi_played_sum += i_tmp_display;
+        *pi_lost_sum += i_tmp_lost;
+
+        if( !b_has_more )
+            break;
+        vlc_mutex_lock( &p_owner->lock );
+    }
 }
 
 static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block )
@@ -1265,21 +1413,72 @@ static void DecoderPlaySpu( decoder_t *p_dec, subpicture_t *p_subpic,
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
     vout_thread_t *p_vout = p_owner->p_spu_vout;
 
-    /* Preroll does not work very well with subtitle */
+    /* */
+    if( p_subpic->i_start <= 0 )
+    {
+        msg_Warn( p_dec, "non-dated spu buffer received" );
+        subpicture_Delete( p_subpic );
+        return;
+    }
+
+    /* */
     vlc_mutex_lock( &p_owner->lock );
 
-    bool b_reject;
-    DecoderWaitUnblock( p_dec, &b_reject );
+    if( p_owner->b_buffering || p_owner->buffer.p_subpic )
+    {
+        p_subpic->p_next = NULL;
 
-    DecoderFixTs( p_dec, &p_subpic->i_start, &p_subpic->i_stop, NULL,
-                  NULL, NULL, b_telx );
+        *p_owner->buffer.pp_subpic_next = p_subpic;
+        p_owner->buffer.pp_subpic_next = &p_subpic->p_next;
 
-    vlc_mutex_unlock( &p_owner->lock );
+        p_owner->buffer.i_count++;
+        /* XXX it is important to be full after the first one */
+        if( p_owner->buffer.i_count > 0 )
+        {
+            p_owner->buffer.b_full = true;
+            vlc_cond_signal( &p_owner->wait );
+        }
+    }
 
-    if( !b_reject )
-        spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
-    else
-        subpicture_Delete( p_subpic );
+    for( ;; )
+    {
+        bool b_has_more = false;
+        bool b_reject;
+        DecoderWaitUnblock( p_dec, &b_reject );
+
+        if( p_owner->b_buffering )
+        {
+            vlc_mutex_unlock( &p_owner->lock );
+            return;
+        }
+
+        /* */
+        if( p_owner->buffer.p_subpic )
+        {
+            p_subpic = p_owner->buffer.p_subpic;
+
+            p_owner->buffer.p_subpic = p_subpic->p_next;
+
+            b_has_more = p_owner->buffer.p_subpic != NULL;
+            if( !b_has_more )
+                p_owner->buffer.pp_subpic_next = &p_owner->buffer.p_subpic;
+        }
+
+        /* */
+        DecoderFixTs( p_dec, &p_subpic->i_start, &p_subpic->i_stop, NULL,
+                      NULL, NULL, b_telx );
+
+        vlc_mutex_unlock( &p_owner->lock );
+
+        if( !b_reject )
+            spu_DisplaySubpicture( p_vout->p_spu, p_subpic );
+        else
+            subpicture_Delete( p_subpic );
+
+        if( !b_has_more )
+            break;
+        vlc_mutex_lock( &p_owner->lock );
+    }
 }
 
 static void DecoderPlaySout( decoder_t *p_dec, block_t *p_sout_block,
@@ -1302,6 +1501,52 @@ static void DecoderPlaySout( decoder_t *p_dec, block_t *p_sout_block,
     sout_InputSendBuffer( p_owner->p_sout_input, p_sout_block );
 }
 
+/* */
+static void DecoderFlushBuffering( decoder_t *p_dec )
+{
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
+    vlc_assert_locked( &p_owner->lock );
+
+    while( p_owner->buffer.p_picture )
+    {
+        picture_t *p_picture = p_owner->buffer.p_picture;
+
+        p_owner->buffer.p_picture = p_picture->p_next;
+        p_owner->buffer.i_count--;
+
+        if( p_owner->p_vout )
+            VoutDisplayedPicture( p_owner->p_vout, p_picture );
+
+        if( !p_owner->buffer.p_picture )
+            p_owner->buffer.pp_picture_next = &p_owner->buffer.p_picture;
+    }
+    while( p_owner->buffer.p_audio )
+    {
+        aout_buffer_t *p_audio = p_owner->buffer.p_audio;
+
+        p_owner->buffer.p_audio = p_audio->p_next;
+        p_owner->buffer.i_count--;
+
+        aout_BufferFree( p_audio );
+
+        if( !p_owner->buffer.p_audio )
+            p_owner->buffer.pp_audio_next = &p_owner->buffer.p_audio;
+    }
+    while( p_owner->buffer.p_subpic )
+    {
+        subpicture_t *p_subpic = p_owner->buffer.p_subpic;
+
+        p_owner->buffer.p_subpic = p_subpic->p_next;
+        p_owner->buffer.i_count--;
+
+        subpicture_Delete( p_subpic );
+
+        if( !p_owner->buffer.p_subpic )
+            p_owner->buffer.pp_subpic_next = &p_owner->buffer.p_subpic;
+    }
+}
+
 /* This function process a block for sout
  */
 static void DecoderProcessSout( decoder_t *p_dec, block_t *p_block )
@@ -1467,18 +1712,6 @@ static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block, bool b_flush
     vout_thread_t *p_vout;
     subpicture_t *p_spu;
 
-    if( b_flush && p_owner->p_spu_vout )
-    {
-        p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
-
-        if( p_vout && p_owner->p_spu_vout == p_vout )
-            spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
-                         p_owner->i_spu_channel );
-
-        if( p_vout )
-            vlc_object_release( p_vout );
-    }
-
     while( (p_spu = p_dec->pf_decode_sub( p_dec, p_block ? &p_block : NULL ) ) )
     {
         vlc_mutex_lock( &p_input->p->counters.counters_lock );
@@ -1507,6 +1740,18 @@ static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block, bool b_flush
         if( p_vout )
             vlc_object_release( p_vout );
     }
+
+    if( b_flush && p_owner->p_spu_vout )
+    {
+        p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
+
+        if( p_vout && p_owner->p_spu_vout == p_vout )
+            spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
+                         p_owner->i_spu_channel );
+
+        if( p_vout )
+            vlc_object_release( p_vout );
+    }
 }
 
 /**
@@ -1572,7 +1817,13 @@ static int DecoderProcess( decoder_t *p_dec, block_t *p_block )
 
     /* */
     if( b_flush_request )
+    {
+        vlc_mutex_lock( &p_owner->lock );
+        DecoderFlushBuffering( p_dec );
+        vlc_mutex_unlock( &p_owner->lock );
+
         DecoderSignalFlushed( p_dec );
+    }
 
     return p_dec->b_error ? VLC_EGENERIC : VLC_SUCCESS;
 }
@@ -1595,6 +1846,11 @@ static void DeleteDecoder( decoder_t * p_dec )
     block_FifoEmpty( p_owner->p_fifo );
     block_FifoRelease( p_owner->p_fifo );
 
+    /* */
+    vlc_mutex_lock( &p_owner->lock );
+    DecoderFlushBuffering( p_dec );
+    vlc_mutex_unlock( &p_owner->lock );
+
     /* Cleanup */
     if( p_owner->p_aout_input )
         aout_DecDelete( p_owner->p_aout, p_owner->p_aout_input );
@@ -1605,19 +1861,16 @@ static void DeleteDecoder( decoder_t * p_dec )
     }
     if( p_owner->p_vout )
     {
-        int i_pic;
-
-#define p_pic p_owner->p_vout->render.pp_picture[i_pic]
         /* Hack to make sure all the the pictures are freed by the decoder */
-        for( i_pic = 0; i_pic < p_owner->p_vout->render.i_pictures;
-             i_pic++ )
+        for( int i_pic = 0; i_pic < p_owner->p_vout->render.i_pictures; i_pic++ )
         {
+            picture_t *p_pic = p_owner->p_vout->render.pp_picture[i_pic];
+
             if( p_pic->i_status == RESERVED_PICTURE )
                 vout_DestroyPicture( p_owner->p_vout, p_pic );
             if( p_pic->i_refcount > 0 )
                 vout_UnlinkPicture( p_owner->p_vout, p_pic );
         }
-#undef p_pic
 
         /* We are about to die. Reattach video output to p_vlc. */
         vout_Request( p_dec, p_owner->p_vout, NULL );
@@ -1686,6 +1939,8 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
         /* Parameters changed, restart the aout */
         vlc_mutex_lock( &p_owner->lock );
 
+        DecoderFlushBuffering( p_dec );
+
         p_owner->p_aout_input = NULL;
         aout_DecDelete( p_owner->p_aout, p_aout_input );
 
@@ -1820,6 +2075,9 @@ static picture_t *vout_new_buffer( decoder_t *p_dec )
         p_owner->video = p_dec->fmt_out.video;
 
         vlc_mutex_lock( &p_owner->lock );
+
+        DecoderFlushBuffering( p_dec );
+
         p_vout = p_owner->p_vout;
         p_owner->p_vout = NULL;
         vlc_mutex_unlock( &p_owner->lock );
@@ -1871,6 +2129,9 @@ static picture_t *vout_new_buffer( decoder_t *p_dec )
         if( DecoderIsFlushing( p_dec ) )
             return NULL;
 
+        /* */
+        DecoderSignalBuffering( p_dec, true );
+
         /* Check the decoder doesn't leak pictures */
         for( i_pic = 0, i_ready_pic = 0; i_pic < p_owner->p_vout->render.i_pictures; i_pic++ )
         {
@@ -1960,6 +2221,12 @@ static subpicture_t *spu_new_buffer( decoder_t *p_dec )
 
     if( p_owner->p_spu_vout != p_vout )
     {
+        vlc_mutex_lock( &p_owner->lock );
+
+        DecoderFlushBuffering( p_dec );
+
+        vlc_mutex_unlock( &p_owner->lock );
+
         spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER,
                      &p_owner->i_spu_channel );
         p_owner->i_spu_order = 0;
index 6265d95aac3b687838329875905e81a9b2cdd803..4c6573ba227f45bfd5080416b90ffe4c2eac872d 100644 (file)
@@ -140,6 +140,9 @@ struct es_out_sys_t
     /* Rate used for clock */
     int         i_rate;
 
+    /* Used for buffering */
+    bool        b_buffering;
+
     /* Record */
     sout_instance_t *p_sout_record;
 };
@@ -155,6 +158,10 @@ static void         EsOutAddInfo( es_out_t *, es_out_id_t *es );
 static bool EsIsSelected( es_out_id_t *es );
 static void EsSelect( es_out_t *out, es_out_id_t *es );
 static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update );
+static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es );
+static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
+static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date );
+static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced );
 static char *LanguageGetName( const char *psz_code );
 static char *LanguageGetCode( const char *psz_lang );
 static char **LanguageSplit( const char *psz_langs );
@@ -274,6 +281,8 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
 
     p_sys->i_rate = i_rate;
 
+    p_sys->b_buffering = true;
+
     p_sys->p_sout_record = NULL;
 
     return out;
@@ -360,59 +369,17 @@ mtime_t input_EsOutGetWakeup( es_out_t *out )
         return 0;
 
     /* We do not have a wake up date if the input cannot have its speed
-     * controlled or sout is imposing its own */
-    if( !p_input->b_can_pace_control || p_input->p->b_out_pace_control )
+     * controlled or sout is imposing its own or while buffering
+     *
+     * FIXME for !p_input->b_can_pace_control a wkeup time is still needed to avoid too strong buffering */
+    if( !p_input->b_can_pace_control ||
+        p_input->p->b_out_pace_control ||
+        p_sys->b_buffering )
         return 0;
 
     return input_clock_GetWakeup( p_sys->p_pgrm->p_clock );
 }
 
-static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
-{
-    es_out_sys_t *p_sys = out->p_sys;
-
-    /* Pause decoders first */
-    for( int i = 0; i < p_sys->i_es; i++ )
-    {
-        es_out_id_t *es = p_sys->es[i];
-
-        /* Send a dummy block to let decoder know that
-         * there is a discontinuity */
-        if( es->p_dec )
-        {
-            input_DecoderChangePause( es->p_dec, b_paused, i_date );
-            if( es->p_dec_record )
-                input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
-        }
-    }
-}
-static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
-{
-    es_out_sys_t *p_sys = out->p_sys;
-
-    for( int i = 0; i < p_sys->i_pgrm; i++ )
-        input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date );
-}
-
-static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
-{
-    es_out_sys_t *p_sys = out->p_sys;
-
-    mtime_t i_delay = 0;
-    if( p_es->fmt.i_cat == AUDIO_ES )
-        i_delay = p_sys->i_audio_delay;
-    else if( p_es->fmt.i_cat == SPU_ES )
-        i_delay = p_sys->i_spu_delay;
-
-    if( i_delay != 0 )
-    {
-        if( p_es->p_dec )
-            input_DecoderChangeDelay( p_es->p_dec, i_delay );
-        if( p_es->p_dec_record )
-            input_DecoderChangeDelay( p_es->p_dec, i_delay );
-    }
-}
-
 void input_EsOutChangeRate( es_out_t *out, int i_rate )
 {
     es_out_sys_t      *p_sys = out->p_sys;
@@ -473,6 +440,8 @@ int input_EsOutSetRecord(  es_out_t *out, bool b_record )
                 continue;
 
             p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
+            if( p_es->p_dec_record && p_sys->b_buffering )
+                input_DecoderStartBuffering( p_es->p_dec_record );
         }
     }
     else
@@ -527,23 +496,21 @@ void input_EsOutChangePosition( es_out_t *out )
 
     for( int i = 0; i < p_sys->i_es; i++ )
     {
-        es_out_id_t *es = p_sys->es[i];
+        es_out_id_t *p_es = p_sys->es[i];
 
-        /* Send a dummy block to let decoder know that
-         * there is a discontinuity */
-        if( es->p_dec )
-        {
-            input_DecoderStartBuffering( es->p_dec );
-            input_DecoderStopBuffering( es->p_dec );
-            if( es->p_dec_record )
-            {
-                input_DecoderStartBuffering( es->p_dec_record );
-                input_DecoderStopBuffering( es->p_dec_record );
-            }
-        }
+        if( !p_es->p_dec )
+            continue;
+
+        input_DecoderStartBuffering( p_es->p_dec );
+
+        if( p_es->p_dec_record )
+            input_DecoderStartBuffering( p_es->p_dec );
     }
 
-    es_out_Control( out, ES_OUT_RESET_PCR );
+    for( int i = 0; i < p_sys->i_pgrm; i++ )
+        input_clock_Reset( p_sys->pgrm[i]->p_clock );
+
+    p_sys->b_buffering = true;
 }
 
 bool input_EsOutDecodersEmpty( es_out_t *out )
@@ -551,6 +518,13 @@ bool input_EsOutDecodersEmpty( es_out_t *out )
     es_out_sys_t      *p_sys = out->p_sys;
     int i;
 
+    if( p_sys->b_buffering && p_sys->p_pgrm )
+    {
+        EsOutDecodersStopBuffering( out, true );
+        if( p_sys->b_buffering )
+            return true;
+    }
+
     for( i = 0; i < p_sys->i_es; i++ )
     {
         es_out_id_t *es = p_sys->es[i];
@@ -566,6 +540,111 @@ bool input_EsOutDecodersEmpty( es_out_t *out )
 /*****************************************************************************
  *
  *****************************************************************************/
+static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+    int i_ret;
+
+    mtime_t i_stream_start;
+    mtime_t i_system_start;
+    mtime_t i_stream_duration;
+    mtime_t i_system_duration;
+    i_ret = input_clock_GetState( p_sys->p_pgrm->p_clock,
+                                  &i_stream_start, &i_system_start,
+                                  &i_stream_duration, &i_system_duration );
+    assert( !i_ret || b_forced );
+    if( i_ret )
+        return;
+
+    if( i_stream_duration <= p_sys->p_input->i_pts_delay && !b_forced )
+    {
+        msg_Dbg( p_sys->p_input, "Buffering %d%%", (int)(100 * i_stream_duration / p_sys->p_input->i_pts_delay) );
+        return;
+    }
+
+    msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
+              (int)(i_stream_duration/1000), (int)(i_system_duration/1000) );
+    p_sys->b_buffering = false;
+
+    const mtime_t i_decoder_buffering_start = mdate();
+    for( int i = 0; i < p_sys->i_es; i++ )
+    {
+        es_out_id_t *p_es = p_sys->es[i];
+
+        if( !p_es->p_dec )
+            continue;
+        input_DecoderWaitBuffering( p_es->p_dec );
+        if( p_es->p_dec_record )
+            input_DecoderWaitBuffering( p_es->p_dec_record );
+    }
+
+    msg_Dbg( p_sys->p_input, "Decoder buffering done in %d ms",
+              (int)(mdate() - i_decoder_buffering_start)/1000 );
+
+    const mtime_t i_ts_delay = 10*1000 + /* FIXME CLEANUP thread wake up time*/
+                               mdate();
+    //msg_Dbg( p_sys->p_input, "==> %lld", i_ts_delay - p_sys->p_input->i_pts_delay );
+    input_clock_ChangeSystemOrigin( p_sys->p_pgrm->p_clock, i_ts_delay - p_sys->p_input->i_pts_delay );
+
+    for( int i = 0; i < p_sys->i_es; i++ )
+    {
+        es_out_id_t *p_es = p_sys->es[i];
+
+        if( !p_es->p_dec )
+            continue;
+
+        input_DecoderStopBuffering( p_es->p_dec );
+        if( p_es->p_dec_record )
+            input_DecoderStopBuffering( p_es->p_dec );
+    }
+}
+static void EsOutDecodersChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+
+    /* Pause decoders first */
+    for( int i = 0; i < p_sys->i_es; i++ )
+    {
+        es_out_id_t *es = p_sys->es[i];
+
+        /* Send a dummy block to let decoder know that
+         * there is a discontinuity */
+        if( es->p_dec )
+        {
+            input_DecoderChangePause( es->p_dec, b_paused, i_date );
+            if( es->p_dec_record )
+                input_DecoderChangePause( es->p_dec_record, b_paused, i_date );
+        }
+    }
+}
+static void EsOutProgramChangePause( es_out_t *out, bool b_paused, mtime_t i_date )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+
+    for( int i = 0; i < p_sys->i_pgrm; i++ )
+        input_clock_ChangePause( p_sys->pgrm[i]->p_clock, b_paused, i_date );
+}
+
+static void EsOutDecoderChangeDelay( es_out_t *out, es_out_id_t *p_es )
+{
+    es_out_sys_t *p_sys = out->p_sys;
+
+    mtime_t i_delay = 0;
+    if( p_es->fmt.i_cat == AUDIO_ES )
+        i_delay = p_sys->i_audio_delay;
+    else if( p_es->fmt.i_cat == SPU_ES )
+        i_delay = p_sys->i_spu_delay;
+
+    if( i_delay != 0 )
+    {
+        if( p_es->p_dec )
+            input_DecoderChangeDelay( p_es->p_dec, i_delay );
+        if( p_es->p_dec_record )
+            input_DecoderChangeDelay( p_es->p_dec, i_delay );
+    }
+}
+
+
 static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language,
                                      bool b_delete )
 {
@@ -1205,8 +1284,18 @@ static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
     input_thread_t *p_input = p_sys->p_input;
 
     p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_input->p->p_sout );
-    if( p_es->p_dec && !p_es->p_master && p_sys->p_sout_record )
-        p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
+    if( p_es->p_dec )
+    {
+        if( p_sys->b_buffering )
+            input_DecoderStartBuffering( p_es->p_dec );
+
+        if( !p_es->p_master && p_sys->p_sout_record )
+        {
+            p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
+            if( p_es->p_dec_record && p_sys->b_buffering )
+                input_DecoderStartBuffering( p_es->p_dec_record );
+        }
+    }
 
     EsOutDecoderChangeDelay( out, p_es );
 }
@@ -1917,12 +2006,16 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
              * TODO do not use mdate() but proper stream acquisition date */
             input_clock_Update( p_pgrm->p_clock, VLC_OBJECT(p_sys->p_input),
                                 p_sys->p_input->b_can_pace_control, i_pcr, mdate() );
+            /* Check buffering state on master clock update */
+            if( p_sys->b_buffering && p_pgrm == p_sys->p_pgrm )
+                EsOutDecodersStopBuffering( out, false );
+
             return VLC_SUCCESS;
         }
 
         case ES_OUT_RESET_PCR:
-            for( i = 0; i < p_sys->i_pgrm; i++ )
-                input_clock_Reset( p_sys->pgrm[i]->p_clock );
+            msg_Err( p_sys->p_input, "ES_OUT_RESET_PCR called" );
+            input_EsOutChangePosition( out );
             return VLC_SUCCESS;
 
         case ES_OUT_GET_TS:
index 3b90aa64119dd72fe65fc056def6efac71f2f6f5..3790ee1d5aa9006fb56b9143dca2d461419b7da2 100644 (file)
@@ -75,6 +75,13 @@ void    input_clock_ChangeRate( input_clock_t *, int i_rate );
  */
 void    input_clock_ChangePause( input_clock_t *, bool b_paused, mtime_t i_date );
 
+/**
+ * This function allows to rebase the original system value date.
+ * It can be called only imediatly after a input_clock_Update call.
+ * FIXME ugly
+ */
+void    input_clock_ChangeSystemOrigin( input_clock_t *, mtime_t i_system );
+
 /**
  * This function converts a timestamp from stream clock to system clock.
  *
@@ -88,5 +95,13 @@ mtime_t input_clock_GetTS( input_clock_t *, int *pi_rate, mtime_t i_pts_delay, m
  */
 int input_clock_GetRate( input_clock_t * );
 
+/**
+ * This function returns current clock state or VLC_EGENERIC if there is not a
+ * reference point.
+ */
+int input_clock_GetState( input_clock_t *,
+                          mtime_t *pi_stream_start, mtime_t *pi_system_start,
+                          mtime_t *pi_stream_duration, mtime_t *pi_system_duration );
+
 #endif
 
index ea176d21c13973215fb504fccea8f328a74ac73b..81f414bf5ce21e7411302cdeb281dfe0178f9449 100644 (file)
@@ -51,6 +51,11 @@ void input_DecoderChangeDelay( decoder_t *, mtime_t i_delay );
  */
 void input_DecoderStartBuffering( decoder_t * );
 
+/**
+ * This function waits for the decoder to have buffered sufficient data.
+ */
+void input_DecoderWaitBuffering( decoder_t * );
+
 /**
  * This function stops the buffering mode.
  */