#include "libavutil/avassert.h"
#include "libavutil/common.h"
-#include "libavutil/fifo.h"
#include "libavutil/opt.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/pixfmt.h"
MediaCodecDecContext *ctx;
- AVFifoBuffer *fifo;
-
AVPacket buffered_pkt;
} MediaCodecH264DecContext;
ff_mediacodec_dec_close(avctx, s->ctx);
s->ctx = NULL;
- av_fifo_free(s->fifo);
-
av_packet_unref(&s->buffered_pkt);
return 0;
av_log(avctx, AV_LOG_INFO, "MediaCodec started successfully, ret = %d\n", ret);
- s->fifo = av_fifo_alloc(sizeof(AVPacket));
- if (!s->fifo) {
- ret = AVERROR(ENOMEM);
- goto done;
- }
-
done:
if (format) {
ff_AMediaFormat_delete(format);
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;
- int got_frame = 0;
- int is_eof = 0;
- AVPacket pkt = { 0 };
/*
* MediaCodec.flush() discards both input and output buffers, thus we
}
}
- ret = ff_decode_get_packet(avctx, &pkt);
- if (ret == AVERROR_EOF)
- is_eof = 1;
- else if (ret == AVERROR(EAGAIN))
- ; /* no input packet, but fallthrough to check for pending frames */
- else if (ret < 0)
+ /* flush buffered packet and check for new frame */
+ ret = mediacodec_send_receive(avctx, s, frame, false);
+ if (ret != AVERROR(EAGAIN))
return ret;
- /* buffer the input packet */
- if (pkt.size) {
- if (av_fifo_space(s->fifo) < sizeof(pkt)) {
- ret = av_fifo_realloc2(s->fifo,
- av_fifo_size(s->fifo) + sizeof(pkt));
- if (ret < 0) {
- av_packet_unref(&pkt);
- return ret;
- }
- }
- av_fifo_generic_write(s->fifo, &pkt, sizeof(pkt), NULL);
- }
-
- /* process buffered data */
- while (!got_frame) {
- /* prepare the input data */
- if (s->buffered_pkt.size <= 0) {
- av_packet_unref(&s->buffered_pkt);
-
- /* no more data */
- if (av_fifo_size(s->fifo) < sizeof(AVPacket)) {
- AVPacket null_pkt = { 0 };
- if (is_eof) {
- ret = ff_mediacodec_dec_decode(avctx, s->ctx, frame,
- &got_frame, &null_pkt);
- if (ret < 0)
- return ret;
- else if (got_frame)
- return 0;
- else
- return AVERROR_EOF;
- }
- return AVERROR(EAGAIN);
- }
-
- av_fifo_generic_read(s->fifo, &s->buffered_pkt, sizeof(s->buffered_pkt), NULL);
- }
+ /* skip fetching new packet if we still have one buffered */
+ if (s->buffered_pkt.size > 0)
+ return AVERROR(EAGAIN);
- ret = ff_mediacodec_dec_decode(avctx, s->ctx, frame, &got_frame, &s->buffered_pkt);
+ /* 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)
return ret;
-
- s->buffered_pkt.size -= ret;
- s->buffered_pkt.data += ret;
}
+ else if (ret < 0)
+ return ret;
- return 0;
+ /* crank decoder with new packet */
+ return mediacodec_send_receive(avctx, s, frame, true);
}
static void mediacodec_decode_flush(AVCodecContext *avctx)
{
MediaCodecH264DecContext *s = avctx->priv_data;
- while (av_fifo_size(s->fifo)) {
- AVPacket pkt;
- av_fifo_generic_read(s->fifo, &pkt, sizeof(pkt), NULL);
- av_packet_unref(&pkt);
- }
- av_fifo_reset(s->fifo);
-
av_packet_unref(&s->buffered_pkt);
ff_mediacodec_dec_flush(avctx, s->ctx);
return ret;
}
-int ff_mediacodec_dec_decode(AVCodecContext *avctx, MediaCodecDecContext *s,
- AVFrame *frame, int *got_frame,
- AVPacket *pkt)
+int ff_mediacodec_dec_send(AVCodecContext *avctx, MediaCodecDecContext *s,
+ AVPacket *pkt)
{
- int ret;
int offset = 0;
int need_draining = 0;
uint8_t *data;
ssize_t index;
size_t size;
FFAMediaCodec *codec = s->codec;
- FFAMediaCodecBufferInfo info = { 0 };
-
int status;
-
int64_t input_dequeue_timeout_us = INPUT_DEQUEUE_TIMEOUT_US;
- int64_t output_dequeue_timeout_us = OUTPUT_DEQUEUE_TIMEOUT_US;
if (s->flushing) {
av_log(avctx, AV_LOG_ERROR, "Decoder is flushing and cannot accept new buffer "
}
if (s->draining && s->eos) {
- return 0;
+ return AVERROR_EOF;
}
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, "Failed to dequeue input buffer, try again later..\n");
break;
}
return AVERROR_EXTERNAL;
}
+ av_log(avctx, AV_LOG_TRACE, "Queued input buffer %zd"
+ " size=%zd ts=%" PRIi64 "\n", index, size, pts);
+
s->draining = 1;
break;
} else {
int64_t pts = pkt->pts;
size = FFMIN(pkt->size - offset, size);
-
memcpy(data, pkt->data + offset, size);
offset += size;
}
}
- if (need_draining || s->draining) {
+ if (offset == 0)
+ return AVERROR(EAGAIN);
+ return offset;
+}
+
+int ff_mediacodec_dec_receive(AVCodecContext *avctx, MediaCodecDecContext *s,
+ AVFrame *frame, bool wait)
+{
+ int ret;
+ uint8_t *data;
+ ssize_t index;
+ size_t size;
+ FFAMediaCodec *codec = s->codec;
+ FFAMediaCodecBufferInfo info = { 0 };
+ int status;
+ int64_t output_dequeue_timeout_us = OUTPUT_DEQUEUE_TIMEOUT_US;
+
+ if (s->draining && s->eos) {
+ return AVERROR_EOF;
+ }
+
+ if (s->draining) {
/* If the codec is flushing or need to be flushed, block for a fair
* amount of time to ensure we got a frame */
output_dequeue_timeout_us = OUTPUT_DEQUEUE_BLOCK_TIMEOUT_US;
- } else if (s->output_buffer_count == 0) {
+ } else if (s->output_buffer_count == 0 || !wait) {
/* If the codec hasn't produced any frames, do not block so we
* can push data to it as fast as possible, and get the first
* frame */
index = ff_AMediaCodec_dequeueOutputBuffer(codec, &info, output_dequeue_timeout_us);
if (index >= 0) {
- int ret;
-
- av_log(avctx, AV_LOG_DEBUG, "Got output buffer %zd"
+ av_log(avctx, AV_LOG_TRACE, "Got output buffer %zd"
" offset=%" PRIi32 " size=%" PRIi32 " ts=%" PRIi64
" flags=%" PRIu32 "\n", index, info.offset, info.size,
info.presentationTimeUs, info.flags);
}
}
- *got_frame = 1;
s->output_buffer_count++;
+ return 0;
} else {
status = ff_AMediaCodec_releaseOutputBuffer(codec, index, 0);
if (status < 0) {
return AVERROR_EXTERNAL;
}
- return offset;
+ return AVERROR(EAGAIN);
}
int ff_mediacodec_dec_flush(AVCodecContext *avctx, MediaCodecDecContext *s)