From: Thomas Volkert <tho...@netzeal.de> --- libavcodec/mediacodecdec.c | 140 ++++++++++++++++++++++++++++++- libavcodec/mediacodecdec.h | 12 +++ libavcodec/mediacodecdec_h264.c | 175 +++------------------------------------ libavcodec/mediacodecdec_mpeg4.c | 154 +++------------------------------- 4 files changed, 170 insertions(+), 311 deletions(-)
diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c index df60104..d780ca4 100644 --- a/libavcodec/mediacodecdec.c +++ b/libavcodec/mediacodecdec.c @@ -23,6 +23,7 @@ #include <string.h> #include <sys/types.h> +#include "libavutil/avassert.h" #include "libavutil/atomic.h" #include "libavutil/common.h" #include "libavutil/mem.h" @@ -152,6 +153,8 @@ static void ff_mediacodec_dec_unref(MediaCodecDecContext *s) return; if (!avpriv_atomic_int_add_and_fetch(&s->refcount, -1)) { + av_freep(&s->codec_name); + if (s->codec) { ff_AMediaCodec_delete(s->codec); s->codec = NULL; @@ -167,8 +170,17 @@ static void ff_mediacodec_dec_unref(MediaCodecDecContext *s) s->surface = NULL; } - av_freep(&s->codec_name); - av_freep(&s); + if (s->bsf) { + av_bsf_free(&s->bsf); + s->bsf = NULL; + } + + if (s->fifo) { + av_fifo_free(s->fifo); + s->fifo = NULL; + } + + av_packet_unref(&s->output_pkt); } } @@ -752,6 +764,130 @@ int ff_mediacodec_dec_is_flushing(AVCodecContext *avctx, MediaCodecDecContext *s return s->flushing; } +void ff_mediacodec_decoder_flush(AVCodecContext *avctx) +{ + MediaCodecDecContext *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->output_pkt); + + ff_mediacodec_dec_flush(avctx, s); +} + +int ff_mediacodec_decoder_decode(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + MediaCodecDecContext *s = avctx->priv_data; + AVFrame *frame = data; + int ret; + + /* buffer the input packet */ + if (avpkt->size) { + AVPacket input_pkt = { 0 }; + + if (av_fifo_space(s->fifo) < sizeof(input_pkt)) { + ret = av_fifo_realloc2(s->fifo, + av_fifo_size(s->fifo) + sizeof(input_pkt)); + if (ret < 0) + return ret; + } + + ret = av_packet_ref(&input_pkt, avpkt); + if (ret < 0) + return ret; + av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL); + } + + /* + * MediaCodec.flush() discards both input and output buffers, thus we + * need to delay the call to this function until the user has released or + * renderered the frames he retains. + * + * After we have buffered an input packet, check if the codec is in the + * flushing state. If it is, we need to call ff_mediacodec_dec_flush. + * + * ff_mediacodec_dec_flush returns 0 if the flush cannot be performed on + * the codec (because the user retains frames). The codec stays in the + * flushing state. + * + * ff_mediacodec_dec_flush returns 1 if the flush can actually be + * performed on the codec. The codec leaves the flushing state and can + * process again packets. + * + * ff_mediacodec_dec_flush returns a negative value if an error has + * occurred. + * + */ + if (ff_mediacodec_dec_is_flushing(avctx, s)) { + if (!ff_mediacodec_dec_flush(avctx, s)) { + return avpkt->size; + } + } + + /* process buffered data */ + while (!*got_frame) { + /* prepare the input data -- execute BSF if needed */ + if (s->output_pkt.size <= 0) { + av_packet_unref(&s->output_pkt); + + /* no more data */ + if (av_fifo_size(s->fifo) < sizeof(AVPacket)) { + return avpkt->size ? avpkt->size : + ff_mediacodec_dec_decode(avctx, s, frame, got_frame, avpkt); + } + + if (s->bsf) { + AVPacket unfiltered_pkt = { 0 }; + + av_fifo_generic_read(s->fifo, &unfiltered_pkt, sizeof(unfiltered_pkt), NULL); + + ret = av_bsf_send_packet(s->bsf, &unfiltered_pkt); + if (ret < 0) { + return ret; + } + + ret = av_bsf_receive_packet(s->bsf, &s->output_pkt); + if (ret == AVERROR(EAGAIN)) { + goto done; + } + + /* h264_mp4toannexb is used here and does not requires flushing */ + av_assert0(ret != AVERROR_EOF); + + if (ret < 0) { + return ret; + } + } else { + av_fifo_generic_read(s->fifo, &s->output_pkt, sizeof(s->output_pkt), NULL); + } + } + + ret = ff_mediacodec_dec_decode(avctx, s, frame, got_frame, &s->output_pkt); + if (ret < 0) + return ret; + + s->output_pkt.size -= ret; + s->output_pkt.data += ret; + } +done: + return avpkt->size; +} + +av_cold int ff_mediacodec_decoder_close(AVCodecContext *avctx) +{ + MediaCodecDecContext *s = avctx->priv_data; + + ff_mediacodec_dec_close(avctx, s); + + return 0; +} + AVHWAccel ff_h264_mediacodec_hwaccel = { .name = "mediacodec", .type = AVMEDIA_TYPE_VIDEO, diff --git a/libavcodec/mediacodecdec.h b/libavcodec/mediacodecdec.h index 8613352..f2fd3f8 100644 --- a/libavcodec/mediacodecdec.h +++ b/libavcodec/mediacodecdec.h @@ -26,6 +26,7 @@ #include <stdint.h> #include <sys/types.h> +#include "libavutil/fifo.h" #include "libavutil/frame.h" #include "libavutil/pixfmt.h" @@ -64,6 +65,10 @@ typedef struct MediaCodecDecContext { int first_buffer; double first_buffer_at; + AVBSFContext *bsf; + AVFifoBuffer *fifo; + AVPacket output_pkt; + } MediaCodecDecContext; int ff_mediacodec_dec_init(AVCodecContext *avctx, @@ -86,6 +91,13 @@ int ff_mediacodec_dec_close(AVCodecContext *avctx, int ff_mediacodec_dec_is_flushing(AVCodecContext *avctx, MediaCodecDecContext *s); +int ff_mediacodec_decoder_decode(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt); + +void ff_mediacodec_decoder_flush(AVCodecContext *avctx); + +int ff_mediacodec_decoder_close(AVCodecContext *avctx); + typedef struct MediaCodecBuffer { MediaCodecDecContext *ctx; diff --git a/libavcodec/mediacodecdec_h264.c b/libavcodec/mediacodecdec_h264.c index caeb64b..2124186 100644 --- a/libavcodec/mediacodecdec_h264.c +++ b/libavcodec/mediacodecdec_h264.c @@ -39,33 +39,6 @@ #define CODEC_MIME "video/avc" -typedef struct MediaCodecH264DecContext { - - MediaCodecDecContext *ctx; - - AVBSFContext *bsf; - - AVFifoBuffer *fifo; - - AVPacket filtered_pkt; - -} MediaCodecH264DecContext; - -static av_cold int mediacodec_decode_close(AVCodecContext *avctx) -{ - MediaCodecH264DecContext *s = avctx->priv_data; - - ff_mediacodec_dec_close(avctx, s->ctx); - s->ctx = NULL; - - av_fifo_free(s->fifo); - - av_bsf_free(&s->bsf); - av_packet_unref(&s->filtered_pkt); - - return 0; -} - static int h264_ps_to_nalu(const uint8_t *src, int src_size, uint8_t **out, int *out_size) { int i; @@ -118,7 +91,7 @@ done: return ret; } -static av_cold int mediacodec_decode_init(AVCodecContext *avctx) +static av_cold int mediacodec_decoder_init_h264(AVCodecContext *avctx) { int i; int ret; @@ -130,7 +103,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) int nal_length_size = 0; FFAMediaFormat *format = NULL; - MediaCodecH264DecContext *s = avctx->priv_data; + MediaCodecDecContext *s = avctx->priv_data; memset(&ps, 0, sizeof(ps)); @@ -185,15 +158,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) goto done; } - s->ctx = av_mallocz(sizeof(*s->ctx)); - if (!s->ctx) { - av_log(avctx, AV_LOG_ERROR, "Failed to allocate MediaCodecDecContext\n"); - ret = AVERROR(ENOMEM); - goto done; - } - - if ((ret = ff_mediacodec_dec_init(avctx, s->ctx, CODEC_MIME, format)) < 0) { - s->ctx = NULL; + if ((ret = ff_mediacodec_dec_init(avctx, s, CODEC_MIME, format)) < 0) { goto done; } @@ -220,7 +185,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) goto done; } - av_init_packet(&s->filtered_pkt); + av_init_packet(&s->output_pkt); done: if (format) { @@ -228,7 +193,7 @@ done: } if (ret < 0) { - mediacodec_decode_close(avctx); + ff_mediacodec_decoder_close(avctx); } ff_h264_ps_uninit(&ps); @@ -236,136 +201,16 @@ done: return ret; } - -static int mediacodec_process_data(AVCodecContext *avctx, AVFrame *frame, - int *got_frame, AVPacket *pkt) -{ - MediaCodecH264DecContext *s = avctx->priv_data; - - return ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, pkt); -} - -static int mediacodec_decode_frame(AVCodecContext *avctx, void *data, - int *got_frame, AVPacket *avpkt) -{ - MediaCodecH264DecContext *s = avctx->priv_data; - AVFrame *frame = data; - int ret; - - /* buffer the input packet */ - if (avpkt->size) { - AVPacket input_pkt = { 0 }; - - if (av_fifo_space(s->fifo) < sizeof(input_pkt)) { - ret = av_fifo_realloc2(s->fifo, - av_fifo_size(s->fifo) + sizeof(input_pkt)); - if (ret < 0) - return ret; - } - - ret = av_packet_ref(&input_pkt, avpkt); - if (ret < 0) - return ret; - av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL); - } - - /* - * MediaCodec.flush() discards both input and output buffers, thus we - * need to delay the call to this function until the user has released or - * renderered the frames he retains. - * - * After we have buffered an input packet, check if the codec is in the - * flushing state. If it is, we need to call ff_mediacodec_dec_flush. - * - * ff_mediacodec_dec_flush returns 0 if the flush cannot be performed on - * the codec (because the user retains frames). The codec stays in the - * flushing state. - * - * ff_mediacodec_dec_flush returns 1 if the flush can actually be - * performed on the codec. The codec leaves the flushing state and can - * process again packets. - * - * ff_mediacodec_dec_flush returns a negative value if an error has - * occurred. - * - */ - if (ff_mediacodec_dec_is_flushing(avctx, s->ctx)) { - if (!ff_mediacodec_dec_flush(avctx, s->ctx)) { - return avpkt->size; - } - } - - /* process buffered data */ - while (!*got_frame) { - /* prepare the input data -- convert to Annex B if needed */ - if (s->filtered_pkt.size <= 0) { - AVPacket input_pkt = { 0 }; - - av_packet_unref(&s->filtered_pkt); - - /* no more data */ - if (av_fifo_size(s->fifo) < sizeof(AVPacket)) { - return avpkt->size ? avpkt->size : - ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, avpkt); - } - - av_fifo_generic_read(s->fifo, &input_pkt, sizeof(input_pkt), NULL); - - ret = av_bsf_send_packet(s->bsf, &input_pkt); - if (ret < 0) { - return ret; - } - - ret = av_bsf_receive_packet(s->bsf, &s->filtered_pkt); - if (ret == AVERROR(EAGAIN)) { - goto done; - } - - /* h264_mp4toannexb is used here and does not requires flushing */ - av_assert0(ret != AVERROR_EOF); - - if (ret < 0) { - return ret; - } - } - - ret = mediacodec_process_data(avctx, frame, got_frame, &s->filtered_pkt); - if (ret < 0) - return ret; - - s->filtered_pkt.size -= ret; - s->filtered_pkt.data += ret; - } -done: - return avpkt->size; -} - -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->filtered_pkt); - - ff_mediacodec_dec_flush(avctx, s->ctx); -} - AVCodec ff_h264_mediacodec_decoder = { .name = "h264_mediacodec", .long_name = NULL_IF_CONFIG_SMALL("H.264 Android MediaCodec decoder"), .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_H264, - .priv_data_size = sizeof(MediaCodecH264DecContext), - .init = mediacodec_decode_init, - .decode = mediacodec_decode_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, + .priv_data_size = sizeof(MediaCodecDecContext), + .init = mediacodec_decoder_init_h264, + .decode = ff_mediacodec_decoder_decode, + .flush = ff_mediacodec_decoder_flush, + .close = ff_mediacodec_decoder_close, .capabilities = CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, }; diff --git a/libavcodec/mediacodecdec_mpeg4.c b/libavcodec/mediacodecdec_mpeg4.c index 7c4559b..8dfa67e 100644 --- a/libavcodec/mediacodecdec_mpeg4.c +++ b/libavcodec/mediacodecdec_mpeg4.c @@ -31,39 +31,12 @@ #define CODEC_MIME "video/mp4v-es" -typedef struct MediaCodecMPEG4DecContext { - - MediaCodecDecContext *ctx; - - AVBSFContext *bsf; - - AVFifoBuffer *fifo; - - AVPacket filtered_pkt; - -} MediaCodecMPEG4DecContext; - -static av_cold int mediacodec_decode_close(AVCodecContext *avctx) -{ - MediaCodecMPEG4DecContext *s = avctx->priv_data; - - ff_mediacodec_dec_close(avctx, s->ctx); - s->ctx = NULL; - - av_fifo_free(s->fifo); - - av_bsf_free(&s->bsf); - av_packet_unref(&s->filtered_pkt); - - return 0; -} - -static av_cold int mediacodec_decode_init(AVCodecContext *avctx) +static av_cold int mediacodec_decoder_init_mpeg4(AVCodecContext *avctx) { int ret; FFAMediaFormat *format = NULL; - MediaCodecMPEG4DecContext *s = avctx->priv_data; + MediaCodecDecContext *s = avctx->priv_data; format = ff_AMediaFormat_new(); if (!format) { @@ -76,15 +49,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) ff_AMediaFormat_setInt32(format, "width", avctx->width); ff_AMediaFormat_setInt32(format, "height", avctx->height); - s->ctx = av_mallocz(sizeof(*s->ctx)); - if (!s->ctx) { - av_log(avctx, AV_LOG_ERROR, "Failed to allocate MediaCodecDecContext\n"); - ret = AVERROR(ENOMEM); - goto done; - } - - if ((ret = ff_mediacodec_dec_init(avctx, s->ctx, CODEC_MIME, format)) < 0) { - s->ctx = NULL; + if ((ret = ff_mediacodec_dec_init(avctx, s, CODEC_MIME, format)) < 0) { goto done; } @@ -111,7 +76,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) goto done; } - av_init_packet(&s->filtered_pkt); + av_init_packet(&s->output_pkt); done: if (format) { @@ -119,121 +84,22 @@ done: } if (ret < 0) { - mediacodec_decode_close(avctx); + ff_mediacodec_decoder_close(avctx); } return ret; } -static int mediacodec_process_data(AVCodecContext *avctx, AVFrame *frame, - int *got_frame, AVPacket *pkt) -{ - MediaCodecMPEG4DecContext *s = avctx->priv_data; - - return ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, pkt); -} - -static int mediacodec_decode_frame(AVCodecContext *avctx, void *data, - int *got_frame, AVPacket *avpkt) -{ - MediaCodecMPEG4DecContext *s = avctx->priv_data; - AVFrame *frame = data; - int ret; - - /* buffer the input packet */ - if (avpkt->size) { - AVPacket input_pkt = { 0 }; - - if (av_fifo_space(s->fifo) < sizeof(input_pkt)) { - ret = av_fifo_realloc2(s->fifo, - av_fifo_size(s->fifo) + sizeof(input_pkt)); - if (ret < 0) - return ret; - } - - ret = av_packet_ref(&input_pkt, avpkt); - if (ret < 0) - return ret; - av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL); - } - - if (ff_mediacodec_dec_is_flushing(avctx, s->ctx)) { - if (!ff_mediacodec_dec_flush(avctx, s->ctx)) { - return avpkt->size; - } - } - - /* process buffered data */ - while (!*got_frame) { - /* prepare the input data -- unpack DivX-style packed b frame if needed */ - if (s->filtered_pkt.size <= 0) { - AVPacket input_pkt = { 0 }; - - av_packet_unref(&s->filtered_pkt); - - /* no more data */ - if (av_fifo_size(s->fifo) < sizeof(AVPacket)) { - return avpkt->size ? avpkt->size : - ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, avpkt); - } - - av_fifo_generic_read(s->fifo, &input_pkt, sizeof(input_pkt), NULL); - - ret = av_bsf_send_packet(s->bsf, &input_pkt); - if (ret < 0) { - return ret; - } - - ret = av_bsf_receive_packet(s->bsf, &s->filtered_pkt); - if (ret == AVERROR(EAGAIN)) { - goto done; - } - - /* no explicit flushing needed */ - av_assert0(ret != AVERROR_EOF); - - if (ret < 0) { - return ret; - } - } - - ret = mediacodec_process_data(avctx, frame, got_frame, &s->filtered_pkt); - if (ret < 0) - return ret; - - s->filtered_pkt.size -= ret; - s->filtered_pkt.data += ret; - } -done: - return avpkt->size; -} - -static void mediacodec_decode_flush(AVCodecContext *avctx) -{ - MediaCodecMPEG4DecContext *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->filtered_pkt); - - ff_mediacodec_dec_flush(avctx, s->ctx); -} - AVCodec ff_mpeg4_mediacodec_decoder = { .name = "mpeg4_mediacodec", .long_name = NULL_IF_CONFIG_SMALL("MPEG-4 Android MediaCodec decoder"), .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_MPEG4, - .priv_data_size = sizeof(MediaCodecMPEG4DecContext), - .init = mediacodec_decode_init, - .decode = mediacodec_decode_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, + .priv_data_size = sizeof(MediaCodecDecContext), + .init = mediacodec_decoder_init_mpeg4, + .decode = ff_mediacodec_decoder_decode, + .flush = ff_mediacodec_decoder_flush, + .close = ff_mediacodec_decoder_close, .capabilities = CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, }; -- 2.7.4 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel