[vlc-devel] [PATCH 1/2] mediacodec: implement drain

Thomas Guillem thomas at gllm.fr
Mon Dec 14 18:46:11 CET 2015


When draining (pp_block == NULL), queue a dummy input buffer with a EOS flag,
and wait for the output thread to process all output frames.

This patch also add extra check when processing output buffers. Indeed, in some
case, the last output buffer with a EOS flag can be invalid.
---
 modules/codec/omxil/mediacodec.c     | 156 +++++++++++++++++++++++++----------
 modules/codec/omxil/mediacodec.h     |   1 +
 modules/codec/omxil/mediacodec_jni.c |  26 ++++--
 modules/codec/omxil/mediacodec_ndk.c |   8 +-
 4 files changed, 138 insertions(+), 53 deletions(-)

diff --git a/modules/codec/omxil/mediacodec.c b/modules/codec/omxil/mediacodec.c
index 569f3a3..ccbbe0e 100644
--- a/modules/codec/omxil/mediacodec.c
+++ b/modules/codec/omxil/mediacodec.c
@@ -840,6 +840,13 @@ static int Video_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out,
         if (p_out->u.buf.i_ts <= p_sys->i_preroll_end)
             return p_sys->api->release_out(p_sys->api, p_out->u.buf.i_index, false);
 
+        if (!p_sys->api->b_direct_rendering && p_out->u.buf.p_ptr == NULL)
+        {
+            /* This can happen when receiving an EOS buffer */
+            msg_Warn(p_dec, "Invalid buffer, dropping frame");
+            return p_sys->api->release_out(p_sys->api, p_out->u.buf.i_index, false);
+        }
+
         p_pic = decoder_NewPicture(p_dec);
         if (!p_pic) {
             msg_Warn(p_dec, "NewPicture failed");
@@ -950,6 +957,13 @@ static int Audio_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out,
     if (p_out->type == MC_OUT_TYPE_BUF)
     {
         block_t *p_block = NULL;
+        if (p_out->u.buf.p_ptr == NULL)
+        {
+            /* This can happen when receiving an EOS buffer */
+            msg_Warn(p_dec, "Invalid buffer, dropping frame");
+            return p_sys->api->release_out(p_sys->api, p_out->u.buf.i_index, false);
+        }
+
         if (!p_sys->b_has_format) {
             msg_Warn(p_dec, "Buffers returned before output format is set, dropping frame");
             return p_sys->api->release_out(p_sys->api, p_out->u.buf.i_index, false);
@@ -1170,7 +1184,7 @@ static void *OutThread(void *data)
                 block_t *p_block = NULL;
 
                 if (p_sys->pf_process_output(p_dec, &out, &p_pic,
-                                             &p_block) == -1)
+                                             &p_block) == -1 && !out.b_eos)
                 {
                     msg_Err(p_dec, "pf_process_output failed");
                     vlc_restorecancel(canc);
@@ -1180,6 +1194,13 @@ static void *OutThread(void *data)
                     decoder_QueueVideo(p_dec, p_pic);
                 else if (p_block)
                     decoder_QueueAudio(p_dec, p_block);
+
+                if (out.b_eos)
+                {
+                    msg_Warn(p_dec, "EOS received, abort OutThread");
+                    vlc_restorecancel(canc);
+                    break;
+                }
             } else if (i_ret != 0)
             {
                 msg_Err(p_dec, "get_out failed");
@@ -1225,9 +1246,9 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
     int i_flags = 0;
     int i_ret;
     bool b_dequeue_timeout = false;
-    block_t *p_block;
+    bool b_draining;
 
-    if (!pp_block || !*pp_block)
+    if (pp_block != NULL && *pp_block == NULL)
         return 0;
 
     vlc_mutex_lock(&p_sys->lock);
@@ -1235,50 +1256,65 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
     if (p_sys->b_error)
         goto end;
 
-    p_block = *pp_block;
-
-    if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED))
+    if (pp_block != NULL)
     {
-        DecodeFlushLocked(p_dec);
-        if (p_sys->b_error)
-            goto end;
-        if (p_block->i_flags & BLOCK_FLAG_CORRUPTED)
-            goto end;
-    }
-
-    /* Parse input block */
-    if ((i_ret = p_sys->pf_on_new_block(p_dec, p_block, &i_flags)) == 1)
-    {
-        if (i_flags & (NEWBLOCK_FLAG_FLUSH|NEWBLOCK_FLAG_RESTART))
+        block_t *p_block = *pp_block;
+        b_draining = false;
+        if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED))
         {
-            msg_Warn(p_dec, "Flushing from DecodeCommon");
-
-            /* Flush before restart to unblock OutThread */
             DecodeFlushLocked(p_dec);
             if (p_sys->b_error)
                 goto end;
+            if (p_block->i_flags & BLOCK_FLAG_CORRUPTED)
+                goto end;
+        }
 
-            if (i_flags & NEWBLOCK_FLAG_RESTART)
+        /* Parse input block */
+        if ((i_ret = p_sys->pf_on_new_block(p_dec, p_block, &i_flags)) == 1)
+        {
+            if (i_flags & (NEWBLOCK_FLAG_FLUSH|NEWBLOCK_FLAG_RESTART))
             {
-                msg_Warn(p_dec, "Restarting from DecodeCommon");
-                StopMediaCodec(p_dec);
-                if (StartMediaCodec(p_dec) != VLC_SUCCESS)
-                {
-                    msg_Err(p_dec, "StartMediaCodec failed");
-                    AbortDecoderLocked(p_dec);
+                msg_Warn(p_dec, "Flushing from DecodeCommon");
+
+                /* Flush before restart to unblock OutThread */
+                DecodeFlushLocked(p_dec);
+                if (p_sys->b_error)
                     goto end;
+
+                if (i_flags & NEWBLOCK_FLAG_RESTART)
+                {
+                    msg_Warn(p_dec, "Restarting from DecodeCommon");
+                    StopMediaCodec(p_dec);
+                    if (StartMediaCodec(p_dec) != VLC_SUCCESS)
+                    {
+                        msg_Err(p_dec, "StartMediaCodec failed");
+                        AbortDecoderLocked(p_dec);
+                        goto end;
+                    }
                 }
             }
         }
+        else
+        {
+            if (i_ret != 0)
+            {
+                AbortDecoderLocked(p_dec);
+                msg_Err(p_dec, "pf_on_new_block failed");
+            }
+            goto end;
+        }
     }
     else
     {
-        if (i_ret != 0)
+        /* No input block, decoder is draining */
+        msg_Err(p_dec, "Decoder is draining");
+        b_draining = true;
+
+        if (p_sys->i_csd_send < p_sys->i_csd_count)
         {
-            AbortDecoderLocked(p_dec);
-            msg_Err(p_dec, "pf_on_new_block failed");
+            /* Didn't sent any CSD, no need to drain */
+            goto end;
         }
-        goto end;
     }
 
     /* Abort if MediaCodec is not yet started */
@@ -1286,7 +1322,8 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
         goto end;
 
     /* Queue CSD blocks and input blocks */
-    while ((p_block = GetNextBlock(p_sys, *pp_block)))
+    block_t *p_block = NULL;
+    while (b_draining || (p_block = GetNextBlock(p_sys, *pp_block)))
     {
         int i_index;
 
@@ -1301,24 +1338,32 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
         if (p_sys->b_error)
             goto end;
 
+        bool b_config = false;
+        mtime_t i_ts = 0;
+        p_sys->b_input_dequeued = true;
+        const void *p_buf = NULL;
+        size_t i_size = 0;
+
         if (i_index >= 0)
         {
-            bool b_config = p_block && (p_block->i_flags & BLOCK_FLAG_CSD);
-            mtime_t i_ts = 0;
-            p_sys->b_input_dequeued = true;
-
-            if (!b_config)
+            assert(b_draining || p_block != NULL);
+            if (p_block != NULL)
             {
-                i_ts = p_block->i_pts;
-                if (!i_ts && p_block->i_dts)
-                    i_ts = p_block->i_dts;
+                b_config = (p_block->i_flags & BLOCK_FLAG_CSD);
+                if (!b_config)
+                {
+                    i_ts = p_block->i_pts;
+                    if (!i_ts && p_block->i_dts)
+                        i_ts = p_block->i_dts;
+                }
+                p_buf = p_block->p_buffer;
+                i_size = p_block->i_buffer;
             }
 
-            if (p_sys->api->queue_in(p_sys->api, i_index,
-                                     p_block->p_buffer, p_block->i_buffer, i_ts,
-                                     b_config) == 0)
+            if (p_sys->api->queue_in(p_sys->api, i_index, p_buf, i_size,
+                                     i_ts, b_config) == 0)
             {
-                if (!b_config)
+                if (!b_config && p_block != NULL)
                 {
                     if (p_block->i_flags & BLOCK_FLAG_PREROLL )
                         p_sys->i_preroll_end = i_ts;
@@ -1333,6 +1378,8 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
                     *pp_block = NULL;
                 }
                 b_dequeue_timeout = false;
+                if (b_draining)
+                    break;
             } else
             {
                 msg_Err(p_dec, "queue_in failed");
@@ -1371,6 +1418,27 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
         }
     }
 
+    if (b_draining)
+    {
+        msg_Warn(p_dec, "EOS sent, waiting for OutThread");
+
+        /* Wait for the OutThread to stop (and process all remaining output
+         * frames. Use a timeout here since we can't know if all decoders will
+         * behave correctly. */
+        mtime_t deadline = mdate() + INT64_C(1000000);
+        while (!p_sys->b_error
+            && vlc_cond_timedwait(&p_sys->dec_cond, &p_sys->lock, deadline) == 0);
+
+        if (!p_sys->b_error)
+            msg_Err(p_dec, "OutThread timed out");
+
+        /* In case pf_decode is called again (it shouldn't happen) */
+        p_sys->b_error_signaled = true;
+
+        vlc_mutex_unlock(&p_sys->lock);
+        return 0;
+    }
+
 end:
     if (pp_block && *pp_block)
     {
diff --git a/modules/codec/omxil/mediacodec.h b/modules/codec/omxil/mediacodec.h
index 0a2acd5..dda9056 100644
--- a/modules/codec/omxil/mediacodec.h
+++ b/modules/codec/omxil/mediacodec.h
@@ -46,6 +46,7 @@ struct mc_api_out
         MC_OUT_TYPE_BUF,
         MC_OUT_TYPE_CONF,
     } type;
+    bool b_eos;
     union
     {
         struct
diff --git a/modules/codec/omxil/mediacodec_jni.c b/modules/codec/omxil/mediacodec_jni.c
index 85e7190..b26b392 100644
--- a/modules/codec/omxil/mediacodec_jni.c
+++ b/modules/codec/omxil/mediacodec_jni.c
@@ -40,6 +40,7 @@
 #define THREAD_NAME "mediacodec_jni"
 
 #define BUFFER_FLAG_CODEC_CONFIG  2
+#define BUFFER_FLAG_END_OF_STREAM 4
 #define INFO_OUTPUT_BUFFERS_CHANGED -3
 #define INFO_OUTPUT_FORMAT_CHANGED  -2
 #define INFO_TRY_AGAIN_LATER        -1
@@ -65,7 +66,7 @@ struct jfields
     jmethodID create_video_format, create_audio_format;
     jmethodID set_integer, set_bytebuffer, get_integer;
     jmethodID buffer_info_ctor;
-    jfieldID size_field, offset_field, pts_field;
+    jfieldID size_field, offset_field, pts_field, flags_field;
 };
 static struct jfields jfields;
 
@@ -140,6 +141,7 @@ static const struct member members[] = {
     { "size", "I", "android/media/MediaCodec$BufferInfo", OFF(size_field), FIELD, true },
     { "offset", "I", "android/media/MediaCodec$BufferInfo", OFF(offset_field), FIELD, true },
     { "presentationTimeUs", "J", "android/media/MediaCodec$BufferInfo", OFF(pts_field), FIELD, true },
+    { "flags", "I", "android/media/MediaCodec$BufferInfo", OFF(flags_field), FIELD, true },
     { NULL, NULL, NULL, 0, 0, false },
 };
 
@@ -694,7 +696,8 @@ static int QueueInput(mc_api *api, int i_index, const void *p_buf,
     uint8_t *p_mc_buf;
     jobject j_mc_buf;
     jsize j_mc_size;
-    jint jflags = b_config ? BUFFER_FLAG_CODEC_CONFIG : 0;
+    jint jflags = (b_config ? BUFFER_FLAG_CODEC_CONFIG : 0)
+                | (p_buf == NULL ? BUFFER_FLAG_END_OF_STREAM : 0);
 
     assert(i_index >= 0);
 
@@ -782,6 +785,10 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
         p_out->u.buf.i_ts = (*env)->GetLongField(env, p_sys->buffer_info,
                                                  jfields.pts_field);
 
+        int flags = (*env)->GetIntField(env, p_sys->buffer_info,
+                                        jfields.flags_field);
+        p_out->b_eos = flags & BUFFER_FLAG_END_OF_STREAM;
+
         if (api->b_direct_rendering)
         {
             p_out->u.buf.p_ptr = NULL;
@@ -790,8 +797,8 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
         else
         {
             jobject buf;
-            uint8_t *ptr;
-            int offset;
+            uint8_t *ptr = NULL;
+            int offset = 0;
 
             if (jfields.get_output_buffers)
                 buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers,
@@ -808,10 +815,14 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
                 }
             }
             //jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
-            ptr = (*env)->GetDirectBufferAddress(env, buf);
+            /* buf can be NULL in case of EOS */
+            if (buf)
+            {
+                ptr = (*env)->GetDirectBufferAddress(env, buf);
 
-            offset = (*env)->GetIntField(env, p_sys->buffer_info,
-                                         jfields.offset_field);
+                offset = (*env)->GetIntField(env, p_sys->buffer_info,
+                                             jfields.offset_field);
+            }
             p_out->u.buf.p_ptr = ptr + offset;
             p_out->u.buf.i_size = (*env)->GetIntField(env, p_sys->buffer_info,
                                                        jfields.size_field);
@@ -842,6 +853,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
         (*env)->ReleaseStringUTFChars(env, format_string, format_ptr);
 
         p_out->type = MC_OUT_TYPE_CONF;
+        p_out->b_eos = false;
         if (api->b_video)
         {
             p_out->u.conf.video.width         = GET_INTEGER(format, "width");
diff --git a/modules/codec/omxil/mediacodec_ndk.c b/modules/codec/omxil/mediacodec_ndk.c
index 310160e..e402ff1 100644
--- a/modules/codec/omxil/mediacodec_ndk.c
+++ b/modules/codec/omxil/mediacodec_ndk.c
@@ -402,7 +402,8 @@ static int QueueInput(mc_api *api, int i_index, const void *p_buf,
     mc_api_sys *p_sys = api->p_sys;
     uint8_t *p_mc_buf;
     size_t i_mc_size;
-    int i_flags = b_config ? AMEDIACODEC_FLAG_CODEC_CONFIG : 0;
+    int i_flags = (b_config ? AMEDIACODEC_FLAG_CODEC_CONFIG : 0)
+                | (p_buf == NULL ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
 
     assert(i_index >= 0);
 
@@ -471,6 +472,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
         p_out->u.buf.i_index = i_index;
 
         p_out->u.buf.i_ts = p_sys->info.presentationTimeUs;
+        p_out->b_eos = p_sys->info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
 
         if (api->b_direct_rendering)
         {
@@ -483,7 +485,8 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
             uint8_t *p_mc_buf = syms.AMediaCodec.getOutputBuffer(p_sys->p_codec,
                                                                  i_index,
                                                                  &i_mc_size);
-            if (!p_mc_buf)
+            /* p_mc_buf can be NULL in case of EOS */
+            if (!p_mc_buf && !p_out->b_eos)
             {
                 msg_Err(api->p_obj, "AMediaCodec.getOutputBuffer failed");
                 return MC_API_ERROR;
@@ -498,6 +501,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
         AMediaFormat *format = syms.AMediaCodec.getOutputFormat(p_sys->p_codec);
 
         p_out->type = MC_OUT_TYPE_CONF;
+        p_out->b_eos = false;
         if (api->b_video)
         {
             p_out->u.conf.video.width         = GetFormatInteger(format, "width");
-- 
2.1.4



More information about the vlc-devel mailing list