X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fmediacodecdec.c;h=3a4240aa952748fdb84c77da34d3407cb0e44116;hb=bcff983dc340e76518935111146c0e1daf4cb37b;hp=cb1151a19527bf98fd3ae3e120667dc666b71bab;hpb=d0f098a5e0bae84d98eb48ea5ba173422e004fe3;p=ffmpeg diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c index cb1151a1952..3a4240aa952 100644 --- a/libavcodec/mediacodecdec.c +++ b/libavcodec/mediacodecdec.c @@ -25,10 +25,10 @@ #include "libavutil/avassert.h" #include "libavutil/common.h" -#include "libavutil/fifo.h" #include "libavutil/opt.h" #include "libavutil/intreadwrite.h" #include "libavutil/pixfmt.h" +#include "libavutil/internal.h" #include "avcodec.h" #include "decode.h" @@ -41,12 +41,15 @@ typedef struct MediaCodecH264DecContext { - MediaCodecDecContext *ctx; + AVClass *avclass; - AVFifoBuffer *fifo; + MediaCodecDecContext *ctx; AVPacket buffered_pkt; + int delay_flush; + int amlogic_mpeg2_api23_workaround; + } MediaCodecH264DecContext; static av_cold int mediacodec_decode_close(AVCodecContext *avctx) @@ -56,8 +59,6 @@ static av_cold int mediacodec_decode_close(AVCodecContext *avctx) ff_mediacodec_dec_close(avctx, s->ctx); s->ctx = NULL; - av_fifo_free(s->fifo); - av_packet_unref(&s->buffered_pkt); return 0; @@ -268,34 +269,11 @@ done: } #endif -#if CONFIG_MPEG2_MEDIACODEC_DECODER -static int mpeg2_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) -{ - int ret = 0; - - if (avctx->extradata) { - ff_AMediaFormat_setBuffer(format, "csd-0", avctx->extradata, avctx->extradata_size); - } - - return ret; -} -#endif - -#if CONFIG_MPEG4_MEDIACODEC_DECODER -static int mpeg4_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) -{ - int ret = 0; - - if (avctx->extradata) { - ff_AMediaFormat_setBuffer(format, "csd-0", avctx->extradata, avctx->extradata_size); - } - - return ret; -} -#endif - -#if CONFIG_VP8_MEDIACODEC_DECODER || CONFIG_VP9_MEDIACODEC_DECODER -static int vpx_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) +#if CONFIG_MPEG2_MEDIACODEC_DECODER || \ + CONFIG_MPEG4_MEDIACODEC_DECODER || \ + CONFIG_VP8_MEDIACODEC_DECODER || \ + CONFIG_VP9_MEDIACODEC_DECODER +static int common_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) { int ret = 0; @@ -310,6 +288,7 @@ static int vpx_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) static av_cold int mediacodec_decode_init(AVCodecContext *avctx) { int ret; + int sdk_int; const char *codec_mime = NULL; @@ -346,7 +325,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) case AV_CODEC_ID_MPEG2VIDEO: codec_mime = "video/mpeg2"; - ret = mpeg2_set_extradata(avctx, format); + ret = common_set_extradata(avctx, format); if (ret < 0) goto done; break; @@ -355,7 +334,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) case AV_CODEC_ID_MPEG4: codec_mime = "video/mp4v-es", - ret = mpeg4_set_extradata(avctx, format); + ret = common_set_extradata(avctx, format); if (ret < 0) goto done; break; @@ -364,7 +343,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) case AV_CODEC_ID_VP8: codec_mime = "video/x-vnd.on2.vp8"; - ret = vpx_set_extradata(avctx, format); + ret = common_set_extradata(avctx, format); if (ret < 0) goto done; break; @@ -373,7 +352,7 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) case AV_CODEC_ID_VP9: codec_mime = "video/x-vnd.on2.vp9"; - ret = vpx_set_extradata(avctx, format); + ret = common_set_extradata(avctx, format); if (ret < 0) goto done; break; @@ -393,17 +372,23 @@ static av_cold int mediacodec_decode_init(AVCodecContext *avctx) goto done; } + s->ctx->delay_flush = s->delay_flush; + if ((ret = ff_mediacodec_dec_init(avctx, s->ctx, codec_mime, format)) < 0) { s->ctx = NULL; goto done; } - av_log(avctx, AV_LOG_INFO, "MediaCodec started successfully, ret = %d\n", ret); + av_log(avctx, AV_LOG_INFO, + "MediaCodec started successfully: codec = %s, ret = %d\n", + s->ctx->codec_name, ret); - s->fifo = av_fifo_alloc(sizeof(AVPacket)); - if (!s->fifo) { - ret = AVERROR(ENOMEM); - goto done; + sdk_int = ff_Build_SDK_INT(avctx); + if (sdk_int <= 23 && + strcmp(s->ctx->codec_name, "OMX.amlogic.mpeg2.decoder.awesome") == 0) { + av_log(avctx, AV_LOG_INFO, "Enabling workaround for %s on API=%d\n", + s->ctx->codec_name, sdk_int); + s->amlogic_mpeg2_api23_workaround = 1; } done: @@ -422,104 +407,74 @@ 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 - * 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)) { + ssize_t index; + + /* In delay_flush mode, wait until the user has released or rendered + all retained frames. */ + if (s->delay_flush && ff_mediacodec_dec_is_flushing(avctx, s->ctx)) { if (!ff_mediacodec_dec_flush(avctx, s->ctx)) { return AVERROR(EAGAIN); } } - 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) + /* poll for new frame */ + ret = ff_mediacodec_dec_receive(avctx, s->ctx, 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; + /* 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; } - 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); + /* 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; } - av_fifo_generic_read(s->fifo, &s->buffered_pkt, sizeof(s->buffered_pkt), NULL); + if (s->amlogic_mpeg2_api23_workaround && s->buffered_pkt.size <= 0) { + /* fallthrough to fetch next packet regardless of input buffer space */ + } else { + /* poll for space again */ + continue; + } } - ret = ff_mediacodec_dec_decode(avctx, s->ctx, frame, &got_frame, &s->buffered_pkt); - if (ret < 0) + /* 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; - - s->buffered_pkt.size -= ret; - s->buffered_pkt.data += ret; + } } - return 0; + return AVERROR(EAGAIN); } 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); @@ -538,112 +493,62 @@ static const AVCodecHWConfigInternal *mediacodec_hw_configs[] = { NULL }; -#if CONFIG_H264_MEDIACODEC_DECODER -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, - .receive_frame = mediacodec_receive_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, - .bsfs = "h264_mp4toannexb", - .hw_configs = mediacodec_hw_configs, - .wrapper_name = "mediacodec", +#define OFFSET(x) offsetof(MediaCodecH264DecContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption ff_mediacodec_vdec_options[] = { + { "delay_flush", "Delay flush until hw output buffers are returned to the decoder", + OFFSET(delay_flush), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, VD }, + { NULL } }; + +#define DECLARE_MEDIACODEC_VCLASS(short_name) \ +static const AVClass ff_##short_name##_mediacodec_dec_class = { \ + .class_name = #short_name "_mediacodec", \ + .item_name = av_default_item_name, \ + .option = ff_mediacodec_vdec_options, \ + .version = LIBAVUTIL_VERSION_INT, \ +}; + +#define DECLARE_MEDIACODEC_VDEC(short_name, full_name, codec_id, bsf) \ +DECLARE_MEDIACODEC_VCLASS(short_name) \ +AVCodec ff_##short_name##_mediacodec_decoder = { \ + .name = #short_name "_mediacodec", \ + .long_name = NULL_IF_CONFIG_SMALL(full_name " Android MediaCodec decoder"), \ + .type = AVMEDIA_TYPE_VIDEO, \ + .id = codec_id, \ + .priv_class = &ff_##short_name##_mediacodec_dec_class, \ + .priv_data_size = sizeof(MediaCodecH264DecContext), \ + .init = mediacodec_decode_init, \ + .receive_frame = mediacodec_receive_frame, \ + .flush = mediacodec_decode_flush, \ + .close = mediacodec_decode_close, \ + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ + .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, \ + .bsfs = bsf, \ + .hw_configs = mediacodec_hw_configs, \ + .wrapper_name = "mediacodec", \ +}; \ + +#if CONFIG_H264_MEDIACODEC_DECODER +DECLARE_MEDIACODEC_VDEC(h264, "H.264", AV_CODEC_ID_H264, "h264_mp4toannexb") #endif #if CONFIG_HEVC_MEDIACODEC_DECODER -AVCodec ff_hevc_mediacodec_decoder = { - .name = "hevc_mediacodec", - .long_name = NULL_IF_CONFIG_SMALL("H.265 Android MediaCodec decoder"), - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .priv_data_size = sizeof(MediaCodecH264DecContext), - .init = mediacodec_decode_init, - .receive_frame = mediacodec_receive_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, - .bsfs = "hevc_mp4toannexb", - .hw_configs = mediacodec_hw_configs, - .wrapper_name = "mediacodec", -}; +DECLARE_MEDIACODEC_VDEC(hevc, "H.265", AV_CODEC_ID_HEVC, "hevc_mp4toannexb") #endif #if CONFIG_MPEG2_MEDIACODEC_DECODER -AVCodec ff_mpeg2_mediacodec_decoder = { - .name = "mpeg2_mediacodec", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 Android MediaCodec decoder"), - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .priv_data_size = sizeof(MediaCodecH264DecContext), - .init = mediacodec_decode_init, - .receive_frame = mediacodec_receive_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, - .hw_configs = mediacodec_hw_configs, - .wrapper_name = "mediacodec", -}; +DECLARE_MEDIACODEC_VDEC(mpeg2, "MPEG-2", AV_CODEC_ID_MPEG2VIDEO, NULL) #endif #if CONFIG_MPEG4_MEDIACODEC_DECODER -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(MediaCodecH264DecContext), - .init = mediacodec_decode_init, - .receive_frame = mediacodec_receive_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, - .hw_configs = mediacodec_hw_configs, - .wrapper_name = "mediacodec", -}; +DECLARE_MEDIACODEC_VDEC(mpeg4, "MPEG-4", AV_CODEC_ID_MPEG4, NULL) #endif #if CONFIG_VP8_MEDIACODEC_DECODER -AVCodec ff_vp8_mediacodec_decoder = { - .name = "vp8_mediacodec", - .long_name = NULL_IF_CONFIG_SMALL("VP8 Android MediaCodec decoder"), - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP8, - .priv_data_size = sizeof(MediaCodecH264DecContext), - .init = mediacodec_decode_init, - .receive_frame = mediacodec_receive_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, - .hw_configs = mediacodec_hw_configs, - .wrapper_name = "mediacodec", -}; +DECLARE_MEDIACODEC_VDEC(vp8, "VP8", AV_CODEC_ID_VP8, NULL) #endif #if CONFIG_VP9_MEDIACODEC_DECODER -AVCodec ff_vp9_mediacodec_decoder = { - .name = "vp9_mediacodec", - .long_name = NULL_IF_CONFIG_SMALL("VP9 Android MediaCodec decoder"), - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .priv_data_size = sizeof(MediaCodecH264DecContext), - .init = mediacodec_decode_init, - .receive_frame = mediacodec_receive_frame, - .flush = mediacodec_decode_flush, - .close = mediacodec_decode_close, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, - .hw_configs = mediacodec_hw_configs, - .wrapper_name = "mediacodec", -}; +DECLARE_MEDIACODEC_VDEC(vp9, "VP9", AV_CODEC_ID_VP9, NULL) #endif