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().
Signed-off-by: Aman Gupta <aman@tmm1.net>
Signed-off-by: Matthieu Bouron <matthieu.bouron@gmail.com>
(cherry picked from commit f6681feda6)
tags/n4.0.1
| @@ -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,54 @@ 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, block 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; | |||
| } | |||
| /* poll for space again */ | |||
| 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); | |||
| } else if (ret < 0) { | |||
| return ret; | |||
| } | |||
| } | |||
| else if (ret < 0) | |||
| return ret; | |||
| /* crank decoder with new packet */ | |||
| return mediacodec_send_receive(avctx, s, frame, true); | |||
| return AVERROR(EAGAIN); | |||
| } | |||
| static void mediacodec_decode_flush(AVCodecContext *avctx) | |||
| @@ -450,6 +450,7 @@ static int mediacodec_dec_flush_codec(AVCodecContext *avctx, MediaCodecDecContex | |||
| s->eos = 0; | |||
| atomic_fetch_add(&s->serial, 1); | |||
| atomic_init(&s->hw_buffer_count, 0); | |||
| s->current_input_buffer = -1; | |||
| status = ff_AMediaCodec_flush(codec); | |||
| if (status < 0) { | |||
| @@ -477,6 +478,7 @@ int ff_mediacodec_dec_init(AVCodecContext *avctx, MediaCodecDecContext *s, | |||
| atomic_init(&s->refcount, 1); | |||
| atomic_init(&s->hw_buffer_count, 0); | |||
| atomic_init(&s->serial, 1); | |||
| s->current_input_buffer = -1; | |||
| pix_fmt = ff_get_format(avctx, pix_fmts); | |||
| if (pix_fmt == AV_PIX_FMT_MEDIACODEC) { | |||
| @@ -561,16 +563,16 @@ fail: | |||
| } | |||
| int ff_mediacodec_dec_send(AVCodecContext *avctx, MediaCodecDecContext *s, | |||
| AVPacket *pkt) | |||
| AVPacket *pkt, bool wait) | |||
| { | |||
| int offset = 0; | |||
| int need_draining = 0; | |||
| uint8_t *data; | |||
| ssize_t index; | |||
| ssize_t index = s->current_input_buffer; | |||
| size_t size; | |||
| FFAMediaCodec *codec = s->codec; | |||
| int status; | |||
| int64_t input_dequeue_timeout_us = INPUT_DEQUEUE_TIMEOUT_US; | |||
| int64_t input_dequeue_timeout_us = wait ? INPUT_DEQUEUE_TIMEOUT_US : 0; | |||
| int64_t pts; | |||
| if (s->flushing) { | |||
| @@ -588,17 +590,19 @@ int ff_mediacodec_dec_send(AVCodecContext *avctx, MediaCodecDecContext *s, | |||
| } | |||
| while (offset < pkt->size || (need_draining && !s->draining)) { | |||
| index = ff_AMediaCodec_dequeueInputBuffer(codec, input_dequeue_timeout_us); | |||
| if (ff_AMediaCodec_infoTryAgainLater(codec, index)) { | |||
| av_log(avctx, AV_LOG_TRACE, "No input buffer available, try again later\n"); | |||
| break; | |||
| } | |||
| if (index < 0) { | |||
| av_log(avctx, AV_LOG_ERROR, "Failed to dequeue input buffer (status=%zd)\n", index); | |||
| return AVERROR_EXTERNAL; | |||
| index = ff_AMediaCodec_dequeueInputBuffer(codec, input_dequeue_timeout_us); | |||
| if (ff_AMediaCodec_infoTryAgainLater(codec, index)) { | |||
| av_log(avctx, AV_LOG_TRACE, "No input buffer available, try again later\n"); | |||
| break; | |||
| } | |||
| if (index < 0) { | |||
| av_log(avctx, AV_LOG_ERROR, "Failed to dequeue input buffer (status=%zd)\n", index); | |||
| return AVERROR_EXTERNAL; | |||
| } | |||
| } | |||
| s->current_input_buffer = -1; | |||
| data = ff_AMediaCodec_getInputBuffer(codec, index, &size); | |||
| if (!data) { | |||
| @@ -65,6 +65,7 @@ typedef struct MediaCodecDecContext { | |||
| int display_height; | |||
| uint64_t output_buffer_count; | |||
| ssize_t current_input_buffer; | |||
| bool delay_flush; | |||
| atomic_int serial; | |||
| @@ -78,7 +79,8 @@ int ff_mediacodec_dec_init(AVCodecContext *avctx, | |||
| int ff_mediacodec_dec_send(AVCodecContext *avctx, | |||
| MediaCodecDecContext *s, | |||
| AVPacket *pkt); | |||
| AVPacket *pkt, | |||
| bool wait); | |||
| int ff_mediacodec_dec_receive(AVCodecContext *avctx, | |||
| MediaCodecDecContext *s, | |||