diff --git a/configure b/configure index aa78342de6..8101b4fce6 100755 --- a/configure +++ b/configure @@ -3315,6 +3315,7 @@ ac3_mf_encoder_deps="mediafoundation" av1_cuvid_decoder_deps="cuvid CUVIDAV1PICPARAMS" av1_mediacodec_decoder_deps="mediacodec" av1_mediacodec_encoder_deps="mediacodec" +av1_mediacodec_encoder_select="extract_extradata_bsf" av1_nvenc_encoder_deps="nvenc NV_ENC_PIC_PARAMS_AV1" av1_nvenc_encoder_select="atsc_a53" h263_v4l2m2m_decoder_deps="v4l2_m2m h263_v4l2_m2m" @@ -3325,7 +3326,7 @@ h264_cuvid_decoder_select="h264_mp4toannexb_bsf" h264_mediacodec_decoder_deps="mediacodec" h264_mediacodec_decoder_select="h264_mp4toannexb_bsf h264_parser" h264_mediacodec_encoder_deps="mediacodec" -h264_mediacodec_encoder_select="h264_metadata" +h264_mediacodec_encoder_select="extract_extradata_bsf h264_metadata" h264_mf_encoder_deps="mediafoundation" h264_mmal_decoder_deps="mmal" h264_nvenc_encoder_deps="nvenc" @@ -3345,7 +3346,7 @@ hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" hevc_mediacodec_decoder_deps="mediacodec" hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" hevc_mediacodec_encoder_deps="mediacodec" -hevc_mediacodec_encoder_select="hevc_metadata" +hevc_mediacodec_encoder_select="extract_extradata_bsf hevc_metadata" hevc_mf_encoder_deps="mediafoundation" hevc_nvenc_encoder_deps="nvenc" hevc_nvenc_encoder_select="atsc_a53" @@ -3377,6 +3378,7 @@ mpeg2_v4l2m2m_decoder_deps="v4l2_m2m mpeg2_v4l2_m2m" mpeg4_cuvid_decoder_deps="cuvid" mpeg4_mediacodec_decoder_deps="mediacodec" mpeg4_mediacodec_encoder_deps="mediacodec" +mpeg4_mediacodec_encoder_select="extract_extradata_bsf" mpeg4_mmal_decoder_deps="mmal" mpeg4_omx_encoder_deps="omx" mpeg4_v4l2m2m_decoder_deps="v4l2_m2m mpeg4_v4l2_m2m" diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c index 8caaad729a..64816ccf0a 100644 --- a/libavcodec/mediacodecenc.c +++ b/libavcodec/mediacodecenc.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "libavutil/avassert.h" +#include "libavutil/avstring.h" #include "libavutil/hwcontext_mediacodec.h" #include "libavutil/imgutils.h" #include "libavutil/mem.h" @@ -74,6 +75,7 @@ typedef struct MediaCodecEncContext { int bitrate_mode; int level; int pts_as_dts; + int extract_extradata; } MediaCodecEncContext; enum { @@ -112,6 +114,23 @@ static void mediacodec_output_format(AVCodecContext *avctx) ff_AMediaFormat_delete(out_format); } +static int extract_extradata_support(AVCodecContext *avctx) +{ + const AVBitStreamFilter *bsf = av_bsf_get_by_name("extract_extradata"); + + if (!bsf) { + av_log(avctx, AV_LOG_WARNING, "extract_extradata bsf not found\n"); + return 0; + } + + for (int i = 0; bsf->codec_ids[i] != AV_CODEC_ID_NONE; i++) { + if (bsf->codec_ids[i] == avctx->codec_id) + return 1; + } + + return 0; +} + static int mediacodec_init_bsf(AVCodecContext *avctx) { MediaCodecEncContext *s = avctx->priv_data; @@ -120,20 +139,32 @@ static int mediacodec_init_bsf(AVCodecContext *avctx) int crop_right = s->width - avctx->width; int crop_bottom = s->height - avctx->height; - if (!crop_right && !crop_bottom) + /* Nothing can be done for this format now */ + if (avctx->pix_fmt == AV_PIX_FMT_MEDIACODEC) return 0; - if (avctx->codec_id == AV_CODEC_ID_H264) - ret = snprintf(str, sizeof(str), "h264_metadata=crop_right=%d:crop_bottom=%d", - crop_right, crop_bottom); - else if (avctx->codec_id == AV_CODEC_ID_HEVC) - ret = snprintf(str, sizeof(str), "hevc_metadata=crop_right=%d:crop_bottom=%d", - crop_right, crop_bottom); - else + s->extract_extradata = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) && + extract_extradata_support(avctx); + if (!crop_right && !crop_bottom && !s->extract_extradata) return 0; - if (ret >= sizeof(str)) - return AVERROR_BUFFER_TOO_SMALL; + ret = 0; + if (crop_right || crop_bottom) { + if (avctx->codec_id == AV_CODEC_ID_H264) + ret = snprintf(str, sizeof(str), "h264_metadata=crop_right=%d:crop_bottom=%d", + crop_right, crop_bottom); + else if (avctx->codec_id == AV_CODEC_ID_HEVC) + ret = snprintf(str, sizeof(str), "hevc_metadata=crop_right=%d:crop_bottom=%d", + crop_right, crop_bottom); + if (ret >= sizeof(str)) + return AVERROR_BUFFER_TOO_SMALL; + } + + if (s->extract_extradata) { + ret = av_strlcatf(str, sizeof(str), "%sextract_extradata", ret ? "," : ""); + if (ret >= sizeof(str)) + return AVERROR_BUFFER_TOO_SMALL; + } ret = av_bsf_list_parse_str(str, &s->bsf); if (ret < 0) @@ -148,6 +179,8 @@ static int mediacodec_init_bsf(AVCodecContext *avctx) return ret; } +static int mediacodec_generate_extradata(AVCodecContext *avctx); + static av_cold int mediacodec_init(AVCodecContext *avctx) { const char *codec_mime = NULL; @@ -337,14 +370,14 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) goto bailout; mediacodec_output_format(avctx); - if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) - av_log(avctx, AV_LOG_WARNING, - "Mediacodec encoder doesn't support AV_CODEC_FLAG_GLOBAL_HEADER. " - "Use extract_extradata bsf when necessary.\n"); s->frame = av_frame_alloc(); - if (!s->frame) + if (!s->frame) { ret = AVERROR(ENOMEM); + goto bailout; + } + + ret = mediacodec_generate_extradata(avctx); bailout: if (format) @@ -549,6 +582,109 @@ static int mediacodec_encode(AVCodecContext *avctx, AVPacket *pkt) return 0; } +static int mediacodec_send_dummy_frame(AVCodecContext *avctx) +{ + MediaCodecEncContext *s = avctx->priv_data; + int ret; + + s->frame->width = avctx->width; + s->frame->height = avctx->height; + s->frame->format = avctx->pix_fmt; + s->frame->pts = 0; + + ret = av_frame_get_buffer(s->frame, 0); + if (ret < 0) + return ret; + + do { + ret = mediacodec_send(avctx, s->frame); + } while (ret == AVERROR(EAGAIN)); + av_frame_unref(s->frame); + + if (ret < 0) + return ret; + + ret = mediacodec_send(avctx, NULL); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Flush failed: %s\n", av_err2str(ret)); + return ret; + } + + return 0; +} + +static int mediacodec_receive_dummy_pkt(AVCodecContext *avctx, AVPacket *pkt) +{ + MediaCodecEncContext *s = avctx->priv_data; + int ret; + + do { + ret = mediacodec_receive(avctx, pkt); + } while (ret == AVERROR(EAGAIN)); + + if (ret < 0) + return ret; + + do { + ret = av_bsf_send_packet(s->bsf, pkt); + if (ret < 0) + return ret; + ret = av_bsf_receive_packet(s->bsf, pkt); + } while (ret == AVERROR(EAGAIN)); + + return ret; +} + +static int mediacodec_generate_extradata(AVCodecContext *avctx) +{ + MediaCodecEncContext *s = avctx->priv_data; + AVPacket *pkt = NULL; + int ret; + size_t side_size; + uint8_t *side; + + if (!(avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) + return 0; + + if (!s->extract_extradata) { + av_log(avctx, AV_LOG_WARNING, + "Mediacodec encoder doesn't support AV_CODEC_FLAG_GLOBAL_HEADER. " + "Use extract_extradata bsf when necessary.\n"); + return 0; + } + + pkt = av_packet_alloc(); + if (!pkt) + return AVERROR(ENOMEM); + + ret = mediacodec_send_dummy_frame(avctx); + if (ret < 0) + goto bailout; + ret = mediacodec_receive_dummy_pkt(avctx, pkt); + if (ret < 0) + goto bailout; + + side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); + if (side && side_size > 0) { + avctx->extradata = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) { + ret = AVERROR(ENOMEM); + goto bailout; + } + + memcpy(avctx->extradata, side, side_size); + avctx->extradata_size = side_size; + } + +bailout: + if (s->eof_sent) { + s->eof_sent = 0; + ff_AMediaCodec_flush(s->codec); + } + av_packet_free(&pkt); + return ret; +} + static av_cold int mediacodec_close(AVCodecContext *avctx) { MediaCodecEncContext *s = avctx->priv_data;