[vlc-devel] [RFC PATCH 09/12] decoder: save first input blocks if the module can fallback

Thomas Guillem thomas at gllm.fr
Tue Jul 19 19:36:31 CEST 2016


If the module can fallback, save (dup+malloc) first input blocks (up to 3MB)
until we get a first output. If the module asks to fallback, the saved input
blocks will be feed to the next decoder module.

Most of the case, the decoder will want to fallback after the first input
buffer.
---
 include/vlc_codec.h |   3 +
 src/input/decoder.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 155 insertions(+), 14 deletions(-)

diff --git a/include/vlc_codec.h b/include/vlc_codec.h
index 98b9bdc..bffd06d 100644
--- a/include/vlc_codec.h
+++ b/include/vlc_codec.h
@@ -64,6 +64,9 @@ struct decoder_t
 
     /* Tell the decoder if it is allowed to drop frames */
     bool                b_frame_drop_allowed;
+    /* Tell the decoder if this module can fallback (ask to load a next
+     * decoder). False by default. */
+    bool                b_can_fallback;
 
     /* All pf_decode_* and pf_packetize functions have the same behavior.
      *
diff --git a/src/input/decoder.c b/src/input/decoder.c
index 9a3acc5..0e70717 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -129,6 +129,14 @@ struct decoder_owner_sys_t
     mtime_t i_ts_delay;
 };
 
+struct fallback_fifo
+{
+    block_t *p_fifo;
+    block_t *p_current;
+    size_t   i_size;
+};
+#define FALLBACK_FIFO_MAX_SIZE (3 * 1024 * 1024)
+
 /* Pictures which are DECODER_BOGUS_VIDEO_DELAY or more in advance probably have
  * a bogus PTS and won't be displayed */
 #define DECODER_BOGUS_VIDEO_DELAY                ((mtime_t)(DEFAULT_PTS_DELAY * 30))
@@ -144,6 +152,7 @@ static int LoadDecoder( decoder_t *p_dec, bool b_packetizer,
                         void **pp_module_savectx )
 {
     p_dec->b_frame_drop_allowed = true;
+    p_dec->b_can_fallback = false;
     p_dec->i_extra_picture_buffers = 0;
 
     p_dec->pf_decode_audio = NULL;
@@ -1362,13 +1371,93 @@ static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block )
         DecoderQueueSpu( p_dec, p_spu );
 }
 
+static void fallback_fifo_Clear( struct fallback_fifo *p_fallback )
+{
+    block_ChainRelease( p_fallback->p_fifo );
+    p_fallback->p_fifo = p_fallback->p_current = NULL;
+    p_fallback->i_size = 0;
+}
+
+static void fallback_fifo_Queue( struct fallback_fifo *p_fallback,
+                                 block_t *p_block )
+{
+    block_t **pp_next;
+
+    for( pp_next = &p_fallback->p_fifo; *pp_next != NULL;
+         pp_next = &((*pp_next)->p_next) )
+        ;
+    *pp_next = p_block;
+    p_fallback->i_size += p_block->i_size;
+}
+
+static int DecoderFallbackSaveBlock( decoder_t *p_dec,
+                                     struct fallback_fifo *p_fallback,
+                                     block_t *p_block )
+{
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
+    if( !p_owner->b_has_data )
+    {
+        /* Save input blocks to the fallback fifo until we know that
+         * the module is capable of rendering data */
+
+        if( p_fallback->i_size + p_block->i_size >= FALLBACK_FIFO_MAX_SIZE )
+        {
+            fallback_fifo_Clear( p_fallback );
+            p_dec->b_can_fallback = false;
+        }
+        else
+        {
+            block_t *p_dup = block_Duplicate( p_block );
+            if( unlikely(p_dup == NULL) )
+                return VLC_ENOMEM;
+            fallback_fifo_Queue( p_fallback, p_dup );
+        }
+    }
+
+    if( unlikely(p_owner->b_has_data && p_fallback->p_fifo != NULL
+              && p_fallback->p_current == NULL) )
+    {
+        /* The fallback fifo is not used and is useless now (since we
+         * have data), clear it */
+        fallback_fifo_Clear( p_fallback );
+        p_dec->b_can_fallback = false;
+    }
+    return VLC_SUCCESS;
+}
+
+static block_t *DecoderFallbackGetBlock( decoder_t *p_dec,
+                                         struct fallback_fifo *p_fallback )
+{
+    block_t *p_block;
+
+    /* Use the fallback fifo as the input block source */
+    if( p_dec->b_can_fallback )
+    {
+        p_block = block_Duplicate( p_fallback->p_current );
+        if( unlikely(p_block == NULL) )
+            return NULL;
+        p_fallback->p_current = p_fallback->p_current->p_next;
+    }
+    else
+    {
+        /* This new decoder can't fallback, we can consume the fallback
+         * fifo directly */
+        p_block = p_fallback->p_current;
+        p_fallback->p_current = p_fallback->p_fifo = p_fallback->p_fifo->p_next;
+        p_block->p_next = NULL;
+    }
+    return p_block;
+}
+
 /**
  * Decode a block
  *
  * \param p_dec the decoder object
  * \param p_block the block to decode
  */
-static void DecoderProcess( decoder_t *p_dec, block_t *p_block )
+static void DecoderProcess( decoder_t *p_dec, block_t *p_block,
+                            struct fallback_fifo *p_fallback )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
@@ -1386,6 +1475,14 @@ static void DecoderProcess( decoder_t *p_dec, block_t *p_block )
         if( ReloadDecoder( p_dec, false, &p_dec->fmt_in,
                            &p_owner->p_module_savectx ) != VLC_SUCCESS )
             goto error;
+
+        if( p_fallback->p_fifo != NULL )
+        {
+            /* Use the fallback fifo as input block source until it's empty */
+            fallback_fifo_Queue( p_fallback, p_block );
+            p_block = NULL;
+            p_fallback->p_current = p_fallback->p_fifo;
+        }
     }
     if( p_dec->b_error )
         goto error;
@@ -1400,6 +1497,24 @@ static void DecoderProcess( decoder_t *p_dec, block_t *p_block )
         }
 
         DecoderUpdatePreroll( &p_owner->i_preroll_end, p_block );
+
+        if( p_dec->b_can_fallback
+         && DecoderFallbackSaveBlock( p_dec, p_fallback, p_block ) != VLC_SUCCESS )
+        {
+            vlc_mutex_unlock( &p_owner->lock );
+            goto error;
+        }
+    }
+    else if( unlikely(p_fallback->p_current != NULL) )
+    {
+        p_block = DecoderFallbackGetBlock( p_dec, p_fallback );
+        if( unlikely(p_block == NULL) )
+        {
+            vlc_mutex_unlock( &p_owner->lock );
+            goto error;
+        }
+
+        DecoderUpdatePreroll( &p_owner->i_preroll_end, p_block );
     }
     vlc_mutex_unlock( &p_owner->lock );
 
@@ -1476,6 +1591,14 @@ static void DecoderProcessFlush( decoder_t *p_dec )
     vlc_mutex_unlock( &p_owner->lock );
 }
 
+static void fallback_fifo_Cleanup( void *p_data )
+{
+    struct fallback_fifo *p_fallback = p_data;
+
+    if( p_fallback->p_fifo != NULL )
+        fallback_fifo_Clear( p_fallback );
+}
+
 /**
  * The decoding main loop
  *
@@ -1485,12 +1608,15 @@ static void *DecoderThread( void *p_data )
 {
     decoder_t *p_dec = (decoder_t *)p_data;
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
+    struct fallback_fifo fallback = { NULL, NULL, 0 };
     bool paused = false;
 
     /* The decoder's main loop */
     vlc_fifo_Lock( p_owner->p_fifo );
     vlc_fifo_CleanupPush( p_owner->p_fifo );
 
+    vlc_cleanup_push( fallback_fifo_Cleanup, &fallback );
+
     for( ;; )
     {
         if( p_owner->flushing )
@@ -1511,6 +1637,8 @@ static void *DecoderThread( void *p_data )
              * harmless). */
             p_owner->flushing = false;
 
+            if( unlikely(fallback.p_fifo != NULL) )
+                fallback_fifo_Clear( &fallback );
             continue;
         }
 
@@ -1545,27 +1673,36 @@ static void *DecoderThread( void *p_data )
         vlc_cond_signal( &p_owner->wait_fifo );
         vlc_testcancel(); /* forced expedited cancellation in case of stop */
 
-        block_t *p_block = vlc_fifo_DequeueUnlocked( p_owner->p_fifo );
-        if( p_block == NULL )
+        block_t *p_block;
+        if( likely(fallback.p_current == NULL) )
         {
-            if( likely(!p_owner->b_draining) )
-            {   /* Wait for a block to decode (or a request to drain) */
-                p_owner->b_idle = true;
-                vlc_fifo_Wait( p_owner->p_fifo );
-                p_owner->b_idle = false;
-                continue;
+            p_block = vlc_fifo_DequeueUnlocked( p_owner->p_fifo );
+            if( p_block == NULL )
+            {
+                if( likely(!p_owner->b_draining) )
+                {   /* Wait for a block to decode (or a request to drain) */
+                    p_owner->b_idle = true;
+                    vlc_fifo_Wait( p_owner->p_fifo );
+                    p_owner->b_idle = false;
+                    continue;
+                }
+                /* 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;
             }
-            /* 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;
+        }
+        else
+        {
+            /* The fallback fifo will be used */
+            p_block = NULL;
         }
 
         vlc_fifo_Unlock( p_owner->p_fifo );
 
         int canc = vlc_savecancel();
-        DecoderProcess( p_dec, p_block );
+        DecoderProcess( p_dec, p_block, &fallback );
 
-        if( p_block == NULL )
+        if( p_block == NULL && likely(fallback.p_current == 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 )
@@ -1583,6 +1720,7 @@ static void *DecoderThread( void *p_data )
         vlc_mutex_unlock( &p_owner->lock );
     }
     vlc_cleanup_pop();
+    vlc_cleanup_pop();
     vlc_assert_unreachable();
 }
 
-- 
2.8.1



More information about the vlc-devel mailing list