[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