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

Thomas Guillem thomas at gllm.fr
Wed Oct 29 17:47:02 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 | 268 ++++++++++++++++++++++++-------
 1 file changed, 208 insertions(+), 60 deletions(-)

diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c
index e352266..de8943a 100644
--- a/modules/codec/omxil/android_mediacodec.c
+++ b/modules/codec/omxil/android_mediacodec.c
@@ -161,7 +161,6 @@ struct decoder_sys_t
 
     bool allocated;
     bool started;
-    bool decoded;
     bool error_state;
     bool error_event_sent;
 
@@ -173,6 +172,17 @@ struct decoder_sys_t
     picture_t** inflight_picture; /**< stores the inflight picture for each output buffer or NULL */
 
     timestamp_fifo_t *timestamp_fifo;
+
+    vlc_mutex_t   input_mutex;
+    vlc_cond_t    input_cond;
+    vlc_thread_t  input_thread;
+    block_fifo_t *p_input_fifo;
+    enum {
+        INPUT_THREAD_STATE_RUNNING,
+        INPUT_THREAD_STATE_FLUSH,
+        INPUT_THREAD_STATE_EXIT,
+        INPUT_THREAD_STATE_ERROR,
+    } input_state;
 };
 
 enum Types
@@ -259,6 +269,13 @@ static void CloseDecoder(vlc_object_t *);
 
 static picture_t *DecodeVideo(decoder_t *, block_t **);
 
+static void *InputThread(void *data);
+static int InputThreadInit(decoder_t *p_dec);
+static void InputThreadDestroy(decoder_t *p_dec);
+static void InputThreadFlush(decoder_t *p_dec);
+static void InputThreadWake(decoder_t *p_dec);
+static bool InputThreadGetErrorState(decoder_t *p_dec);
+
 static void InvalidateAllPictures(decoder_t *);
 
 /*****************************************************************************
@@ -536,7 +553,6 @@ static int OpenDecoder(vlc_object_t *p_this)
         (*env)->ExceptionClear(env);
         goto error;
     }
-    p_sys->started = true;
 
     p_sys->input_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_input_buffers);
     p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_buffers);
@@ -552,6 +568,11 @@ static int OpenDecoder(vlc_object_t *p_this)
 
     jni_detach_thread();
 
+    if (InputThreadInit(p_dec) != 0)
+        goto error;
+
+    p_sys->started = true;
+
     const int timestamp_fifo_size = 32;
     p_sys->timestamp_fifo = timestamp_FifoNew(timestamp_fifo_size);
     if (!p_sys->timestamp_fifo)
@@ -578,6 +599,9 @@ static void CloseDecoder(vlc_object_t *p_this)
      * to prevent the vout from using destroyed output buffers. */
     if (p_sys->direct_rendering)
         InvalidateAllPictures(p_dec);
+
+    InputThreadDestroy(p_dec);
+
     jni_attach_thread(&env, THREAD_NAME);
     if (p_sys->input_buffers)
         (*env)->DeleteGlobalRef(env, p_sys->input_buffers);
@@ -722,11 +746,170 @@ 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;
 }
 
+static void InputThreadSetStateWait(decoder_t *p_dec,
+                                    unsigned int state,
+                                    unsigned int waiting_state)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    vlc_mutex_lock(&p_sys->input_mutex);
+    if (p_sys->input_state == INPUT_THREAD_STATE_ERROR) {
+        vlc_mutex_unlock(&p_sys->input_mutex);
+        return;
+    }
+    p_sys->input_state = state;
+    vlc_cond_signal(&p_sys->input_cond);
+    block_FifoWake(p_sys->p_input_fifo);
+
+    while (p_sys->input_state != waiting_state)
+        vlc_cond_wait(&p_sys->input_cond, &p_sys->input_mutex);
+
+    vlc_mutex_unlock(&p_sys->input_mutex);
+}
+
+static void InputThreadFlush(decoder_t *p_dec)
+{
+    InputThreadSetStateWait(p_dec, INPUT_THREAD_STATE_FLUSH,
+                            INPUT_THREAD_STATE_RUNNING);
+}
+
+static void InputThreadWake(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    vlc_mutex_lock(&p_sys->input_mutex);
+    vlc_cond_signal(&p_sys->input_cond);
+    vlc_mutex_unlock(&p_sys->input_mutex);
+}
+
+static bool InputThreadGetErrorState(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    bool ret;
+
+    vlc_mutex_lock(&p_sys->input_mutex);
+    ret = p_sys->input_state == INPUT_THREAD_STATE_ERROR;
+    vlc_mutex_unlock(&p_sys->input_mutex);
+
+    return ret;
+}
+
+static void InputThreadSetErrorState(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    vlc_mutex_lock(&p_sys->input_mutex);
+    p_sys->input_state = INPUT_THREAD_STATE_ERROR;
+    vlc_mutex_unlock(&p_sys->input_mutex);
+}
+
+static int InputThreadInit(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    p_sys->input_state = INPUT_THREAD_STATE_RUNNING;
+
+    p_sys->p_input_fifo = block_FifoNew();
+    if (!p_sys->p_input_fifo)
+        goto err;
+
+    vlc_mutex_init(&p_sys->input_mutex);
+    vlc_cond_init(&p_sys->input_cond);
+
+    if (vlc_clone(&p_sys->input_thread,
+                  InputThread, p_dec, VLC_THREAD_PRIORITY_LOW))
+        goto err;
+
+    return 0;
+err:
+    InputThreadDestroy(p_dec);
+    return -1;
+}
+
+static void InputThreadDestroy(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if (p_sys->input_thread) {
+        InputThreadSetStateWait(p_dec, INPUT_THREAD_STATE_EXIT,
+                                INPUT_THREAD_STATE_EXIT);
+        vlc_join(p_sys->input_thread, NULL);
+
+        vlc_mutex_destroy(&p_sys->input_mutex);
+        vlc_cond_destroy(&p_sys->input_cond);
+    }
+
+    if (p_sys->p_input_fifo)
+        block_FifoRelease(p_sys->p_input_fifo);
+}
+
+static void *InputThread(void *data)
+{
+    decoder_t *p_dec = data;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    block_t *p_block = NULL;
+    JNIEnv *env;
+    bool decoded = false;
+
+    jni_attach_thread(&env, THREAD_NAME);
+    if (!env) {
+        InputThreadSetErrorState(p_dec);
+        return NULL;
+    }
+
+    while (true) {
+        vlc_mutex_lock(&p_sys->input_mutex);
+        if (p_sys->input_state == INPUT_THREAD_STATE_RUNNING
+            && p_block != NULL) {
+            /* p_block is valid. PutInput didn't succeed to dequeue an input
+             * buffer, so wait here for 50ms. This thread can sill be waken up
+             * by others events (flush, exit...) */
+            vlc_cond_timedwait(&p_sys->input_cond, &p_sys->input_mutex,
+                               mdate() + 50000);
+        }
+        if (p_sys->input_state == INPUT_THREAD_STATE_FLUSH) {
+            if (p_block != NULL) {
+                block_Release(p_block);
+                p_block = NULL;
+            }
+            block_FifoEmpty(p_sys->p_input_fifo);
+            if (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);
+                    p_sys->error_state = true;
+                }
+                decoded = false;
+            }
+            p_sys->input_state = INPUT_THREAD_STATE_RUNNING;
+            vlc_cond_signal(&p_sys->input_cond);
+        } else if (p_sys->input_state != INPUT_THREAD_STATE_RUNNING) {
+            vlc_mutex_unlock(&p_sys->input_mutex);
+            break;
+        }
+        vlc_mutex_unlock(&p_sys->input_mutex);
+
+        if (p_block == NULL)
+            p_block = block_FifoGet(p_sys->p_input_fifo);
+
+        if (p_block != NULL) {
+            if (PutInput(p_dec, env, &p_block, 0) != 0) {
+                InputThreadSetErrorState(p_dec);
+                break;
+            } else if (p_block == NULL) {
+                decoded = true;
+            }
+        }
+    }
+    if (env != NULL)
+        jni_detach_thread();
+    return NULL;
+}
+
 static int GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong timeout)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
@@ -940,8 +1123,8 @@ 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 (InputThreadGetErrorState(p_dec))
+        p_sys->error_state = true;
 
     if (p_sys->error_state) {
         block_Release(*pp_block);
@@ -949,28 +1132,23 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
         goto endclean;
     }
 
-    jni_attach_thread(&env, THREAD_NAME);
-
-    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) {
+    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);
             /* 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;
-            }
+            InputThreadFlush(p_dec);
+            goto endclean;
         }
-        p_sys->decoded = false;
-        goto endclean;
+
+        block_FifoPut(p_sys->p_input_fifo, *pp_block);
+        *pp_block = NULL;
     }
 
     /* Use the aspect ratio provided by the input (ie read from packetizer).
@@ -981,46 +1159,18 @@ 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;
-        }
+    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 */
+        InputThreadWake(p_dec);
     }
 
 endclean:
@@ -1035,8 +1185,6 @@ endclean:
             p_sys->error_event_sent = true;
         }
     }
-    if (env != NULL)
-        jni_detach_thread();
 
     return p_pic;
 }
-- 
2.1.0




More information about the vlc-devel mailing list