[vlc-devel] [PATCH 11/11] mediacodec: add input/output threads, remove polling
Thomas Guillem
thomas at gllm.fr
Wed Aug 26 18:10:08 CEST 2015
This commit improve MediaCodec performances and remove polling every 10 msecs
when DecodeCommon is waiting for an input or an output buffer.
This commit adds two threads:
- InThread: Wait for a new block queued by DecodeCommon, wait indefinitely for
a MediaCodec input buffer (this can be cancelled by a flush), copy the block
into the input buffer and queue it.
- OutThread: Once an input buffer is queued, wait indefinitely for a MediaCodec
output buffer (this can be cancelled by a flush), queue this buffer to a Fifo
that will be dequeued by DecodeCommon. When a new output buffer is available,
notify the Decoder process that will call pf_decode with a NULL pp_block, and
returns a new picture.
Both threads, and pf_decode call are locked by the same mutex. Only
dequeue_in/dequeue_out from InThread/OutThread are not locked since these
functions can block.
---
modules/codec/omxil/mediacodec.c | 671 ++++++++++++++++++++++++++++++---------
1 file changed, 513 insertions(+), 158 deletions(-)
diff --git a/modules/codec/omxil/mediacodec.c b/modules/codec/omxil/mediacodec.c
index bed8225..33b0a01 100644
--- a/modules/codec/omxil/mediacodec.c
+++ b/modules/codec/omxil/mediacodec.c
@@ -53,6 +53,7 @@
extern void jni_EventHardwareAccelerationError(); // TODO REMOVE
#define BLOCK_FLAG_CSD (0x01 << BLOCK_FLAG_PRIVATE_SHIFT)
+#define BLOCK_FLAG_NEW (0x02 << BLOCK_FLAG_PRIVATE_SHIFT)
/* Codec Specific Data */
struct csd
@@ -80,6 +81,14 @@ typedef void (*dec_on_flush_cb)(decoder_t *);
*/
typedef int (*dec_process_output_cb)(decoder_t *, mc_api_out *, picture_t **, block_t **);
+struct out_fifo
+{
+ uint32_t begin;
+ uint32_t size;
+ uint32_t capacity;
+ mc_api_out *p_outs;
+};
+
struct decoder_sys_t
{
mc_api *api;
@@ -87,7 +96,7 @@ struct decoder_sys_t
const char *mime;
- /* Codec Specific Data buffer: sent in PutInput after a start or a flush
+ /* Codec Specific Data buffer: sent in InThread after a start or a flush
* with the BUFFER_FLAG_CODEC_CONFIG flag.*/
block_t **pp_csd;
size_t i_csd_count;
@@ -96,9 +105,7 @@ struct decoder_sys_t
bool b_update_format;
bool b_has_format;
- bool decoded;
bool error_state;
- bool b_new_block;
int64_t i_preroll_end;
/* Specific Audio/Video callbacks */
@@ -106,6 +113,20 @@ struct decoder_sys_t
dec_on_flush_cb pf_on_flush;
dec_process_output_cb pf_process_output;
+ vlc_mutex_t lock;
+ vlc_cond_t cond;
+ vlc_cond_t flush_cond;
+ vlc_cond_t dec_cond;
+ vlc_thread_t in_thread;
+ vlc_thread_t out_thread;
+ block_t *p_in_block;
+ struct out_fifo out_fifo;
+ bool b_flush_in;
+ bool b_flush_out;
+ bool b_output_ready;
+ bool b_mc_running;
+ bool b_threads_running;
+
union
{
struct
@@ -148,6 +169,11 @@ static void Audio_OnFlush(decoder_t *);
static int Audio_ProcessOutput(decoder_t *, mc_api_out *, picture_t **, block_t **);
static block_t *DecodeAudio(decoder_t *, block_t **);
+static int DecodeFlush(decoder_t *, bool);
+static void StopMediaCodec(decoder_t *);
+static void *InThread(void *);
+static void *OutThread(void *);
+
static void InvalidateAllPictures(decoder_t *);
static int InsertInflightPicture(decoder_t *, picture_t *, unsigned int );
@@ -182,6 +208,59 @@ vlc_module_begin ()
add_shortcut( "mediacodec_jni" )
vlc_module_end ()
+static int OutFifo_Init(struct out_fifo *p_fifo, uint32_t capacity)
+{
+ p_fifo->p_outs = malloc(capacity * sizeof(mc_api_out));
+ if (!p_fifo->p_outs)
+ return -1;
+ p_fifo->begin = p_fifo->size = 0;
+ p_fifo->capacity = capacity;
+ return 0;
+}
+
+static void OutFifo_Deinit(struct out_fifo *p_fifo)
+{
+ free(p_fifo->p_outs);
+}
+
+static bool OutFifo_IsFull(struct out_fifo *p_fifo)
+{
+ return p_fifo->size == p_fifo->capacity;
+}
+
+static int OutFifo_Put(struct out_fifo *p_fifo, mc_api_out *p_out)
+{
+ if (OutFifo_IsFull(p_fifo))
+ return -1;
+ uint32_t end = (p_fifo->begin + p_fifo->size) % p_fifo->capacity;
+ p_fifo->p_outs[end] = *p_out;
+ p_fifo->size++;
+ return 0;
+}
+
+static mc_api_out *OutFifo_Get(struct out_fifo *p_fifo)
+{
+ if (p_fifo->size == 0)
+ return NULL;
+ mc_api_out *p_out = &p_fifo->p_outs[p_fifo->begin];
+ p_fifo->begin = (p_fifo->begin + 1) % p_fifo->capacity;
+ p_fifo->size--;
+ return p_out;
+}
+
+static int OutFifo_Clear(struct out_fifo *p_fifo)
+{
+ struct mc_api_out *p_out, *p_out_cfg = NULL;
+
+ while ((p_out = OutFifo_Get(p_fifo)))
+ {
+ if (p_out->type == MC_OUT_TYPE_CONF)
+ p_out_cfg = p_out;
+ }
+ if (p_out_cfg)
+ OutFifo_Put(p_fifo, p_out_cfg);
+ return 0;
+}
static void CSDFree(decoder_t *p_dec)
{
@@ -197,7 +276,7 @@ static void CSDFree(decoder_t *p_dec)
p_sys->i_csd_count = 0;
}
-/* Create the p_sys->p_csd that will be sent via PutInput */
+/* Create the p_sys->p_csd that will be sent from InThread */
static int CSDDup(decoder_t *p_dec, const struct csd *p_csd, size_t i_count)
{
decoder_sys_t *p_sys = p_dec->p_sys;
@@ -442,11 +521,6 @@ static void StopMediaCodec(decoder_t *p_dec)
{
decoder_sys_t *p_sys = p_dec->p_sys;
- /* Invalidate all pictures that are currently in flight in order
- * to prevent the vout from using destroyed output buffers. */
- if (p_sys->api->b_direct_rendering)
- InvalidateAllPictures(p_dec);
-
p_sys->api->stop(p_sys->api);
if (p_dec->fmt_in.i_cat == VIDEO_ES && p_sys->u.video.p_awh)
AWindowHandler_releaseSurface(p_sys->u.video.p_awh, AWindow_Video);
@@ -461,6 +535,7 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
decoder_sys_t *p_sys;
mc_api *api;
const char *mime = NULL;
+ bool b_late_opening = false;
/* Video or Audio if "mediacodec-audio" bool is true */
if (p_dec->fmt_in.i_cat != VIDEO_ES && (p_dec->fmt_in.i_cat != AUDIO_ES
@@ -539,6 +614,7 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
{
api->clean(api);
free(api);
+ free(p_sys);
return VLC_ENOMEM;
}
p_sys->api = api;
@@ -552,7 +628,12 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
p_dec->fmt_out.audio = p_dec->fmt_in.audio;
p_dec->b_need_packetized = true;
p_sys->mime = mime;
- p_sys->b_new_block = true;
+
+ vlc_mutex_init(&p_sys->lock);
+ vlc_cond_init(&p_sys->cond);
+ vlc_cond_init(&p_sys->flush_cond);
+ vlc_cond_init(&p_sys->dec_cond);
+ OutFifo_Init(&p_sys->out_fifo, 32);
if (p_dec->fmt_in.i_cat == VIDEO_ES)
{
@@ -583,14 +664,15 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
{
msg_Warn(p_dec, "waiting for sps/pps for codec %4.4s",
(const char *)&p_dec->fmt_in.i_codec);
- return VLC_SUCCESS;
+ b_late_opening = true;
+ break;
}
case VLC_CODEC_VC1:
if (!p_dec->fmt_in.i_extra)
{
msg_Warn(p_dec, "waiting for extra data for codec %4.4s",
(const char *)&p_dec->fmt_in.i_codec);
- return VLC_SUCCESS;
+ b_late_opening = true;
}
break;
}
@@ -620,21 +702,46 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
{
msg_Warn(p_dec, "waiting for extra data for codec %4.4s",
(const char *)&p_dec->fmt_in.i_codec);
- return VLC_SUCCESS;
+ b_late_opening = true;
}
break;
}
if (!p_sys->u.audio.i_channels && p_sys->u.audio.b_need_channels)
{
msg_Warn(p_dec, "waiting for valid channel count");
- return VLC_SUCCESS;
+ b_late_opening = true;
}
}
- if (StartMediaCodec(p_dec) == VLC_SUCCESS)
- return VLC_SUCCESS;
- else
+ vlc_mutex_lock(&p_sys->lock);
+
+ if (!b_late_opening && StartMediaCodec(p_dec) != VLC_SUCCESS)
+ {
msg_Err(p_dec, "StartMediaCodec failed");
+ vlc_mutex_unlock(&p_sys->lock);
+ goto bailout;
+ }
+
+ p_sys->b_threads_running = true;
+ int i_in_thread = vlc_clone(&p_sys->in_thread, InThread, p_dec,
+ VLC_THREAD_PRIORITY_LOW);
+ int i_out_thread = vlc_clone(&p_sys->out_thread, OutThread, p_dec,
+ VLC_THREAD_PRIORITY_LOW);
+ if (i_in_thread || i_out_thread)
+ {
+ msg_Err(p_dec, "vlc_clone failed");
+ p_sys->b_threads_running = false;
+ vlc_mutex_unlock(&p_sys->lock);
+ if (!i_in_thread)
+ vlc_join(p_sys->in_thread, NULL);
+ if (!i_out_thread)
+ vlc_join(p_sys->out_thread, NULL);
+ goto bailout;
+ }
+ vlc_mutex_unlock(&p_sys->lock);
+
+ return VLC_SUCCESS;
+
bailout:
CloseDecoder(p_this);
return VLC_EGENERIC;
@@ -661,6 +768,26 @@ static void CloseDecoder(vlc_object_t *p_this)
if (!p_sys)
return;
+ vlc_mutex_lock(&p_sys->lock);
+ if (p_sys->b_threads_running)
+ {
+ p_sys->b_threads_running = false;
+ DecodeFlush(p_dec, false);
+ vlc_cond_broadcast(&p_sys->cond);
+ vlc_mutex_unlock(&p_sys->lock);
+ vlc_join(p_sys->out_thread, NULL);
+ vlc_join(p_sys->in_thread, NULL);
+ }
+ else
+ vlc_mutex_unlock(&p_sys->lock);
+
+ OutFifo_Deinit(&p_sys->out_fifo);
+
+ vlc_mutex_destroy(&p_sys->lock);
+ vlc_cond_destroy(&p_sys->cond);
+ vlc_cond_destroy(&p_sys->flush_cond);
+ vlc_cond_destroy(&p_sys->dec_cond);
+
StopMediaCodec(p_dec);
CSDFree(p_dec);
@@ -1024,205 +1151,433 @@ static void HEVCProcessBlock(decoder_t *p_dec, block_t *p_block,
VLC_UNUSED(p_size_changed);
}
-static int DecodeFlush(decoder_t *p_dec)
+static int DecodeFlush(decoder_t *p_dec, bool b_from_input)
{
decoder_sys_t *p_sys = p_dec->p_sys;
+ bool b_was_running = p_sys->b_mc_running;
+
+ p_sys->b_mc_running = false;
+ p_sys->b_flush_in = !b_from_input;
+ p_sys->b_flush_out = true;
+ p_sys->i_preroll_end = 0;
+ p_sys->b_output_ready = false;
+ /* Resend CODEC_CONFIG buffer after a flush */
+ p_sys->i_csd_send = 0;
- if (p_sys->decoded || p_sys->i_csd_send > 0)
- {
- p_sys->pf_on_flush(p_dec);
+ p_sys->pf_on_flush(p_dec);
+
+ OutFifo_Clear(&p_sys->out_fifo);
+
+ if (b_was_running && p_sys->api->flush(p_sys->api) != VLC_SUCCESS)
+ return VLC_EGENERIC;
+
+ vlc_cond_broadcast(&p_sys->cond);
+
+ while (p_sys->b_threads_running && (p_sys->b_flush_in || p_sys->b_flush_out))
+ vlc_cond_wait(&p_sys->flush_cond, &p_sys->lock);
+
+ if (!p_sys->b_threads_running)
+ return VLC_EGENERIC;
- p_sys->i_preroll_end = 0;
- if (p_sys->api->flush(p_sys->api) != VLC_SUCCESS)
- return VLC_EGENERIC;
- /* resend CODEC_CONFIG buffer after a flush */
- p_sys->i_csd_send = 0;
- }
- p_sys->decoded = false;
return VLC_SUCCESS;
}
-static int GetAndProcessOutput(decoder_t *p_dec, picture_t **pp_out_pic,
- block_t **pp_out_block, mtime_t i_timeout)
+static block_t *GetNextInput(decoder_sys_t *p_sys)
{
- decoder_sys_t *p_sys = p_dec->p_sys;
- struct mc_api_out out;
- int i_index, i_ret;
-
- i_index = p_sys->api->dequeue_out(p_sys->api, i_timeout);
- if (i_index >= 0 || i_index == MC_API_INFO_OUTPUT_FORMAT_CHANGED
- || i_index == MC_API_INFO_OUTPUT_BUFFERS_CHANGED)
- i_ret = p_sys->api->get_out(p_sys->api, i_index, &out);
- else if (i_index == MC_API_INFO_TRYAGAIN)
- i_ret = 0;
+ if (p_sys->i_csd_send < p_sys->i_csd_count)
+ return p_sys->pp_csd[p_sys->i_csd_send++];
else
- i_ret = -1;
-
- if (i_ret != 1)
- return i_ret;
-
- return p_sys->pf_process_output(p_dec, &out, pp_out_pic,
- pp_out_block);
+ return p_sys->p_in_block;
}
-/**
- * DecodeCommon called from DecodeVideo or DecodeAudio.
- * It returns -1 in case of error, 0 otherwise. The output buffer is returned
- * in pp_out_pic for Video, and pp_out_block for Audio.
- */
-static int DecodeCommon(decoder_t *p_dec, block_t **pp_block,
- picture_t **pp_out_pic, block_t **pp_out_block)
+static void *InThread(void *data)
{
+ decoder_t *p_dec = data;
decoder_sys_t *p_sys = p_dec->p_sys;
- block_t *p_block = pp_block ? *pp_block : NULL;
- unsigned int i_attempts = 0;
- jlong timeout = 0;
- int i_output_ret = 0;
- int i_input_ret = 0;
- bool b_abort = false;
- bool b_error = false;
- bool b_new_block = p_block ? p_sys->b_new_block : false;
- if (p_sys->error_state)
- goto endclean;
-
- if (b_new_block)
+ for (;;)
{
- int i_ret, i_flags = 0;
+ int i_index;
+ block_t *p_block = NULL;
+ bool b_config;
- p_sys->b_new_block = false;
+ vlc_mutex_lock(&p_sys->lock);
- if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED))
+ /* Wait for a flush or a new input buffer */
+ while (p_sys->b_threads_running && !p_sys->b_flush_in
+ && !(p_block = GetNextInput(p_sys)))
+ vlc_cond_wait(&p_sys->cond, &p_sys->lock);
+
+ b_config = p_block && (p_block->i_flags & BLOCK_FLAG_CSD);
+
+ if (p_sys->b_flush_in)
{
- if (DecodeFlush(p_dec) != VLC_SUCCESS)
- b_error = true;
- goto endclean;
+ if (p_sys->p_in_block)
+ block_Release(p_sys->p_in_block);
+ p_sys->p_in_block = NULL;
+
+ /* Acknowledge flushed state */
+ p_sys->b_flush_in = false;
+ vlc_cond_signal(&p_sys->flush_cond);
+ goto next;
}
- i_ret = p_sys->pf_on_new_block(p_dec, p_block, &i_flags);
- if (i_ret != 1)
+ /* Check if thread is not stopped */
+ if (!p_sys->b_threads_running)
+ goto end;
+
+ assert(p_block);
+
+ if (!b_config && p_block->i_flags & BLOCK_FLAG_NEW)
{
- if (i_ret == -1)
- b_error = true;
- goto endclean;
+ int i_flags = 0;
+ int i_ret = p_sys->pf_on_new_block(p_dec, p_block, &i_flags);
+
+ p_block->i_flags &= ~BLOCK_FLAG_NEW;
+ if (i_ret == 1)
+ {
+ if (i_flags & (NEWBLOCK_FLAG_FLUSH|NEWBLOCK_FLAG_RESTART))
+ {
+ msg_Warn(p_dec, "Flushing from InThread");
+
+ /* Flush before restart to unblock OutThread */
+ if (DecodeFlush(p_dec, true) != VLC_SUCCESS)
+ goto end;
+
+ if (i_flags & NEWBLOCK_FLAG_RESTART)
+ {
+ msg_Warn(p_dec, "Restarting from InThread");
+ StopMediaCodec(p_dec);
+ if (StartMediaCodec(p_dec) != VLC_SUCCESS)
+ goto end;
+ }
+ goto next;
+ }
+ } else if (i_ret == 0)
+ {
+ /* Ignore current block */
+ block_Release(p_block);
+ p_sys->p_in_block = NULL;
+ vlc_cond_signal(&p_sys->dec_cond);
+ goto next;
+ }
+ else
+ {
+ msg_Err(p_dec, "OnNewBlock failed");
+ goto end;
+ }
+ }
+
+ if (!b_config)
+ {
+ /* Signal DecodeCommon that a new block can be queued */
+ p_sys->p_in_block = NULL;
+ vlc_cond_signal(&p_sys->dec_cond);
}
- if (i_flags & NEWBLOCK_FLAG_FLUSH)
+ if (!p_sys->api->b_started)
{
- if (DecodeFlush(p_dec) != VLC_SUCCESS)
- goto endclean;
+ /* Abort if MediaCodec is not yet started */
+ if (!b_config)
+ block_Release(p_block);
+ goto next;
}
- if (i_flags & NEWBLOCK_FLAG_RESTART)
+
+ vlc_mutex_unlock(&p_sys->lock);
+ /* Wait for an input buffer. This function returns when a new input
+ * buffer is available or if input is flushed. */
+ i_index = p_sys->api->dequeue_in(p_sys->api, -1);
+ vlc_mutex_lock(&p_sys->lock);
+
+ /* Ignore dequeue_in errors caused by flush */
+ if (p_sys->b_flush_in)
{
- StopMediaCodec(p_dec);
- if (StartMediaCodec(p_dec) != VLC_SUCCESS)
- goto endclean;
+ if (p_block && !b_config)
+ block_Release(p_block);
+
+ /* If i_index >= 0, DecodeFlush didn't flush this buffer, so flush
+ * again to release it. */
+ if (i_index >= 0 && p_sys->api->flush(p_sys->api) != VLC_SUCCESS)
+ goto end;
+ goto next;
}
- }
- if (!p_sys->api->b_started)
- goto endclean;
- do
- {
- if ((p_sys->i_csd_send < p_sys->i_csd_count || p_block)
- && i_input_ret == 0)
+ if (i_index >= 0)
{
- block_t *p_in_block;
- mtime_t i_ts;
- int i_index;
+ mtime_t i_ts = 0;
+ p_sys->b_mc_running = true;
- if (p_sys->i_csd_send < p_sys->i_csd_count)
+ if (!b_config)
{
- p_in_block = p_sys->pp_csd[p_sys->i_csd_send];
- i_ts = 0;
- }
- else
- {
- p_in_block = p_block;
i_ts = p_block->i_pts;
if (!i_ts && p_block->i_dts)
i_ts = p_block->i_dts;
}
- i_index = p_sys->api->dequeue_in(p_sys->api, timeout);
- if (i_index >= 0)
- i_input_ret = p_sys->api->queue_in(p_sys->api, i_index,
- p_in_block->p_buffer,
- p_in_block->i_buffer, i_ts,
- p_in_block->i_flags & BLOCK_FLAG_CSD) == 0 ? 1 : -1;
- else if (i_index == MC_API_INFO_TRYAGAIN)
- i_input_ret = 0;
- else
- i_input_ret = -1;
-
- if (i_input_ret == 1)
+ 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->i_csd_send < p_sys->i_csd_count)
+ if (!b_config)
{
- p_sys->i_csd_send++;
- i_input_ret = 0;
- }
- else
- {
- p_sys->decoded = true;
if (p_block->i_flags & BLOCK_FLAG_PREROLL )
p_sys->i_preroll_end = i_ts;
+
+ block_Release(p_block);
+ /* One input buffer is queued, unblock OutThread that will
+ * fetch output buffers */
+ p_sys->b_output_ready = true;
+ vlc_cond_broadcast(&p_sys->cond);
}
+ } else
+ {
+ msg_Err(p_dec, "queue_in failed");
+ goto end;
}
- if (!p_sys->decoded)
- continue;
}
+ else
+ {
+ msg_Err(p_dec, "dequeue_in failed");
+ goto end;
+ }
+next:
+ vlc_mutex_unlock(&p_sys->lock);
+ continue;
+end:
+ msg_Warn(p_dec, "InThread stopped");
+ p_sys->b_threads_running = false;
+ if (p_block && !b_config)
+ block_Release(p_block);
+ p_sys->p_in_block = NULL;
+ vlc_cond_signal(&p_sys->dec_cond);
+ vlc_cond_signal(&p_sys->flush_cond);
+ vlc_mutex_unlock(&p_sys->lock);
+ break;
+ }
+
+ return NULL;
+}
+
+static void *OutThread(void *data)
+{
+ decoder_t *p_dec = data;
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ bool b_got_output = false;
+
+ for (;;)
+ {
+ int i_index;
+
+ vlc_mutex_lock(&p_sys->lock);
+
+ /* Wait output ready and free space in output FIFO */
+ while (p_sys->b_threads_running && !p_sys->b_flush_out
+ && (!p_sys->b_output_ready || OutFifo_IsFull(&p_sys->out_fifo)))
+ vlc_cond_wait(&p_sys->cond, &p_sys->lock);
+
+ if (p_sys->b_flush_out)
+ {
+ /* Acknowledge flushed state */
+ p_sys->b_flush_out = false;
+ b_got_output = false;
+ vlc_cond_broadcast(&p_sys->flush_cond);
+ goto next;
+ }
+
+ /* Check if thread is not stopped */
+ if (!p_sys->b_threads_running)
+ goto end;
- if (i_input_ret != -1 && p_sys->decoded && i_output_ret == 0)
+ if (OutFifo_IsFull(&p_sys->out_fifo))
+ goto next;
+
+ vlc_mutex_unlock(&p_sys->lock);
+ /* Wait for an output buffer. This function returns when a new output
+ * is available or if output is flushed. Assume that MediaCodec is in
+ * bad state if waiting more than 3 seconds for an output. */
+ i_index = p_sys->api->dequeue_out(p_sys->api,
+ b_got_output ? -1 : INT64_C(3000000));
+ vlc_mutex_lock(&p_sys->lock);
+
+ /* Ignore dequeue_out errors caused by flush */
+ if (p_sys->b_flush_out)
+ goto next;
+
+ if (i_index >= 0 || i_index == MC_API_INFO_OUTPUT_FORMAT_CHANGED
+ || i_index == MC_API_INFO_OUTPUT_BUFFERS_CHANGED)
{
- i_output_ret = GetAndProcessOutput(p_dec, pp_out_pic, pp_out_block,
- timeout);
+ struct mc_api_out out;
+ int i_ret = p_sys->api->get_out(p_sys->api, i_index, &out);
- if (!p_sys->b_has_format && i_output_ret == 0 && i_input_ret == 0
- && ++i_attempts > 100)
+ if (i_ret == 1)
{
- /* No output and no format, thereforce mediacodec didn't
- * produce any output or events yet. Don't wait indefinitely
- * and abort after 2seconds (100 * 2 * 10ms) without any data.
- * Indeed, MediaCodec can fail without throwing any exception
- * or error returns... */
- msg_Err(p_dec, "No output/input for %lld ms, abort",
- i_attempts * timeout);
- b_error = true;
- break;
+ b_got_output = true;
+ /* Queue the new output buffer for DecodeCommon */
+ OutFifo_Put(&p_sys->out_fifo, &out);
+ vlc_cond_signal(&p_sys->dec_cond);
+ decoder_OnOutputReady(p_dec);
+ } else if (i_ret != 0)
+ {
+ msg_Err(p_dec, "get_out failed");
+ goto end;
}
}
- timeout = 10 * 1000; // 10 ms
- /* loop until either the input or the output are processed (i_input_ret
- * or i_output_ret == 1 ) or caused an error (i_input_ret or
- * i_output_ret == -1 )*/
- } while (p_block && i_input_ret == 0 && i_output_ret == 0 && !b_abort);
+ else
+ {
+ if (i_index == MC_API_INFO_TRYAGAIN)
+ {
+ /* MediaCodec didn't produce any output or events in 3 seconds.
+ * Don't wait indefinitely and abort since it can fail without
+ * throwing any exception or error returns... */
+ msg_Err(p_dec, "No output for 3 seconds, abort");
+ }
+ else
+ msg_Err(p_dec, "dequeue_out failed");
+ goto end;
+ }
+next:
+ vlc_mutex_unlock(&p_sys->lock);
+ continue;
+end:
+ msg_Warn(p_dec, "OutThread stopped");
+ p_sys->b_threads_running = false;
+ vlc_cond_signal(&p_sys->dec_cond);
+ vlc_cond_signal(&p_sys->flush_cond);
+ vlc_mutex_unlock(&p_sys->lock);
+ break;
+ }
+
+ return NULL;
+}
+
+static int GetAndProcessOutput(decoder_t *p_dec, picture_t **pp_out_pic,
+ block_t **pp_out_block)
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ bool b_was_full = OutFifo_IsFull(&p_sys->out_fifo);
+ struct mc_api_out *p_out = OutFifo_Get(&p_sys->out_fifo);
+
+ if (!p_out)
+ return 0;
- if (i_input_ret == -1 || i_output_ret == -1)
+ /* Signal the OutThread that there is free space in the Fifo */
+ if (b_was_full)
+ vlc_cond_broadcast(&p_sys->cond);
+ return p_sys->pf_process_output(p_dec, p_out, pp_out_pic,
+ pp_out_block);
+}
+
+/**
+ * DecodeCommon called from DecodeVideo or DecodeAudio.
+ * It returns -1 in case of error, 0 otherwise. The output buffer is returned
+ * in pp_out_pic for Video, and pp_out_block for Audio.
+ */
+static int DecodeCommon(decoder_t *p_dec, block_t **pp_block,
+ picture_t **pp_out_pic, block_t **pp_out_block)
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ block_t *p_block = pp_block ? *pp_block : NULL;
+ bool b_error = false;
+
+ vlc_mutex_lock(&p_sys->lock);
+
+ if (p_sys->error_state || !p_sys->b_threads_running)
{
- msg_Err(p_dec, "%s failed",
- i_input_ret == -1 ? "PutInput" : "GetOutput");
b_error = true;
+ goto end;
}
-endclean:
+ if (p_block)
+ {
+ int i_out_ret = 0;
+ mtime_t i_deadline;
+
+ if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED))
+ {
+ block_Release(p_block);
+ *pp_block = NULL;
+
+ if (DecodeFlush(p_dec, false) != VLC_SUCCESS)
+ {
+ msg_Err(p_dec, "DecodeFlush failed");
+ b_error = true;
+ goto end;
+ }
+ goto end;
+ }
- /* If pf_decode returns NULL, we'll get a new p_block from the next
- * pf_decode call. Therefore we need to release the current one even if we
- * couldn't process it (it happens in case or error or if MediaCodec is
- * still not opened). We also must release the current p_block if we were
- * able to process it. */
- if (p_block && (i_output_ret != 1 || i_input_ret != 0))
+ i_deadline= p_sys->api->b_direct_rendering ?
+ mdate() + INT64_C(500000) : -1;
+ /* Loop until p_sys->p_in_block or an output is processed */
+ while (p_sys->b_threads_running
+ && !(i_out_ret = GetAndProcessOutput(p_dec, pp_out_pic, pp_out_block))
+ && p_sys->p_in_block)
+ {
+ if (i_deadline != -1)
+ {
+ /* HACK: When direct rendering is enabled, there is a possible
+ * deadlock between the Decoder and the Vout. It happens when
+ * the Vout is paused and when the Decoder is flushing. In that
+ * case, the Vout won't release any output buffers, therefore
+ * MediaCodec won't dequeue any input buffers. To work around
+ * this issue, release all output buffers if DecodeCommon is
+ * waiting more than 500 msec for a new input buffer. */
+ int i_ret = vlc_cond_timedwait(&p_sys->dec_cond, &p_sys->lock,
+ i_deadline);
+ if (i_ret == ETIMEDOUT)
+ {
+ msg_Warn(p_dec, "Decoder stuck: invalidate all buffers");
+ InvalidateAllPictures(p_dec);
+ i_deadline = -1;
+ }
+ }
+ else
+ vlc_cond_wait(&p_sys->dec_cond, &p_sys->lock);
+ }
+
+ if (i_out_ret == -1)
+ {
+ msg_Err(p_dec, "GetAndProcessOutput failed");
+ b_error = true;
+ goto end;
+ }
+ if (!p_sys->p_in_block)
+ {
+ /* Queue a new input block */
+ *pp_block = NULL;
+ p_block->i_flags |= BLOCK_FLAG_NEW;
+ p_sys->p_in_block = p_block;
+ vlc_cond_broadcast(&p_sys->cond);
+ }
+ }
+ else
{
- block_Release(p_block);
- *pp_block = NULL;
- p_sys->b_new_block = true;
+ /* Try to process an output without waiting. Indeed, there is no
+ * p_block, so pf_decode can return immediately without any
+ * input/output processed. */
+ if (GetAndProcessOutput(p_dec, pp_out_pic, pp_out_block) == -1)
+ {
+ msg_Err(p_dec, "GetAndProcessOutput failed");
+ b_error = true;
+ }
}
- if (b_error && !p_sys->error_state) {
- /* Signal the error to the Java. */
- jni_EventHardwareAccelerationError();
- p_sys->error_state = true;
+
+end:
+ if (b_error)
+ {
+ if (*pp_block)
+ {
+ block_Release(*pp_block);
+ *pp_block = NULL;
+ }
+ if (!p_sys->error_state) {
+ /* Signal the error to the Java. */
+ jni_EventHardwareAccelerationError();
+ p_sys->error_state = true;
+ p_sys->b_threads_running = false;
+ }
}
+ vlc_mutex_unlock(&p_sys->lock);
return b_error ? -1 : 0;
}
--
2.1.4
More information about the vlc-devel
mailing list