]> git.sesse.net Git - vlc/blobdiff - src/input/decoder.c
decoder: reduce lock scope and cosmetic
[vlc] / src / input / decoder.c
index 65dc51154a1b5ea1d06fac4744475f96dbe95ad6..d1da2226bf4eef09eda7742ce6a2cbaf7bfefa20 100644 (file)
@@ -89,6 +89,7 @@ struct decoder_owner_sys_t
     vlc_mutex_t lock;
     vlc_cond_t  wait_request;
     vlc_cond_t  wait_acknowledge;
+    vlc_cond_t  wait_fifo; /* TODO: merge with wait_acknowledge */
 
     /* -- These variables need locking on write(only) -- */
     audio_output_t *p_aout;
@@ -105,13 +106,15 @@ struct decoder_owner_sys_t
     } pause;
 
     /* Waiting */
-    bool b_woken;
     bool b_waiting;
     bool b_first;
     bool b_has_data;
 
     /* Flushing */
     bool b_flushing;
+    bool b_draining;
+    bool b_drained;
+    bool b_idle;
 
     /* CC */
     struct
@@ -167,21 +170,6 @@ static bool DecoderIsFlushing( decoder_t *p_dec )
     return b_flushing;
 }
 
-static void DecoderSignalWait( decoder_t *p_dec )
-{
-    decoder_owner_sys_t *p_owner = p_dec->p_owner;
-
-    vlc_mutex_lock( &p_owner->lock );
-
-    if( p_owner->b_waiting )
-    {
-        p_owner->b_has_data = true;
-        vlc_cond_signal( &p_owner->wait_acknowledge );
-    }
-
-    vlc_mutex_unlock( &p_owner->lock );
-}
-
 static block_t *DecoderBlockFlushNew()
 {
     block_t *p_null = block_Alloc( 128 );
@@ -448,7 +436,13 @@ static picture_t *vout_new_buffer( decoder_t *p_dec )
             return p_picture;
 
         /* */
-        DecoderSignalWait( p_dec );
+        vlc_mutex_lock( &p_owner->lock );
+        if( p_owner->b_waiting )
+        {
+            p_owner->b_has_data = true;
+            vlc_cond_signal( &p_owner->wait_acknowledge );
+        }
+        vlc_mutex_unlock( &p_owner->lock );
 
         /* Check the decoder doesn't leak pictures */
         vout_FixLeaks( p_owner->p_vout );
@@ -1073,7 +1067,7 @@ static void DecoderPlayAudio( decoder_t *p_dec, block_t *p_audio,
     audio_output_t *p_aout = p_owner->p_aout;
 
     /* */
-    if( p_audio && p_audio->i_pts <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify audio_output/*
+    if( p_audio->i_pts <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify audio_output/*
     {
         msg_Warn( p_dec, "non-dated audio buffer received" );
         *pi_lost_sum += 1;
@@ -1083,58 +1077,47 @@ static void DecoderPlayAudio( decoder_t *p_dec, block_t *p_audio,
 
     /* */
     vlc_mutex_lock( &p_owner->lock );
-
-    if( p_audio && p_owner->b_waiting )
+race:
+    if( p_owner->b_waiting )
     {
         p_owner->b_has_data = true;
         vlc_cond_signal( &p_owner->wait_acknowledge );
     }
 
-    for( ;; )
-    {
-        bool b_paused;
-
-        bool b_reject = DecoderWaitUnblock( p_dec );
-
-        b_paused = p_owner->b_paused;
-
-        if (!p_audio)
-            break;
-
-        /* */
-        int i_rate = INPUT_RATE_DEFAULT;
+    bool b_reject = DecoderWaitUnblock( p_dec );
+    bool b_paused = p_owner->b_paused;
 
-        DecoderFixTs( p_dec, &p_audio->i_pts, NULL, &p_audio->i_length,
-                      &i_rate, AOUT_MAX_ADVANCE_TIME );
+    /* */
+    int i_rate = INPUT_RATE_DEFAULT;
 
-        if( p_audio->i_pts <= VLC_TS_INVALID
-         || i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE
-         || i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
-            b_reject = true;
+    DecoderFixTs( p_dec, &p_audio->i_pts, NULL, &p_audio->i_length,
+                  &i_rate, AOUT_MAX_ADVANCE_TIME );
 
-        DecoderWaitDate( p_dec, &b_reject,
-                         p_audio->i_pts - AOUT_MAX_PREPARE_TIME );
+    if( p_audio->i_pts <= VLC_TS_INVALID
+     || i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE
+     || i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE )
+        b_reject = true;
 
-        if( unlikely(p_owner->b_paused != b_paused) )
-            continue; /* race with input thread? retry... */
-        if( p_aout == NULL )
-            b_reject = true;
+    DecoderWaitDate( p_dec, &b_reject,
+                     p_audio->i_pts - AOUT_MAX_PREPARE_TIME );
 
-        if( !b_reject )
-        {
-            assert( !p_owner->b_paused );
-            if( !aout_DecPlay( p_aout, p_audio, i_rate ) )
-                *pi_played_sum += 1;
-            *pi_lost_sum += aout_DecGetResetLost( p_aout );
-        }
-        else
-        {
-            msg_Dbg( p_dec, "discarded audio buffer" );
-            *pi_lost_sum += 1;
-            block_Release( p_audio );
-        }
+    if( unlikely(p_owner->b_paused != b_paused) )
+        goto race; /* race with input thread? retry... */
+    if( p_aout == NULL )
+        b_reject = true;
 
-        break;
+    if( !b_reject )
+    {
+        assert( !p_owner->b_paused );
+        if( !aout_DecPlay( p_aout, p_audio, i_rate ) )
+            *pi_played_sum += 1;
+        *pi_lost_sum += aout_DecGetResetLost( p_aout );
+    }
+    else
+    {
+        msg_Dbg( p_dec, "discarded audio buffer" );
+        *pi_lost_sum += 1;
+        block_Release( p_audio );
     }
     vlc_mutex_unlock( &p_owner->lock );
 }
@@ -1147,11 +1130,7 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block )
     int i_lost = 0;
     int i_played = 0;
 
-    if (!p_block) {
-        /* Play a NULL block to output buffered frames */
-        DecoderPlayAudio( p_dec, NULL, &i_played, &i_lost );
-    }
-    else while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) )
+    while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) )
     {
         if( DecoderIsFlushing( p_dec ) )
         {
@@ -1174,7 +1153,7 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block )
         {
             msg_Dbg( p_dec, "End of audio preroll" );
             if( p_owner->p_aout )
-                aout_DecFlush( p_owner->p_aout );
+                aout_DecFlush( p_owner->p_aout, false );
             /* */
             p_owner->i_preroll_end = VLC_TS_INVALID;
         }
@@ -1240,7 +1219,7 @@ static void DecoderProcessAudio( decoder_t *p_dec, block_t *p_block, bool b_flus
     }
 
     if( b_flush && p_owner->p_aout )
-        aout_DecFlush( p_owner->p_aout );
+        aout_DecFlush( p_owner->p_aout, false );
 }
 
 static void DecoderPlaySpu( decoder_t *p_dec, subpicture_t *p_subpic )
@@ -1445,41 +1424,42 @@ static void *DecoderThread( void *p_data )
         vlc_fifo_Lock( p_owner->p_fifo );
         vlc_fifo_CleanupPush( p_owner->p_fifo );
 
+        vlc_cond_signal( &p_owner->wait_fifo );
+
         while( vlc_fifo_IsEmpty( p_owner->p_fifo ) )
         {
-            if( p_owner->b_woken )
+            if( p_owner->b_draining )
+            {   /* We have emptied the FIFO and there is a pending request to
+                 * drain. Pass p_block = NULL to decoder just once. */
+                p_owner->b_draining = false;
                 break;
+            }
+
+            p_owner->b_idle = true;
             vlc_fifo_Wait( p_owner->p_fifo );
             /* Make sure there is no cancellation point other than this one^^.
              * If you need one, be sure to push cleanup of p_block. */
+            p_owner->b_idle = false;
         }
 
         p_block = vlc_fifo_DequeueUnlocked( p_owner->p_fifo );
-        if( p_block != NULL )
-            vlc_cond_signal( &p_owner->wait_acknowledge );
-
-        p_owner->b_woken = false;
         vlc_cleanup_run();
 
-        if( p_block == NULL || p_block->i_flags & BLOCK_FLAG_CORE_EOS )
-            DecoderSignalWait( p_dec );
+        int canc = vlc_savecancel();
+        DecoderProcess( p_dec, p_block );
 
-        if( p_block )
-        {
-            int canc = vlc_savecancel();
-
-            if( p_block->i_flags & BLOCK_FLAG_CORE_EOS )
-            {
-                /* calling DecoderProcess() with NULL block will make
-                 * decoders/packetizers flush their buffers */
-                block_Release( p_block );
-                p_block = NULL;
-            }
-
-            DecoderProcess( p_dec, p_block );
-
-            vlc_restorecancel( canc );
+        vlc_mutex_lock( &p_owner->lock );
+        if( p_block == NULL )
+        {   /* Draining: the decoder is drained and all decoded buffers are
+             * queued to the output at this point. Now drain the output. */
+            if( p_owner->p_aout != NULL )
+                aout_DecFlush( p_owner->p_aout, true );
         }
+        p_owner->b_drained = (p_block == NULL);
+
+        vlc_cond_signal( &p_owner->wait_acknowledge );
+        vlc_mutex_unlock( &p_owner->lock );
+        vlc_restorecancel( canc );
     }
     return NULL;
 }
@@ -1544,7 +1524,6 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     es_format_Init( &p_owner->fmt, UNKNOWN_ES, 0 );
 
     /* decoder fifo */
-    p_owner->b_woken = false;
     p_owner->p_fifo = block_FifoNew();
     if( unlikely(p_owner->p_fifo == NULL) )
     {
@@ -1618,6 +1597,7 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     vlc_mutex_init( &p_owner->lock );
     vlc_cond_init( &p_owner->wait_request );
     vlc_cond_init( &p_owner->wait_acknowledge );
+    vlc_cond_init( &p_owner->wait_fifo );
 
     p_owner->b_fmt_description = false;
     p_owner->p_description = NULL;
@@ -1631,6 +1611,9 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     p_owner->b_has_data = false;
 
     p_owner->b_flushing = false;
+    p_owner->b_draining = false;
+    p_owner->b_drained = false;
+    p_owner->b_idle = false;
 
     /* */
     p_owner->cc.b_supported = false;
@@ -1672,7 +1655,7 @@ static void DeleteDecoder( decoder_t * p_dec )
     if( p_owner->p_aout )
     {
         /* TODO: REVISIT gap-less audio */
-        aout_DecFlush( p_owner->p_aout );
+        aout_DecFlush( p_owner->p_aout, false );
         aout_DecDelete( p_owner->p_aout );
         input_resource_PutAout( p_owner->p_resource, p_owner->p_aout );
         if( p_owner->p_input != NULL )
@@ -1728,6 +1711,7 @@ static void DeleteDecoder( decoder_t * p_dec )
         vlc_object_release( p_owner->p_packetizer );
     }
 
+    vlc_cond_destroy( &p_owner->wait_fifo );
     vlc_cond_destroy( &p_owner->wait_acknowledge );
     vlc_cond_destroy( &p_owner->wait_request );
     vlc_mutex_destroy( &p_owner->lock );
@@ -1898,7 +1882,7 @@ void input_DecoderDecode( decoder_t *p_dec, block_t *p_block, bool b_do_pace )
          * Locking is not necessary as b_waiting is only read, not written by
          * the decoder thread. */
         while( vlc_fifo_GetCount( p_owner->p_fifo ) >= 10 )
-            vlc_fifo_WaitCond( p_owner->p_fifo, &p_owner->wait_acknowledge );
+            vlc_fifo_WaitCond( p_owner->p_fifo, &p_owner->wait_fifo );
     }
 
     vlc_fifo_QueueUnlocked( p_owner->p_fifo, p_block );
@@ -1908,20 +1892,23 @@ void input_DecoderDecode( decoder_t *p_dec, block_t *p_block, bool b_do_pace )
 bool input_DecoderIsEmpty( decoder_t * p_dec )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
     assert( !p_owner->b_waiting );
 
-    bool b_empty = block_FifoCount( p_dec->p_owner->p_fifo ) <= 0;
+    if( block_FifoCount( p_dec->p_owner->p_fifo ) > 0 )
+        return false;
+
+    bool b_empty;
+
+    vlc_mutex_lock( &p_owner->lock );
+    if( p_owner->fmt.i_cat == VIDEO_ES && p_owner->p_vout != NULL )
+        b_empty = vout_IsEmpty( p_owner->p_vout );
+    else if( p_owner->fmt.i_cat == AUDIO_ES )
+        b_empty = p_owner->b_drained;
+    else
+        b_empty = true; /* TODO subtitles support */
+    vlc_mutex_unlock( &p_owner->lock );
 
-    if( b_empty )
-    {
-        vlc_mutex_lock( &p_owner->lock );
-        /* TODO subtitles support */
-        if( p_owner->fmt.i_cat == VIDEO_ES && p_owner->p_vout )
-            b_empty = vout_IsEmpty( p_owner->p_vout );
-        else if( p_owner->fmt.i_cat == AUDIO_ES && p_owner->p_aout )
-            b_empty = aout_DecIsEmpty( p_owner->p_aout );
-        vlc_mutex_unlock( &p_owner->lock );
-    }
     return b_empty;
 }
 
@@ -1935,12 +1922,12 @@ bool input_DecoderIsEmpty( decoder_t * p_dec )
  */
 void input_DecoderDrain( decoder_t *p_dec )
 {
-    block_t *p_block = block_Alloc(0);
-    if( unlikely(p_block == NULL) )
-        return;
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
-    p_block->i_flags |= BLOCK_FLAG_CORE_EOS;
-    input_DecoderDecode( p_dec, p_block, false );
+    vlc_fifo_Lock( p_owner->p_fifo );
+    p_owner->b_draining = true;
+    vlc_fifo_Signal( p_owner->p_fifo );
+    vlc_fifo_Unlock( p_owner->p_fifo );
 }
 
 static void DecoderFlush( decoder_t *p_dec )
@@ -1949,8 +1936,11 @@ static void DecoderFlush( decoder_t *p_dec )
 
     vlc_assert_locked( &p_owner->lock );
 
+    vlc_fifo_Lock( p_owner->p_fifo );
     /* Empty the fifo */
-    block_FifoEmpty( p_owner->p_fifo );
+    block_ChainRelease( vlc_fifo_DequeueAllUnlocked( p_owner->p_fifo ) );
+    p_owner->b_draining = false; /* flush supersedes drain */
+    vlc_fifo_Unlock( p_owner->p_fifo );
 
     /* Monitor for flush end */
     p_owner->b_flushing = true;
@@ -2066,30 +2056,31 @@ void input_DecoderChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
-    vlc_mutex_lock( &p_owner->lock );
     /* Normally, p_owner->b_paused != b_paused here. But if a track is added
      * while the input is paused (e.g. add sub file), then b_paused is
-     * (incorrectly) false. */
-    if( likely(p_owner->b_paused != b_paused) ) {
-        p_owner->b_paused = b_paused;
-        p_owner->pause.i_date = i_date;
-        p_owner->pause.i_ignore = 0;
-        vlc_cond_signal( &p_owner->wait_request );
-
-        /* XXX only audio and video output have to be paused.
-         * - for sout it is useless
-         * - for subs, it is done by the vout
-         */
-        if( p_owner->fmt.i_cat == AUDIO_ES )
-        {
-            if( p_owner->p_aout )
-                aout_DecChangePause( p_owner->p_aout, b_paused, i_date );
-        }
-        else if( p_owner->fmt.i_cat == VIDEO_ES )
-        {
-            if( p_owner->p_vout )
-                vout_ChangePause( p_owner->p_vout, b_paused, i_date );
-        }
+     * (incorrectly) false. FIXME: This is a bug in the decoder owner. */
+    if( unlikely(p_owner->b_paused == b_paused) )
+        return;
+
+    vlc_mutex_lock( &p_owner->lock );
+    p_owner->b_paused = b_paused;
+    p_owner->pause.i_date = i_date;
+    p_owner->pause.i_ignore = 0;
+    vlc_cond_signal( &p_owner->wait_request );
+
+    /* XXX only audio and video output have to be paused.
+     * - for sout it is useless
+     * - for subs, it is done by the vout
+     */
+    if( p_owner->fmt.i_cat == AUDIO_ES )
+    {
+        if( p_owner->p_aout )
+            aout_DecChangePause( p_owner->p_aout, b_paused, i_date );
+    }
+    else if( p_owner->fmt.i_cat == VIDEO_ES )
+    {
+        if( p_owner->p_vout )
+            vout_ChangePause( p_owner->p_vout, b_paused, i_date );
     }
     vlc_mutex_unlock( &p_owner->lock );
 }
@@ -2136,14 +2127,21 @@ void input_DecoderWait( decoder_t *p_dec )
     assert( p_owner->b_waiting );
 
     vlc_mutex_lock( &p_owner->lock );
+    vlc_fifo_Lock( p_owner->p_fifo );
     while( !p_owner->b_has_data )
     {
-        vlc_fifo_Lock( p_owner->p_fifo );
-        p_owner->b_woken = true;
+        if( p_owner->b_idle )
+        {
+            assert( vlc_fifo_IsEmpty( p_owner->p_fifo ) );
+            msg_Warn( p_dec, "can't wait without data to decode" );
+            break;
+        }
         vlc_fifo_Signal( p_owner->p_fifo );
         vlc_fifo_Unlock( p_owner->p_fifo );
         vlc_cond_wait( &p_owner->wait_acknowledge, &p_owner->lock );
+        vlc_fifo_Lock( p_owner->p_fifo );
     }
+    vlc_fifo_Unlock( p_owner->p_fifo );
     vlc_mutex_unlock( &p_owner->lock );
 }