Message ID | 20180503022500.76182-2-ffmpeg@tmm1.net |
---|---|
State | Accepted |
Commit | f6681feda641c026d84f6d207f661bf9b87d9d70 |
Headers | show |
On Wed, May 02, 2018 at 07:24:58PM -0700, Aman Gupta wrote: > From: Aman Gupta <aman@tmm1.net> > > 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> > --- > libavcodec/mediacodecdec.c | 80 ++++++++++++++++++++------------------- > libavcodec/mediacodecdec_common.c | 28 ++++++++------ > libavcodec/mediacodecdec_common.h | 4 +- > 3 files changed, 61 insertions(+), 51 deletions(-) > > diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c > index 86ceee5a83..2ac22dd1f6 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,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; > + } Same comment as for the previous version of the patch: https://ffmpeg.org/pipermail/ffmpeg-devel/2018-April/228978.html [...]
On Thu, May 3, 2018 at 12:33 AM, Matthieu Bouron <matthieu.bouron@gmail.com> wrote: > On Wed, May 02, 2018 at 07:24:58PM -0700, Aman Gupta wrote: > > From: Aman Gupta <aman@tmm1.net> > > > > 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> > > --- > > libavcodec/mediacodecdec.c | 80 ++++++++++++++++++++---------- > --------- > > libavcodec/mediacodecdec_common.c | 28 ++++++++------ > > libavcodec/mediacodecdec_common.h | 4 +- > > 3 files changed, 61 insertions(+), 51 deletions(-) > > > > diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c > > index 86ceee5a83..2ac22dd1f6 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,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; > > + } > > Same comment as for the previous version of the patch: > https://ffmpeg.org/pipermail/ffmpeg-devel/2018-April/228978.html I think that comment has been addressed in this version of the patch. The wait here only happens if current_input_buffer is not available, meaning that the decoder is not accepting new packets. If there is space for new input packets, the wait will not happen. I think the bug in the version you tested (presumably from my GH fork) was the missing "poll for space again" continue above. Aman > > > [...] > > -- > Matthieu B. >
On Thu, May 03, 2018 at 10:24:49AM -0700, Aman Gupta wrote: > On Thu, May 3, 2018 at 12:33 AM, Matthieu Bouron <matthieu.bouron@gmail.com> > wrote: > > > On Wed, May 02, 2018 at 07:24:58PM -0700, Aman Gupta wrote: > > > From: Aman Gupta <aman@tmm1.net> > > > > > > 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> > > > --- > > > libavcodec/mediacodecdec.c | 80 ++++++++++++++++++++---------- > > --------- > > > libavcodec/mediacodecdec_common.c | 28 ++++++++------ > > > libavcodec/mediacodecdec_common.h | 4 +- > > > 3 files changed, 61 insertions(+), 51 deletions(-) > > > > > > diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c > > > index 86ceee5a83..2ac22dd1f6 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,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; > > > + } > > > > Same comment as for the previous version of the patch: > > https://ffmpeg.org/pipermail/ffmpeg-devel/2018-April/228978.html > > > I think that comment has been addressed in this version of the patch. The > wait here only happens if current_input_buffer is not available, > meaning that the decoder is not accepting new packets. If there is space > for new input packets, the wait will not happen. > > I think the bug in the version you tested (presumably from my GH fork) was > the missing "poll for space again" continue above. Sorry, I re-did some test on my devices and the issue has been addressed with this patch. So, LGTM. [...]
On Fri, May 4, 2018 at 8:13 AM, Matthieu Bouron <matthieu.bouron@gmail.com> wrote: > On Thu, May 03, 2018 at 10:24:49AM -0700, Aman Gupta wrote: > > On Thu, May 3, 2018 at 12:33 AM, Matthieu Bouron < > matthieu.bouron@gmail.com> > > wrote: > > > > > On Wed, May 02, 2018 at 07:24:58PM -0700, Aman Gupta wrote: > > > > From: Aman Gupta <aman@tmm1.net> > > > > > > > > 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> > > > > --- > > > > libavcodec/mediacodecdec.c | 80 > ++++++++++++++++++++---------- > > > --------- > > > > libavcodec/mediacodecdec_common.c | 28 ++++++++------ > > > > libavcodec/mediacodecdec_common.h | 4 +- > > > > 3 files changed, 61 insertions(+), 51 deletions(-) > > > > > > > > diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c > > > > index 86ceee5a83..2ac22dd1f6 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,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; > > > > + } > > > > > > Same comment as for the previous version of the patch: > > > https://ffmpeg.org/pipermail/ffmpeg-devel/2018-April/228978.html > > > > > > I think that comment has been addressed in this version of the patch. The > > wait here only happens if current_input_buffer is not available, > > meaning that the decoder is not accepting new packets. If there is space > > for new input packets, the wait will not happen. > > > > I think the bug in the version you tested (presumably from my GH fork) > was > > the missing "poll for space again" continue above. > > Sorry, I re-did some test on my devices and the issue has been addressed > with this patch. So, LGTM. > Thanks for testing. Patchset pushed. > > [...] > > -- > Matthieu B. >
diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c index 86ceee5a83..2ac22dd1f6 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,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) diff --git a/libavcodec/mediacodecdec_common.c b/libavcodec/mediacodecdec_common.c index c0f0a6b983..887865a281 100644 --- a/libavcodec/mediacodecdec_common.c +++ b/libavcodec/mediacodecdec_common.c @@ -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) { diff --git a/libavcodec/mediacodecdec_common.h b/libavcodec/mediacodecdec_common.h index 023d4c5fa7..0b21129fee 100644 --- a/libavcodec/mediacodecdec_common.h +++ b/libavcodec/mediacodecdec_common.h @@ -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,