Commit b69e1571 authored by Thomas Guillem's avatar Thomas Guillem

mediacodec: implement drain

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.
parent 59e8639b
...@@ -866,6 +866,13 @@ static int Video_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out, ...@@ -866,6 +866,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) 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); 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); p_pic = decoder_NewPicture(p_dec);
if (!p_pic) { if (!p_pic) {
msg_Warn(p_dec, "NewPicture failed"); msg_Warn(p_dec, "NewPicture failed");
...@@ -976,6 +983,13 @@ static int Audio_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out, ...@@ -976,6 +983,13 @@ static int Audio_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out,
if (p_out->type == MC_OUT_TYPE_BUF) if (p_out->type == MC_OUT_TYPE_BUF)
{ {
block_t *p_block = NULL; 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) { if (!p_sys->b_has_format) {
msg_Warn(p_dec, "Buffers returned before output format is set, dropping frame"); 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); return p_sys->api->release_out(p_sys->api, p_out->u.buf.i_index, false);
...@@ -1196,7 +1210,7 @@ static void *OutThread(void *data) ...@@ -1196,7 +1210,7 @@ static void *OutThread(void *data)
block_t *p_block = NULL; block_t *p_block = NULL;
if (p_sys->pf_process_output(p_dec, &out, &p_pic, 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"); msg_Err(p_dec, "pf_process_output failed");
vlc_restorecancel(canc); vlc_restorecancel(canc);
...@@ -1206,6 +1220,13 @@ static void *OutThread(void *data) ...@@ -1206,6 +1220,13 @@ static void *OutThread(void *data)
decoder_QueueVideo(p_dec, p_pic); decoder_QueueVideo(p_dec, p_pic);
else if (p_block) else if (p_block)
decoder_QueueAudio(p_dec, 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) } else if (i_ret != 0)
{ {
msg_Err(p_dec, "get_out failed"); msg_Err(p_dec, "get_out failed");
...@@ -1251,9 +1272,9 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block) ...@@ -1251,9 +1272,9 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
int i_flags = 0; int i_flags = 0;
int i_ret; int i_ret;
bool b_dequeue_timeout = false; 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; return 0;
vlc_mutex_lock(&p_sys->lock); vlc_mutex_lock(&p_sys->lock);
...@@ -1261,8 +1282,10 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block) ...@@ -1261,8 +1282,10 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
if (p_sys->b_error) if (p_sys->b_error)
goto end; goto end;
p_block = *pp_block; if (pp_block != NULL)
{
block_t *p_block = *pp_block;
b_draining = false;
if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED))
{ {
DecodeFlushLocked(p_dec); DecodeFlushLocked(p_dec);
...@@ -1306,13 +1329,27 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block) ...@@ -1306,13 +1329,27 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
} }
goto end; goto end;
} }
}
else
{
/* No input block, decoder is draining */
msg_Err(p_dec, "Decoder is draining");
b_draining = true;
if (!p_sys->b_output_ready )
{
/* Output no ready, no need to drain */
goto end;
}
}
/* Abort if MediaCodec is not yet started */ /* Abort if MediaCodec is not yet started */
if (!p_sys->api->b_started) if (!p_sys->api->b_started)
goto end; goto end;
/* Queue CSD blocks and input blocks */ /* 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; int i_index;
...@@ -1327,24 +1364,32 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block) ...@@ -1327,24 +1364,32 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
if (p_sys->b_error) if (p_sys->b_error)
goto end; goto end;
if (i_index >= 0) bool b_config = false;
{
bool b_config = p_block && (p_block->i_flags & BLOCK_FLAG_CSD);
mtime_t i_ts = 0; mtime_t i_ts = 0;
p_sys->b_input_dequeued = true; p_sys->b_input_dequeued = true;
const void *p_buf = NULL;
size_t i_size = 0;
if (i_index >= 0)
{
assert(b_draining || p_block != NULL);
if (p_block != NULL)
{
b_config = (p_block->i_flags & BLOCK_FLAG_CSD);
if (!b_config) if (!b_config)
{ {
i_ts = p_block->i_pts; i_ts = p_block->i_pts;
if (!i_ts && p_block->i_dts) if (!i_ts && p_block->i_dts)
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, if (p_sys->api->queue_in(p_sys->api, i_index, p_buf, i_size,
p_block->p_buffer, p_block->i_buffer, i_ts, i_ts, b_config) == 0)
b_config) == 0)
{ {
if (!b_config) if (!b_config && p_block != NULL)
{ {
if (p_block->i_flags & BLOCK_FLAG_PREROLL ) if (p_block->i_flags & BLOCK_FLAG_PREROLL )
p_sys->i_preroll_end = i_ts; p_sys->i_preroll_end = i_ts;
...@@ -1359,6 +1404,8 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block) ...@@ -1359,6 +1404,8 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block)
*pp_block = NULL; *pp_block = NULL;
} }
b_dequeue_timeout = false; b_dequeue_timeout = false;
if (b_draining)
break;
} else } else
{ {
msg_Err(p_dec, "queue_in failed"); msg_Err(p_dec, "queue_in failed");
...@@ -1397,6 +1444,27 @@ static int DecodeCommon(decoder_t *p_dec, block_t **pp_block) ...@@ -1397,6 +1444,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: end:
if (pp_block && *pp_block) if (pp_block && *pp_block)
{ {
......
...@@ -46,6 +46,7 @@ struct mc_api_out ...@@ -46,6 +46,7 @@ struct mc_api_out
MC_OUT_TYPE_BUF, MC_OUT_TYPE_BUF,
MC_OUT_TYPE_CONF, MC_OUT_TYPE_CONF,
} type; } type;
bool b_eos;
union union
{ {
struct struct
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#define THREAD_NAME "mediacodec_jni" #define THREAD_NAME "mediacodec_jni"
#define BUFFER_FLAG_CODEC_CONFIG 2 #define BUFFER_FLAG_CODEC_CONFIG 2
#define BUFFER_FLAG_END_OF_STREAM 4
#define INFO_OUTPUT_BUFFERS_CHANGED -3 #define INFO_OUTPUT_BUFFERS_CHANGED -3
#define INFO_OUTPUT_FORMAT_CHANGED -2 #define INFO_OUTPUT_FORMAT_CHANGED -2
#define INFO_TRY_AGAIN_LATER -1 #define INFO_TRY_AGAIN_LATER -1
...@@ -65,7 +66,7 @@ struct jfields ...@@ -65,7 +66,7 @@ struct jfields
jmethodID create_video_format, create_audio_format; jmethodID create_video_format, create_audio_format;
jmethodID set_integer, set_bytebuffer, get_integer; jmethodID set_integer, set_bytebuffer, get_integer;
jmethodID buffer_info_ctor; jmethodID buffer_info_ctor;
jfieldID size_field, offset_field, pts_field; jfieldID size_field, offset_field, pts_field, flags_field;
}; };
static struct jfields jfields; static struct jfields jfields;
...@@ -140,6 +141,7 @@ static const struct member members[] = { ...@@ -140,6 +141,7 @@ static const struct member members[] = {
{ "size", "I", "android/media/MediaCodec$BufferInfo", OFF(size_field), FIELD, true }, { "size", "I", "android/media/MediaCodec$BufferInfo", OFF(size_field), FIELD, true },
{ "offset", "I", "android/media/MediaCodec$BufferInfo", OFF(offset_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 }, { "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 }, { NULL, NULL, NULL, 0, 0, false },
}; };
...@@ -699,7 +701,8 @@ static int QueueInput(mc_api *api, int i_index, const void *p_buf, ...@@ -699,7 +701,8 @@ static int QueueInput(mc_api *api, int i_index, const void *p_buf,
uint8_t *p_mc_buf; uint8_t *p_mc_buf;
jobject j_mc_buf; jobject j_mc_buf;
jsize j_mc_size; 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); assert(i_index >= 0);
...@@ -787,6 +790,10 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out) ...@@ -787,6 +790,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, p_out->u.buf.i_ts = (*env)->GetLongField(env, p_sys->buffer_info,
jfields.pts_field); 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) if (api->b_direct_rendering)
{ {
p_out->u.buf.p_ptr = NULL; p_out->u.buf.p_ptr = NULL;
...@@ -795,8 +802,8 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out) ...@@ -795,8 +802,8 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
else else
{ {
jobject buf; jobject buf;
uint8_t *ptr; uint8_t *ptr = NULL;
int offset; int offset = 0;
if (jfields.get_output_buffers) if (jfields.get_output_buffers)
buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers, buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers,
...@@ -813,10 +820,14 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out) ...@@ -813,10 +820,14 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
} }
} }
//jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf); //jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
/* buf can be NULL in case of EOS */
if (buf)
{
ptr = (*env)->GetDirectBufferAddress(env, buf); ptr = (*env)->GetDirectBufferAddress(env, buf);
offset = (*env)->GetIntField(env, p_sys->buffer_info, offset = (*env)->GetIntField(env, p_sys->buffer_info,
jfields.offset_field); jfields.offset_field);
}
p_out->u.buf.p_ptr = ptr + offset; p_out->u.buf.p_ptr = ptr + offset;
p_out->u.buf.i_size = (*env)->GetIntField(env, p_sys->buffer_info, p_out->u.buf.i_size = (*env)->GetIntField(env, p_sys->buffer_info,
jfields.size_field); jfields.size_field);
...@@ -847,6 +858,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out) ...@@ -847,6 +858,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
(*env)->ReleaseStringUTFChars(env, format_string, format_ptr); (*env)->ReleaseStringUTFChars(env, format_string, format_ptr);
p_out->type = MC_OUT_TYPE_CONF; p_out->type = MC_OUT_TYPE_CONF;
p_out->b_eos = false;
if (api->b_video) if (api->b_video)
{ {
p_out->u.conf.video.width = GET_INTEGER(format, "width"); p_out->u.conf.video.width = GET_INTEGER(format, "width");
......
...@@ -408,7 +408,8 @@ static int QueueInput(mc_api *api, int i_index, const void *p_buf, ...@@ -408,7 +408,8 @@ static int QueueInput(mc_api *api, int i_index, const void *p_buf,
mc_api_sys *p_sys = api->p_sys; mc_api_sys *p_sys = api->p_sys;
uint8_t *p_mc_buf; uint8_t *p_mc_buf;
size_t i_mc_size; 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); assert(i_index >= 0);
...@@ -477,6 +478,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out) ...@@ -477,6 +478,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_index = i_index;
p_out->u.buf.i_ts = p_sys->info.presentationTimeUs; 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) if (api->b_direct_rendering)
{ {
...@@ -489,7 +491,8 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out) ...@@ -489,7 +491,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, uint8_t *p_mc_buf = syms.AMediaCodec.getOutputBuffer(p_sys->p_codec,
i_index, i_index,
&i_mc_size); &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"); msg_Err(api->p_obj, "AMediaCodec.getOutputBuffer failed");
return MC_API_ERROR; return MC_API_ERROR;
...@@ -504,6 +507,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out) ...@@ -504,6 +507,7 @@ static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
AMediaFormat *format = syms.AMediaCodec.getOutputFormat(p_sys->p_codec); AMediaFormat *format = syms.AMediaCodec.getOutputFormat(p_sys->p_codec);
p_out->type = MC_OUT_TYPE_CONF; p_out->type = MC_OUT_TYPE_CONF;
p_out->b_eos = false;
if (api->b_video) if (api->b_video)
{ {
p_out->u.conf.video.width = GetFormatInteger(format, "width"); p_out->u.conf.video.width = GetFormatInteger(format, "width");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment