[vlc-devel] [RFC PATCH 4/6] mediacodec: process input buffers in a separate thread

Thomas Guillem thomas at gllm.fr
Mon Nov 3 19:41:20 CET 2014


DecodeVideo will now send input blocks to a FIFO that will be processed by a
separate thread. This function will return instantaneously, either a valid
pic or NULL if there was no output buffers available. "invalid_picture hack" is
not needed anymore.

Fix a deadlock when a slow video was flushed, due to a seek.
(fixes #12397)
---
 modules/codec/omxil/android_mediacodec.c | 195 +++++++++++++++++++++----------
 1 file changed, 133 insertions(+), 62 deletions(-)

diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c
index e9d0b4e..4029416 100644
--- a/modules/codec/omxil/android_mediacodec.c
+++ b/modules/codec/omxil/android_mediacodec.c
@@ -42,6 +42,7 @@
 #include <OMX_Component.h>
 #include "omxil_utils.h"
 #include "android_opaque.h"
+#include "queue_thread.h"
 
 #define INFO_OUTPUT_BUFFERS_CHANGED -3
 #define INFO_OUTPUT_FORMAT_CHANGED  -2
@@ -161,7 +162,6 @@ struct decoder_sys_t
 
     bool allocated;
     bool started;
-    bool decoded;
     bool error_state;
     bool error_event_sent;
 
@@ -173,6 +173,10 @@ struct decoder_sys_t
     picture_t** inflight_picture; /**< stores the inflight picture for each output buffer or NULL */
 
     timestamp_fifo_t *timestamp_fifo;
+
+    bool            b_iqthread_decoded;
+    queue_thread   *p_iqthread;
+    JNIEnv         *p_iqthread_env;
 };
 
 enum Types
@@ -259,6 +263,8 @@ static void CloseDecoder(vlc_object_t *);
 
 static picture_t *DecodeVideo(decoder_t *, block_t **);
 
+static int PutInput(decoder_t *p_dec, JNIEnv *env, block_t **pp_block, jlong timeout);
+
 static void InvalidateAllPictures(decoder_t *);
 
 /*****************************************************************************
@@ -293,6 +299,80 @@ static int jstrcmp(JNIEnv* env, jobject str, const char* str2)
 }
 
 /*****************************************************************************
+ * QueueThread callbacks
+ *****************************************************************************/
+static int QueueThreadCb_Init(queue_thread *p_qthread)
+{
+    decoder_t *p_dec = (decoder_t*)p_qthread->p_owner;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    JNIEnv **pp_env = &p_sys->p_iqthread_env;
+
+    jni_attach_thread(pp_env, THREAD_NAME);
+    if (!*pp_env)
+        return -1;
+
+    return 0;
+}
+
+static int QueueThreadCb_Destroy(queue_thread *p_qthread)
+{
+    decoder_t *p_dec = (decoder_t*)p_qthread->p_owner;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    JNIEnv **pp_env = &p_sys->p_iqthread_env;
+
+    if (*pp_env)
+        jni_detach_thread();
+    *pp_env = NULL;
+
+    return 0;
+}
+
+static int InputQueueThreadCb_Run(queue_thread *p_qthread, bool b_flushing)
+{
+    decoder_t *p_dec = (decoder_t*)p_qthread->p_owner;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    JNIEnv *env = p_sys->p_iqthread_env;
+    queue_thread_elm *p_elm;
+    block_t *p_block;
+
+    if (!env)
+        return -1;
+
+    if (b_flushing) {
+        while ((p_elm = QueueThread_FifoPop(p_qthread))) {
+            block_t *p_block = (block_t *) p_elm;
+                block_Release( p_block );
+        }
+        if (p_sys->b_iqthread_decoded) {
+            (*env)->CallVoidMethod(env, p_sys->codec, p_sys->flush);
+            if ((*env)->ExceptionOccurred(env)) {
+                msg_Warn(p_dec, "Exception occurred in MediaCodec.flush");
+                (*env)->ExceptionClear(env);
+                return -1;
+            }
+            p_sys->b_iqthread_decoded = false;
+        }
+        return 0;
+    } else {
+        p_elm = QueueThread_FifoPeek(p_qthread);
+        p_block = (block_t *)p_elm;
+
+        if (!p_block)
+            return 0;
+
+        if (PutInput(p_dec, env, &p_block, 0) != 0)
+            return -1;
+
+        if (p_block == NULL) {
+            p_sys->b_iqthread_decoded = true;
+            QueueThread_FifoPop(p_qthread);
+            return 1;
+        } else
+            return 0;
+    }
+}
+
+/*****************************************************************************
  * OpenDecoder: Create the decoder instance
  *****************************************************************************/
 static int OpenDecoder(vlc_object_t *p_this)
@@ -552,6 +632,16 @@ static int OpenDecoder(vlc_object_t *p_this)
 
     jni_detach_thread();
 
+    p_sys->p_iqthread = QueueThread_New(VLC_OBJECT(p_dec));
+    if (!p_sys->p_iqthread)
+        goto error;
+    p_sys->p_iqthread->p_owner = (queue_thread_owner_sys *)p_dec;
+    p_sys->p_iqthread->pf_init = QueueThreadCb_Init;
+    p_sys->p_iqthread->pf_destroy = QueueThreadCb_Destroy;
+    p_sys->p_iqthread->pf_run = InputQueueThreadCb_Run;
+    if (QueueThread_Start(p_sys->p_iqthread) != 0)
+        goto error;
+
     const int timestamp_fifo_size = 32;
     p_sys->timestamp_fifo = timestamp_FifoNew(timestamp_fifo_size);
     if (!p_sys->timestamp_fifo)
@@ -574,10 +664,17 @@ static void CloseDecoder(vlc_object_t *p_this)
     if (!p_sys)
         return;
 
+    if (p_sys->p_iqthread)
+        QueueThread_Pause(p_sys->p_iqthread);
+
     /* Invalidate all pictures that are currently in flight in order
      * to prevent the vout from using destroyed output buffers. */
     if (p_sys->direct_rendering)
         InvalidateAllPictures(p_dec);
+
+    if (p_sys->p_iqthread)
+        QueueThread_Delete(p_sys->p_iqthread);
+
     jni_attach_thread(&env, THREAD_NAME);
     if (p_sys->input_buffers)
         (*env)->DeleteGlobalRef(env, p_sys->input_buffers);
@@ -722,7 +819,6 @@ static int PutInput(decoder_t *p_dec, JNIEnv *env, block_t **pp_block, jlong tim
     }
     block_Release(p_block);
     *pp_block = NULL;
-    p_sys->decoded = true;
 
     return 0;
 }
@@ -938,37 +1034,40 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
     picture_t *p_pic = NULL;
     JNIEnv *env = NULL;
 
-    if (!pp_block || !*pp_block)
-        return NULL;
+    if (QueueThread_GetErrorState(p_sys->p_iqthread))
+        p_sys->error_state = true;
 
     if (p_sys->error_state) {
-        block_Release(*pp_block);
-        *pp_block = NULL;
+        if( pp_block && *pp_block )
+        {
+            block_Release(*pp_block);
+            *pp_block = NULL;
+        }
         goto endclean;
     }
 
-    jni_attach_thread(&env, THREAD_NAME);
+    if (pp_block && *pp_block) {
+        if ((*pp_block)->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
+            block_Release(*pp_block);
+            *pp_block = NULL;
+            timestamp_FifoEmpty(p_sys->timestamp_fifo);
+
+            QueueThread_Pause(p_sys->p_iqthread);
 
-    if ((*pp_block)->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
-        block_Release(*pp_block);
-        *pp_block = NULL;
-        timestamp_FifoEmpty(p_sys->timestamp_fifo);
-        if (p_sys->decoded) {
             /* Invalidate all pictures that are currently in flight
              * since flushing make all previous indices returned by
              * MediaCodec invalid. */
             if (p_sys->direct_rendering)
                 InvalidateAllPictures(p_dec);
 
-            (*env)->CallVoidMethod(env, p_sys->codec, p_sys->flush);
-            if ((*env)->ExceptionOccurred(env)) {
-                msg_Warn(p_dec, "Exception occurred in MediaCodec.flush");
-                (*env)->ExceptionClear(env);
-                p_sys->error_state = true;
-            }
+            QueueThread_Flush(p_sys->p_iqthread);
+
+            QueueThread_Start(p_sys->p_iqthread);
+            return NULL;
+        } else {
+            QueueThread_FifoPush(p_sys->p_iqthread, (queue_thread_elm *)*pp_block);
+            *pp_block = NULL;
         }
-        p_sys->decoded = false;
-        goto endclean;
     }
 
     /* Use the aspect ratio provided by the input (ie read from packetizer).
@@ -979,47 +1078,21 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
         p_dec->fmt_out.video.i_sar_den = p_dec->fmt_in.video.i_sar_den;
     }
 
-    jlong timeout = 0;
-    const int max_polling_attempts = 50;
-    int attempts = 0;
-    while (*pp_block != NULL && p_pic == NULL) {
-        if (PutInput(p_dec, env, pp_block, (jlong) 0) != 0) {
-            p_sys->error_state = true;
-            break;
-        }
+    decoder_UpdateVideoFormat( p_dec );
+    
+    jni_attach_thread(&env, THREAD_NAME);
 
-        if (GetOutput(p_dec, env, &p_pic, timeout) != 0) {
-            p_sys->error_state = true;
-            break;
-        }
+    if (env == NULL || GetOutput(p_dec, env, &p_pic, 0) != 0)
+        p_sys->error_state = true;
 
-        if (p_pic == NULL && *pp_block != NULL) {
-            timeout = 30 * 1000;
-            ++attempts;
-            /* With opaque DR the output buffers are released by the
-               vout therefore we implement a timeout for polling in
-               order to avoid being indefinitely stalled in this loop. */
-            if (p_sys->direct_rendering && attempts == max_polling_attempts) {
-                p_pic = decoder_NewPicture(p_dec);
-                if (p_pic) {
-                    p_pic->date = VLC_TS_INVALID;
-                    picture_sys_t *p_picsys = p_pic->p_sys;
-                    p_picsys->pf_display_callback = NULL;
-                    p_picsys->pf_unlock_callback = NULL;
-                    p_picsys->p_dec = NULL;
-                    p_picsys->i_index = -1;
-                    p_picsys->b_valid = false;
-                }
-                else {
-                    /* If we cannot return a picture we must free the
-                       block since the decoder will proceed with the
-                       next block. */
-                    block_Release(*pp_block);
-                    *pp_block = NULL;
-                }
-            }
-        }
-    }
+    if (env != NULL)
+        jni_detach_thread();
+
+    if (p_pic) {
+        /* Wake the input thread. A new input buffer may be available if we got
+         * an output buffer */
+        QueueThread_Wake(p_sys->p_iqthread);
+     }
 
 endclean:
     if (p_sys->error_state) {
@@ -1033,8 +1106,6 @@ endclean:
             p_sys->error_event_sent = true;
         }
     }
-    if (env != NULL)
-        jni_detach_thread();
-
+ 
     return p_pic;
 }
-- 
2.1.1




More information about the vlc-devel mailing list