On Tue, Apr 24, 2018 at 01:59:26PM -0700, Aman Gupta wrote: > From: Aman Gupta <a...@tmm1.net> > > This fixes issues reported on some devices where both > avcodec_send_packet and avcodec_receive_frame can return EAGAIN > at the same time, instead of one or the other blocking. > > The new logic follows a recommendation by @rcombs to use > dequeueInputBuffer with a timeout of 0 as a way to detect > whether the codec wants more data. The dequeued buffer index is > kept in MediaCodecDecContext until it can be used next. > > A similar technique is also used by the Google's official media > player Exoplayer: see MediaCodecRenderer.feedInputBuffer(). > --- > libavcodec/mediacodecdec.c | 78 > ++++++++++++++++++++------------------- > libavcodec/mediacodecdec_common.c | 28 ++++++++------ > libavcodec/mediacodecdec_common.h | 4 +- > 3 files changed, 59 insertions(+), 51 deletions(-) > > diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c > index e5d3e6a0af..1a681d771e 100644 > --- a/libavcodec/mediacodecdec.c > +++ b/libavcodec/mediacodecdec.c > @@ -391,33 +391,11 @@ done: > return ret; > } > > -static int mediacodec_send_receive(AVCodecContext *avctx, > - MediaCodecH264DecContext *s, > - AVFrame *frame, bool wait) > -{ > - int ret; > - > - /* send any pending data from buffered packet */ > - while (s->buffered_pkt.size) { > - ret = ff_mediacodec_dec_send(avctx, s->ctx, &s->buffered_pkt); > - if (ret == AVERROR(EAGAIN)) > - break; > - else if (ret < 0) > - return ret; > - s->buffered_pkt.size -= ret; > - s->buffered_pkt.data += ret; > - if (s->buffered_pkt.size <= 0) > - av_packet_unref(&s->buffered_pkt); > - } > - > - /* check for new frame */ > - return ff_mediacodec_dec_receive(avctx, s->ctx, frame, wait); > -} > - > static int mediacodec_receive_frame(AVCodecContext *avctx, AVFrame *frame) > { > MediaCodecH264DecContext *s = avctx->priv_data; > int ret; > + ssize_t index; > > /* in delay_flush mode, wait until the user has released or rendered > all retained frames. */ > @@ -427,28 +405,52 @@ static int mediacodec_receive_frame(AVCodecContext > *avctx, AVFrame *frame) > } > } > > - /* flush buffered packet and check for new frame */ > - ret = mediacodec_send_receive(avctx, s, frame, false); > + /* poll for new frame */ > + ret = ff_mediacodec_dec_receive(avctx, s->ctx, frame, false); > if (ret != AVERROR(EAGAIN)) > return ret; > > - /* skip fetching new packet if we still have one buffered */ > - if (s->buffered_pkt.size > 0) > - return mediacodec_send_receive(avctx, s, frame, true); > + /* feed decoder */ > + while (1) { > + if (s->ctx->current_input_buffer < 0) { > + /* poll for input space */ > + index = ff_AMediaCodec_dequeueInputBuffer(s->ctx->codec, 0); > + if (index < 0) { > + /* no space, wait a while for an output frame to appear */ > + return ff_mediacodec_dec_receive(avctx, s->ctx, frame, true); > + } > + s->ctx->current_input_buffer = index; > + } > > - /* fetch new packet or eof */ > - ret = ff_decode_get_packet(avctx, &s->buffered_pkt); > - if (ret == AVERROR_EOF) { > - AVPacket null_pkt = { 0 }; > - ret = ff_mediacodec_dec_send(avctx, s->ctx, &null_pkt); > - if (ret < 0) > + /* try to flush any buffered packet data */ > + if (s->buffered_pkt.size > 0) { > + ret = ff_mediacodec_dec_send(avctx, s->ctx, &s->buffered_pkt, > false); > + if (ret > 0) { > + s->buffered_pkt.size -= ret; > + s->buffered_pkt.data += ret; > + if (s->buffered_pkt.size <= 0) > + av_packet_unref(&s->buffered_pkt); > + } else if (ret < 0 && ret == AVERROR(EAGAIN)) { > + return ret; > + } > + continue; > + } > + > + /* fetch new packet or eof */ > + ret = ff_decode_get_packet(avctx, &s->buffered_pkt); > + if (ret == AVERROR_EOF) { > + AVPacket null_pkt = { 0 }; > + ret = ff_mediacodec_dec_send(avctx, s->ctx, &null_pkt, true); > + if (ret < 0) > + return ret; > + } > + else if (ret == AVERROR(EAGAIN) && s->ctx->current_input_buffer < 0) > + return ff_mediacodec_dec_receive(avctx, s->ctx, frame, true);
If the input is starved on the FFmpeg side, we shouldn't block on trying to receive a new frame from MediaCodec as it causes a performance regression while the decoder is still not buffered enough to output its first frame (after init or after a flush) as the function will end up waiting 8ms for each new packet (for each call to avcodec_send_packet()). Returning directly EAGAIN from here is fine as we will end up calling ff_mediacodec_dec_receive() from the next call to avcodec_send_packet() or avcodec_receive_frame(). With this modification the patch works fine on my devices without any performance regression. [...] -- Matthieu B. _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel