mirror of
				https://github.com/nyanmisaka/ffmpeg-rockchip.git
				synced 2025-10-31 12:36:41 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			757 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			757 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Android MediaCodec encoders
 | |
|  *
 | |
|  * Copyright (c) 2022 Zhao Zhili <zhilizhao@tencent.com>
 | |
|  *
 | |
|  * This file is part of FFmpeg.
 | |
|  *
 | |
|  * FFmpeg is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * FFmpeg is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with FFmpeg; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include "config_components.h"
 | |
| 
 | |
| #include "libavutil/avassert.h"
 | |
| #include "libavutil/hwcontext_mediacodec.h"
 | |
| #include "libavutil/imgutils.h"
 | |
| #include "libavutil/opt.h"
 | |
| 
 | |
| #include "avcodec.h"
 | |
| #include "bsf.h"
 | |
| #include "codec_internal.h"
 | |
| #include "encode.h"
 | |
| #include "hwconfig.h"
 | |
| #include "jni.h"
 | |
| #include "mediacodec.h"
 | |
| #include "mediacodec_wrapper.h"
 | |
| #include "mediacodecdec_common.h"
 | |
| 
 | |
| #define INPUT_DEQUEUE_TIMEOUT_US 8000
 | |
| #define OUTPUT_DEQUEUE_TIMEOUT_US 8000
 | |
| 
 | |
| enum BitrateMode {
 | |
|     /* Constant quality mode */
 | |
|     BITRATE_MODE_CQ = 0,
 | |
|     /* Variable bitrate mode */
 | |
|     BITRATE_MODE_VBR = 1,
 | |
|     /* Constant bitrate mode */
 | |
|     BITRATE_MODE_CBR = 2,
 | |
|     /* Constant bitrate mode with frame drops */
 | |
|     BITRATE_MODE_CBR_FD = 3,
 | |
| };
 | |
| 
 | |
| typedef struct MediaCodecEncContext {
 | |
|     AVClass *avclass;
 | |
|     FFAMediaCodec *codec;
 | |
|     int use_ndk_codec;
 | |
|     const char *name;
 | |
|     FFANativeWindow *window;
 | |
| 
 | |
|     int fps;
 | |
|     int width;
 | |
|     int height;
 | |
| 
 | |
|     uint8_t *extradata;
 | |
|     int extradata_size;
 | |
|     int eof_sent;
 | |
| 
 | |
|     AVFrame *frame;
 | |
|     AVBSFContext *bsf;
 | |
| 
 | |
|     int bitrate_mode;
 | |
|     int level;
 | |
|     int pts_as_dts;
 | |
| } MediaCodecEncContext;
 | |
| 
 | |
| enum {
 | |
|     COLOR_FormatYUV420Planar                              = 0x13,
 | |
|     COLOR_FormatYUV420SemiPlanar                          = 0x15,
 | |
|     COLOR_FormatSurface                                   = 0x7F000789,
 | |
| };
 | |
| 
 | |
| static const struct {
 | |
|     int color_format;
 | |
|     enum AVPixelFormat pix_fmt;
 | |
| } color_formats[] = {
 | |
|     { COLOR_FormatYUV420Planar,         AV_PIX_FMT_YUV420P },
 | |
|     { COLOR_FormatYUV420SemiPlanar,     AV_PIX_FMT_NV12    },
 | |
|     { COLOR_FormatSurface,              AV_PIX_FMT_MEDIACODEC },
 | |
| };
 | |
| 
 | |
| static const enum AVPixelFormat avc_pix_fmts[] = {
 | |
|     AV_PIX_FMT_MEDIACODEC,
 | |
|     AV_PIX_FMT_YUV420P,
 | |
|     AV_PIX_FMT_NV12,
 | |
|     AV_PIX_FMT_NONE
 | |
| };
 | |
| 
 | |
| static void mediacodec_output_format(AVCodecContext *avctx)
 | |
| {
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     char *name = ff_AMediaCodec_getName(s->codec);
 | |
|     FFAMediaFormat *out_format = ff_AMediaCodec_getOutputFormat(s->codec);
 | |
|     char *str = ff_AMediaFormat_toString(out_format);
 | |
| 
 | |
|     av_log(avctx, AV_LOG_DEBUG, "MediaCodec encoder %s output format %s\n",
 | |
|            name ? name : "unknown", str);
 | |
|     av_free(name);
 | |
|     av_free(str);
 | |
|     ff_AMediaFormat_delete(out_format);
 | |
| }
 | |
| 
 | |
| static int mediacodec_init_bsf(AVCodecContext *avctx)
 | |
| {
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     char str[128];
 | |
|     int ret;
 | |
|     int crop_right = s->width - avctx->width;
 | |
|     int crop_bottom = s->height - avctx->height;
 | |
| 
 | |
|     if (!crop_right && !crop_bottom)
 | |
|         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
 | |
|         return 0;
 | |
| 
 | |
|     if (ret >= sizeof(str))
 | |
|         return AVERROR_BUFFER_TOO_SMALL;
 | |
| 
 | |
|     ret = av_bsf_list_parse_str(str, &s->bsf);
 | |
|     if (ret < 0)
 | |
|         return ret;
 | |
| 
 | |
|     ret = avcodec_parameters_from_context(s->bsf->par_in, avctx);
 | |
|     if (ret < 0)
 | |
|         return ret;
 | |
|     s->bsf->time_base_in = avctx->time_base;
 | |
|     ret = av_bsf_init(s->bsf);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static av_cold int mediacodec_init(AVCodecContext *avctx)
 | |
| {
 | |
|     const char *codec_mime = NULL;
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     FFAMediaFormat *format = NULL;
 | |
|     int ret;
 | |
|     int gop;
 | |
| 
 | |
|     if (s->use_ndk_codec < 0)
 | |
|         s->use_ndk_codec = !av_jni_get_java_vm(avctx);
 | |
| 
 | |
|     switch (avctx->codec_id) {
 | |
|     case AV_CODEC_ID_H264:
 | |
|         codec_mime = "video/avc";
 | |
|         break;
 | |
|     case AV_CODEC_ID_HEVC:
 | |
|         codec_mime = "video/hevc";
 | |
|         break;
 | |
|     default:
 | |
|         av_assert0(0);
 | |
|     }
 | |
| 
 | |
|     if (s->name)
 | |
|         s->codec = ff_AMediaCodec_createCodecByName(s->name, s->use_ndk_codec);
 | |
|     else
 | |
|         s->codec = ff_AMediaCodec_createEncoderByType(codec_mime, s->use_ndk_codec);
 | |
|     if (!s->codec) {
 | |
|         av_log(avctx, AV_LOG_ERROR, "Failed to create encoder for type %s\n",
 | |
|                codec_mime);
 | |
|         return AVERROR_EXTERNAL;
 | |
|     }
 | |
| 
 | |
|     format = ff_AMediaFormat_new(s->use_ndk_codec);
 | |
|     if (!format) {
 | |
|         av_log(avctx, AV_LOG_ERROR, "Failed to create media format\n");
 | |
|         return AVERROR_EXTERNAL;
 | |
|     }
 | |
| 
 | |
|     ff_AMediaFormat_setString(format, "mime", codec_mime);
 | |
|     // Workaround the alignment requirement of mediacodec. We can't do it
 | |
|     // silently for AV_PIX_FMT_MEDIACODEC.
 | |
|     if (avctx->pix_fmt != AV_PIX_FMT_MEDIACODEC) {
 | |
|         s->width = FFALIGN(avctx->width, 16);
 | |
|         s->height = FFALIGN(avctx->height, 16);
 | |
|     } else {
 | |
|         s->width = avctx->width;
 | |
|         s->height = avctx->height;
 | |
|         if (s->width % 16 || s->height % 16)
 | |
|             av_log(avctx, AV_LOG_WARNING,
 | |
|                     "Video size %dx%d isn't align to 16, it may have device compatibility issue\n",
 | |
|                     s->width, s->height);
 | |
|     }
 | |
|     ff_AMediaFormat_setInt32(format, "width", s->width);
 | |
|     ff_AMediaFormat_setInt32(format, "height", s->height);
 | |
| 
 | |
|     if (avctx->pix_fmt == AV_PIX_FMT_MEDIACODEC) {
 | |
|         AVMediaCodecContext *user_ctx = avctx->hwaccel_context;
 | |
|         if (avctx->hw_device_ctx) {
 | |
|             AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)(avctx->hw_device_ctx->data);
 | |
|             AVMediaCodecDeviceContext *dev_ctx;
 | |
| 
 | |
|             if (device_ctx->type != AV_HWDEVICE_TYPE_MEDIACODEC || !device_ctx->hwctx) {
 | |
|                 ret = AVERROR(EINVAL);
 | |
|                 goto bailout;
 | |
|             }
 | |
|             dev_ctx = device_ctx->hwctx;
 | |
|             s->window = ff_mediacodec_surface_ref(dev_ctx->surface, dev_ctx->native_window, avctx);
 | |
|         }
 | |
| 
 | |
|         if (!s->window && user_ctx && user_ctx->surface)
 | |
|             s->window = ff_mediacodec_surface_ref(user_ctx->surface, NULL, avctx);
 | |
| 
 | |
|         if (!s->window) {
 | |
|             ret = AVERROR(EINVAL);
 | |
|             av_log(avctx, AV_LOG_ERROR, "Missing hw_device_ctx or hwaccel_context for AV_PIX_FMT_MEDIACODEC\n");
 | |
|             goto bailout;
 | |
|         }
 | |
|         /* Although there is a method ANativeWindow_toSurface() introduced in
 | |
|          * API level 26, it's easier and safe to always require a Surface for
 | |
|          * Java MediaCodec.
 | |
|          */
 | |
|         if (!s->use_ndk_codec && !s->window->surface) {
 | |
|             ret = AVERROR(EINVAL);
 | |
|             av_log(avctx, AV_LOG_ERROR, "Missing jobject Surface for AV_PIX_FMT_MEDIACODEC. "
 | |
|                     "Please note that Java MediaCodec doesn't work with ANativeWindow.\n");
 | |
|             goto bailout;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < FF_ARRAY_ELEMS(color_formats); i++) {
 | |
|         if (avctx->pix_fmt == color_formats[i].pix_fmt) {
 | |
|             ff_AMediaFormat_setInt32(format, "color-format",
 | |
|                                      color_formats[i].color_format);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (avctx->bit_rate)
 | |
|         ff_AMediaFormat_setInt32(format, "bitrate", avctx->bit_rate);
 | |
|     if (s->bitrate_mode >= 0)
 | |
|         ff_AMediaFormat_setInt32(format, "bitrate-mode", s->bitrate_mode);
 | |
|     // frame-rate and i-frame-interval are required to configure codec
 | |
|     if (avctx->framerate.num >= avctx->framerate.den && avctx->framerate.den > 0) {
 | |
|         s->fps = avctx->framerate.num / avctx->framerate.den;
 | |
|     } else {
 | |
|         s->fps = 30;
 | |
|         av_log(avctx, AV_LOG_INFO, "Use %d as the default MediaFormat frame-rate\n", s->fps);
 | |
|     }
 | |
|     gop = round(avctx->gop_size / s->fps);
 | |
|     if (gop == 0) {
 | |
|         gop = 1;
 | |
|         av_log(avctx, AV_LOG_INFO,
 | |
|                 "Use %d as the default MediaFormat i-frame-interval, "
 | |
|                 "please set gop_size properly (>= fps)\n", gop);
 | |
|     } else {
 | |
|         av_log(avctx, AV_LOG_DEBUG, "Set i-frame-interval to %d\n", gop);
 | |
|     }
 | |
| 
 | |
|     ff_AMediaFormat_setInt32(format, "frame-rate", s->fps);
 | |
|     ff_AMediaFormat_setInt32(format, "i-frame-interval", gop);
 | |
| 
 | |
|     ret = ff_AMediaCodecProfile_getProfileFromAVCodecContext(avctx);
 | |
|     if (ret > 0) {
 | |
|         av_log(avctx, AV_LOG_DEBUG, "set profile to 0x%x\n", ret);
 | |
|         ff_AMediaFormat_setInt32(format, "profile", ret);
 | |
|     }
 | |
|     if (s->level > 0) {
 | |
|         av_log(avctx, AV_LOG_DEBUG, "set level to 0x%x\n", s->level);
 | |
|         ff_AMediaFormat_setInt32(format, "level", s->level);
 | |
|     }
 | |
|     if (avctx->max_b_frames > 0) {
 | |
|         if (avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
 | |
|             av_log(avctx, AV_LOG_ERROR,
 | |
|                     "Enabling B frames will produce packets with no DTS. "
 | |
|                     "Use -strict experimental to use it anyway.\n");
 | |
|             ret = AVERROR(EINVAL);
 | |
|             goto bailout;
 | |
|         }
 | |
|         ff_AMediaFormat_setInt32(format, "max-bframes", avctx->max_b_frames);
 | |
|     }
 | |
|     if (s->pts_as_dts == -1)
 | |
|         s->pts_as_dts = avctx->max_b_frames <= 0;
 | |
| 
 | |
|     ret = ff_AMediaCodec_getConfigureFlagEncode(s->codec);
 | |
|     ret = ff_AMediaCodec_configure(s->codec, format, s->window, NULL, ret);
 | |
|     if (ret) {
 | |
|         av_log(avctx, AV_LOG_ERROR, "MediaCodec configure failed, %s\n", av_err2str(ret));
 | |
|         goto bailout;
 | |
|     }
 | |
| 
 | |
|     ret = ff_AMediaCodec_start(s->codec);
 | |
|     if (ret) {
 | |
|         av_log(avctx, AV_LOG_ERROR, "MediaCodec failed to start, %s\n", av_err2str(ret));
 | |
|         goto bailout;
 | |
|     }
 | |
| 
 | |
|     ret = mediacodec_init_bsf(avctx);
 | |
|     if (ret)
 | |
|         goto bailout;
 | |
| 
 | |
|     mediacodec_output_format(avctx);
 | |
| 
 | |
|     s->frame = av_frame_alloc();
 | |
|     if (!s->frame)
 | |
|         ret = AVERROR(ENOMEM);
 | |
| 
 | |
| bailout:
 | |
|     if (format)
 | |
|         ff_AMediaFormat_delete(format);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static int mediacodec_receive(AVCodecContext *avctx,
 | |
|                                AVPacket *pkt,
 | |
|                                int *got_packet)
 | |
| {
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     FFAMediaCodec *codec = s->codec;
 | |
|     FFAMediaCodecBufferInfo out_info = {0};
 | |
|     uint8_t *out_buf;
 | |
|     size_t out_size = 0;
 | |
|     int ret;
 | |
|     int extradata_size = 0;
 | |
|     int64_t timeout_us = s->eof_sent ? OUTPUT_DEQUEUE_TIMEOUT_US : 0;
 | |
|     ssize_t index = ff_AMediaCodec_dequeueOutputBuffer(codec, &out_info, timeout_us);
 | |
| 
 | |
|     if (ff_AMediaCodec_infoTryAgainLater(codec, index))
 | |
|         return AVERROR(EAGAIN);
 | |
| 
 | |
|     if (ff_AMediaCodec_infoOutputFormatChanged(codec, index)) {
 | |
|         mediacodec_output_format(avctx);
 | |
|         return AVERROR(EAGAIN);
 | |
|     }
 | |
| 
 | |
|     if (ff_AMediaCodec_infoOutputBuffersChanged(codec, index)) {
 | |
|         ff_AMediaCodec_cleanOutputBuffers(codec);
 | |
|         return AVERROR(EAGAIN);
 | |
|     }
 | |
| 
 | |
|     if (index < 0)
 | |
|         return AVERROR_EXTERNAL;
 | |
| 
 | |
|     if (out_info.flags & ff_AMediaCodec_getBufferFlagEndOfStream(codec))
 | |
|         return AVERROR_EOF;
 | |
| 
 | |
|     out_buf = ff_AMediaCodec_getOutputBuffer(codec, index, &out_size);
 | |
|     if (!out_buf) {
 | |
|         ret = AVERROR_EXTERNAL;
 | |
|         goto bailout;
 | |
|     }
 | |
| 
 | |
|     if (out_info.flags & ff_AMediaCodec_getBufferFlagCodecConfig(codec)) {
 | |
|         ret = av_reallocp(&s->extradata, out_info.size);
 | |
|         if (ret)
 | |
|             goto bailout;
 | |
| 
 | |
|         s->extradata_size = out_info.size;
 | |
|         memcpy(s->extradata, out_buf + out_info.offset, out_info.size);
 | |
|         ff_AMediaCodec_releaseOutputBuffer(codec, index, false);
 | |
|         // try immediately
 | |
|         return mediacodec_receive(avctx, pkt, got_packet);
 | |
|     }
 | |
| 
 | |
|     ret = ff_get_encode_buffer(avctx, pkt, out_info.size + s->extradata_size, 0);
 | |
|     if (ret < 0)
 | |
|       goto bailout;
 | |
| 
 | |
|     if (s->extradata_size) {
 | |
|         extradata_size = s->extradata_size;
 | |
|         s->extradata_size = 0;
 | |
|         memcpy(pkt->data, s->extradata, extradata_size);
 | |
|     }
 | |
|     memcpy(pkt->data + extradata_size, out_buf + out_info.offset, out_info.size);
 | |
|     pkt->pts = av_rescale_q(out_info.presentationTimeUs, AV_TIME_BASE_Q, avctx->time_base);
 | |
|     if (s->pts_as_dts)
 | |
|         pkt->dts = pkt->pts;
 | |
|     if (out_info.flags & ff_AMediaCodec_getBufferFlagKeyFrame(codec))
 | |
|         pkt->flags |= AV_PKT_FLAG_KEY;
 | |
|     ret = 0;
 | |
|     *got_packet = 1;
 | |
| 
 | |
|     av_log(avctx, AV_LOG_TRACE, "receive packet pts %" PRId64 " dts %" PRId64
 | |
|            " flags %d extradata %d\n",
 | |
|            pkt->pts, pkt->dts, pkt->flags, extradata_size);
 | |
| 
 | |
| bailout:
 | |
|     ff_AMediaCodec_releaseOutputBuffer(codec, index, false);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void copy_frame_to_buffer(AVCodecContext *avctx, const AVFrame *frame, uint8_t *dst, size_t size)
 | |
| {
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     uint8_t *dst_data[4] = {};
 | |
|     int dst_linesize[4] = {};
 | |
|     const uint8_t *src_data[4] = {
 | |
|             frame->data[0], frame->data[1], frame->data[2], frame->data[3]
 | |
|     };
 | |
| 
 | |
|     if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) {
 | |
|         dst_data[0] = dst;
 | |
|         dst_data[1] = dst + s->width * s->height;
 | |
|         dst_data[2] = dst_data[1] + s->width * s->height / 4;
 | |
| 
 | |
|         dst_linesize[0] = s->width;
 | |
|         dst_linesize[1] = dst_linesize[2] = s->width / 2;
 | |
|     } else if (avctx->pix_fmt == AV_PIX_FMT_NV12) {
 | |
|         dst_data[0] = dst;
 | |
|         dst_data[1] = dst + s->width * s->height;
 | |
| 
 | |
|         dst_linesize[0] = s->width;
 | |
|         dst_linesize[1] = s->width;
 | |
|     } else {
 | |
|         av_assert0(0);
 | |
|     }
 | |
| 
 | |
|     av_image_copy(dst_data, dst_linesize, src_data, frame->linesize,
 | |
|                   avctx->pix_fmt, avctx->width, avctx->height);
 | |
| }
 | |
| 
 | |
| static int mediacodec_send(AVCodecContext *avctx,
 | |
|                            const AVFrame *frame) {
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     FFAMediaCodec *codec = s->codec;
 | |
|     ssize_t index;
 | |
|     uint8_t *input_buf = NULL;
 | |
|     size_t input_size = 0;
 | |
|     int64_t pts = 0;
 | |
|     uint32_t flags = 0;
 | |
|     int64_t timeout_us;
 | |
| 
 | |
|     if (s->eof_sent)
 | |
|         return 0;
 | |
| 
 | |
|     if (s->window) {
 | |
|         if (!frame) {
 | |
|             s->eof_sent = 1;
 | |
|             return ff_AMediaCodec_signalEndOfInputStream(codec);
 | |
|         }
 | |
| 
 | |
|         if (frame->data[3])
 | |
|             av_mediacodec_release_buffer((AVMediaCodecBuffer *)frame->data[3], 1);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     timeout_us = INPUT_DEQUEUE_TIMEOUT_US;
 | |
|     index = ff_AMediaCodec_dequeueInputBuffer(codec, timeout_us);
 | |
|     if (ff_AMediaCodec_infoTryAgainLater(codec, index))
 | |
|         return AVERROR(EAGAIN);
 | |
| 
 | |
|     if (index < 0) {
 | |
|         av_log(avctx, AV_LOG_ERROR, "dequeue input buffer failed, %zd", index);
 | |
|         return AVERROR_EXTERNAL;
 | |
|     }
 | |
| 
 | |
|     if (frame) {
 | |
|         input_buf = ff_AMediaCodec_getInputBuffer(codec, index, &input_size);
 | |
|         copy_frame_to_buffer(avctx, frame, input_buf, input_size);
 | |
| 
 | |
|         pts = av_rescale_q(frame->pts, avctx->time_base, AV_TIME_BASE_Q);
 | |
|     } else {
 | |
|         flags |= ff_AMediaCodec_getBufferFlagEndOfStream(codec);
 | |
|         s->eof_sent = 1;
 | |
|     }
 | |
| 
 | |
|     ff_AMediaCodec_queueInputBuffer(codec, index, 0, input_size, pts, flags);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int mediacodec_encode(AVCodecContext *avctx, AVPacket *pkt)
 | |
| {
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     int ret;
 | |
|     int got_packet = 0;
 | |
| 
 | |
|     // Return on three case:
 | |
|     // 1. Serious error
 | |
|     // 2. Got a packet success
 | |
|     // 3. No AVFrame is available yet (don't return if get_frame return EOF)
 | |
|     while (1) {
 | |
|         if (s->bsf) {
 | |
|             ret = av_bsf_receive_packet(s->bsf, pkt);
 | |
|             if (!ret)
 | |
|                 return 0;
 | |
|             if (ret != AVERROR(EAGAIN))
 | |
|                 return ret;
 | |
|         }
 | |
| 
 | |
|         ret = mediacodec_receive(avctx, pkt, &got_packet);
 | |
|         if (s->bsf) {
 | |
|             if (!ret || ret == AVERROR_EOF)
 | |
|                 ret = av_bsf_send_packet(s->bsf, pkt);
 | |
|         } else {
 | |
|             if (!ret)
 | |
|                 return 0;
 | |
|         }
 | |
| 
 | |
|         if (ret != AVERROR(EAGAIN))
 | |
|             return ret;
 | |
| 
 | |
|         if (!s->frame->buf[0]) {
 | |
|             ret = ff_encode_get_frame(avctx, s->frame);
 | |
|             if (ret && ret != AVERROR_EOF)
 | |
|                 return ret;
 | |
|         }
 | |
| 
 | |
|         ret = mediacodec_send(avctx, s->frame->buf[0] ? s->frame : NULL);
 | |
|         if (!ret)
 | |
|             av_frame_unref(s->frame);
 | |
|         else if (ret != AVERROR(EAGAIN))
 | |
|             return ret;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static av_cold int mediacodec_close(AVCodecContext *avctx)
 | |
| {
 | |
|     MediaCodecEncContext *s = avctx->priv_data;
 | |
|     if (s->codec) {
 | |
|         ff_AMediaCodec_stop(s->codec);
 | |
|         ff_AMediaCodec_delete(s->codec);
 | |
|         s->codec = NULL;
 | |
|     }
 | |
| 
 | |
|     if (s->window) {
 | |
|         ff_mediacodec_surface_unref(s->window, avctx);
 | |
|         s->window = NULL;
 | |
|     }
 | |
| 
 | |
|     av_bsf_free(&s->bsf);
 | |
|     av_frame_free(&s->frame);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static const AVCodecHWConfigInternal *const mediacodec_hw_configs[] = {
 | |
|     &(const AVCodecHWConfigInternal) {
 | |
|         .public          = {
 | |
|             .pix_fmt     = AV_PIX_FMT_MEDIACODEC,
 | |
|             .methods     = AV_CODEC_HW_CONFIG_METHOD_AD_HOC |
 | |
|                            AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
 | |
|             .device_type = AV_HWDEVICE_TYPE_MEDIACODEC,
 | |
|         },
 | |
|         .hwaccel         = NULL,
 | |
|     },
 | |
|     NULL
 | |
| };
 | |
| 
 | |
| #define OFFSET(x) offsetof(MediaCodecEncContext, x)
 | |
| #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 | |
| #define COMMON_OPTION                                                                                       \
 | |
|     { "ndk_codec", "Use MediaCodec from NDK",                                                               \
 | |
|                     OFFSET(use_ndk_codec), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE },                      \
 | |
|     { "codec_name", "Select codec by name",                                                                 \
 | |
|                     OFFSET(name), AV_OPT_TYPE_STRING, {0}, 0, 0, VE },                                      \
 | |
|     { "bitrate_mode", "Bitrate control method",                                                             \
 | |
|                     OFFSET(bitrate_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, "bitrate_mode" },  \
 | |
|     { "cq", "Constant quality mode",                                                                        \
 | |
|                     0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CQ}, 0, 0, VE, "bitrate_mode" },             \
 | |
|     { "vbr", "Variable bitrate mode",                                                                       \
 | |
|                     0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_VBR}, 0, 0, VE, "bitrate_mode" },            \
 | |
|     { "cbr", "Constant bitrate mode",                                                                       \
 | |
|                     0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CBR}, 0, 0, VE, "bitrate_mode" },            \
 | |
|     { "cbr_fd", "Constant bitrate mode with frame drops",                                                   \
 | |
|                     0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CBR_FD}, 0, 0, VE, "bitrate_mode" },         \
 | |
|     { "pts_as_dts", "Use PTS as DTS. It is enabled automatically if avctx max_b_frames <= 0, "              \
 | |
|                     "since most of Android devices don't output B frames by default.",                      \
 | |
|                     OFFSET(pts_as_dts), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE },                         \
 | |
| 
 | |
| 
 | |
| #define MEDIACODEC_ENCODER_CLASS(name)              \
 | |
| static const AVClass name ## _mediacodec_class = {  \
 | |
|     .class_name = #name "_mediacodec",              \
 | |
|     .item_name  = av_default_item_name,             \
 | |
|     .option     = name ## _options,                 \
 | |
|     .version    = LIBAVUTIL_VERSION_INT,            \
 | |
| };                                                  \
 | |
| 
 | |
| #define DECLARE_MEDIACODEC_ENCODER(short_name, long_name, codec_id)     \
 | |
| MEDIACODEC_ENCODER_CLASS(short_name)                                    \
 | |
| const FFCodec ff_ ## short_name ## _mediacodec_encoder = {              \
 | |
|     .p.name           = #short_name "_mediacodec",                      \
 | |
|     CODEC_LONG_NAME(long_name " Android MediaCodec encoder"),           \
 | |
|     .p.type           = AVMEDIA_TYPE_VIDEO,                             \
 | |
|     .p.id             = codec_id,                                       \
 | |
|     .p.capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY           \
 | |
|                         | AV_CODEC_CAP_HARDWARE,                        \
 | |
|     .priv_data_size   = sizeof(MediaCodecEncContext),                   \
 | |
|     .p.pix_fmts       = avc_pix_fmts,                                   \
 | |
|     .init             = mediacodec_init,                                \
 | |
|     FF_CODEC_RECEIVE_PACKET_CB(mediacodec_encode),                      \
 | |
|     .close            = mediacodec_close,                               \
 | |
|     .p.priv_class     = &short_name ## _mediacodec_class,               \
 | |
|     .caps_internal    = FF_CODEC_CAP_INIT_CLEANUP,                      \
 | |
|     .p.wrapper_name = "mediacodec",                                     \
 | |
|     .hw_configs     = mediacodec_hw_configs,                            \
 | |
| };                                                                      \
 | |
| 
 | |
| #if CONFIG_H264_MEDIACODEC_ENCODER
 | |
| 
 | |
| enum MediaCodecAvcLevel {
 | |
|     AVCLevel1       = 0x01,
 | |
|     AVCLevel1b      = 0x02,
 | |
|     AVCLevel11      = 0x04,
 | |
|     AVCLevel12      = 0x08,
 | |
|     AVCLevel13      = 0x10,
 | |
|     AVCLevel2       = 0x20,
 | |
|     AVCLevel21      = 0x40,
 | |
|     AVCLevel22      = 0x80,
 | |
|     AVCLevel3       = 0x100,
 | |
|     AVCLevel31      = 0x200,
 | |
|     AVCLevel32      = 0x400,
 | |
|     AVCLevel4       = 0x800,
 | |
|     AVCLevel41      = 0x1000,
 | |
|     AVCLevel42      = 0x2000,
 | |
|     AVCLevel5       = 0x4000,
 | |
|     AVCLevel51      = 0x8000,
 | |
|     AVCLevel52      = 0x10000,
 | |
|     AVCLevel6       = 0x20000,
 | |
|     AVCLevel61      = 0x40000,
 | |
|     AVCLevel62      = 0x80000,
 | |
| };
 | |
| 
 | |
| static const AVOption h264_options[] = {
 | |
|     COMMON_OPTION
 | |
|     { "level", "Specify level",
 | |
|                 OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" },
 | |
|     { "1",      "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel1  },  0, 0, VE, "level" },
 | |
|     { "1b",     "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel1b }, 0, 0, VE, "level" },
 | |
|     { "1.1",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel11 }, 0, 0, VE, "level" },
 | |
|     { "1.2",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel12 }, 0, 0, VE, "level" },
 | |
|     { "1.3",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel13 }, 0, 0, VE, "level" },
 | |
|     { "2",      "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel2  },  0, 0, VE, "level" },
 | |
|     { "2.1",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel21 }, 0, 0, VE, "level" },
 | |
|     { "2.2",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel22 }, 0, 0, VE, "level" },
 | |
|     { "3",      "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel3  },  0, 0, VE, "level" },
 | |
|     { "3.1",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel31 }, 0, 0, VE, "level" },
 | |
|     { "3.2",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel32 }, 0, 0, VE, "level" },
 | |
|     { "4",      "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel4  },  0, 0, VE, "level" },
 | |
|     { "4.1",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel41 }, 0, 0, VE, "level" },
 | |
|     { "4.2",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel42 }, 0, 0, VE, "level" },
 | |
|     { "5",      "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel5  },  0, 0, VE, "level" },
 | |
|     { "5.1",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel51 }, 0, 0, VE, "level" },
 | |
|     { "5.2",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel52 }, 0, 0, VE, "level" },
 | |
|     { "6.0",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel6  }, 0, 0, VE, "level" },
 | |
|     { "6.1",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel61 }, 0, 0, VE, "level" },
 | |
|     { "6.2",    "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel62 }, 0, 0, VE, "level" },
 | |
|     { NULL, }
 | |
| };
 | |
| 
 | |
| DECLARE_MEDIACODEC_ENCODER(h264, "H.264", AV_CODEC_ID_H264)
 | |
| 
 | |
| #endif  // CONFIG_H264_MEDIACODEC_ENCODER
 | |
| 
 | |
| #if CONFIG_HEVC_MEDIACODEC_ENCODER
 | |
| 
 | |
| enum MediaCodecHevcLevel {
 | |
|     HEVCMainTierLevel1  = 0x1,
 | |
|     HEVCHighTierLevel1  = 0x2,
 | |
|     HEVCMainTierLevel2  = 0x4,
 | |
|     HEVCHighTierLevel2  = 0x8,
 | |
|     HEVCMainTierLevel21 = 0x10,
 | |
|     HEVCHighTierLevel21 = 0x20,
 | |
|     HEVCMainTierLevel3  = 0x40,
 | |
|     HEVCHighTierLevel3  = 0x80,
 | |
|     HEVCMainTierLevel31 = 0x100,
 | |
|     HEVCHighTierLevel31 = 0x200,
 | |
|     HEVCMainTierLevel4  = 0x400,
 | |
|     HEVCHighTierLevel4  = 0x800,
 | |
|     HEVCMainTierLevel41 = 0x1000,
 | |
|     HEVCHighTierLevel41 = 0x2000,
 | |
|     HEVCMainTierLevel5  = 0x4000,
 | |
|     HEVCHighTierLevel5  = 0x8000,
 | |
|     HEVCMainTierLevel51 = 0x10000,
 | |
|     HEVCHighTierLevel51 = 0x20000,
 | |
|     HEVCMainTierLevel52 = 0x40000,
 | |
|     HEVCHighTierLevel52 = 0x80000,
 | |
|     HEVCMainTierLevel6  = 0x100000,
 | |
|     HEVCHighTierLevel6  = 0x200000,
 | |
|     HEVCMainTierLevel61 = 0x400000,
 | |
|     HEVCHighTierLevel61 = 0x800000,
 | |
|     HEVCMainTierLevel62 = 0x1000000,
 | |
|     HEVCHighTierLevel62 = 0x2000000,
 | |
| };
 | |
| 
 | |
| static const AVOption hevc_options[] = {
 | |
|     COMMON_OPTION
 | |
|     { "level", "Specify tier and level",
 | |
|                 OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" },
 | |
|     { "m1",    "Main tier level 1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel1  },  0, 0, VE,  "level" },
 | |
|     { "h1",    "High tier level 1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel1  },  0, 0, VE,  "level" },
 | |
|     { "m2",    "Main tier level 2",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel2  },  0, 0, VE,  "level" },
 | |
|     { "h2",    "High tier level 2",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel2  },  0, 0, VE,  "level" },
 | |
|     { "m2.1",  "Main tier level 2.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel21 },  0, 0, VE,  "level" },
 | |
|     { "h2.1",  "High tier level 2.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel21 },  0, 0, VE,  "level" },
 | |
|     { "m3",    "Main tier level 3",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel3  },  0, 0, VE,  "level" },
 | |
|     { "h3",    "High tier level 3",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel3  },  0, 0, VE,  "level" },
 | |
|     { "m3.1",  "Main tier level 3.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel31 },  0, 0, VE,  "level" },
 | |
|     { "h3.1",  "High tier level 3.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel31 },  0, 0, VE,  "level" },
 | |
|     { "m4",    "Main tier level 4",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel4  },  0, 0, VE,  "level" },
 | |
|     { "h4",    "High tier level 4",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel4  },  0, 0, VE,  "level" },
 | |
|     { "m4.1",  "Main tier level 4.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel41 },  0, 0, VE,  "level" },
 | |
|     { "h4.1",  "High tier level 4.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel41 },  0, 0, VE,  "level" },
 | |
|     { "m5",    "Main tier level 5",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel5  },  0, 0, VE,  "level" },
 | |
|     { "h5",    "High tier level 5",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel5  },  0, 0, VE,  "level" },
 | |
|     { "m5.1",  "Main tier level 5.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel51 },  0, 0, VE,  "level" },
 | |
|     { "h5.1",  "High tier level 5.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel51 },  0, 0, VE,  "level" },
 | |
|     { "m5.2",  "Main tier level 5.2",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel52 },  0, 0, VE,  "level" },
 | |
|     { "h5.2",  "High tier level 5.2",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel52 },  0, 0, VE,  "level" },
 | |
|     { "m6",    "Main tier level 6",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel6  },  0, 0, VE,  "level" },
 | |
|     { "h6",    "High tier level 6",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel6  },  0, 0, VE,  "level" },
 | |
|     { "m6.1",  "Main tier level 6.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel61 },  0, 0, VE,  "level" },
 | |
|     { "h6.1",  "High tier level 6.1",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel61 },  0, 0, VE,  "level" },
 | |
|     { "m6.2",  "Main tier level 6.2",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel62 },  0, 0, VE,  "level" },
 | |
|     { "h6.2",  "High tier level 6.2",
 | |
|                 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel62 },  0, 0, VE,  "level" },
 | |
|     { NULL, }
 | |
| };
 | |
| 
 | |
| DECLARE_MEDIACODEC_ENCODER(hevc, "H.265", AV_CODEC_ID_HEVC)
 | |
| 
 | |
| #endif  // CONFIG_HEVC_MEDIACODEC_ENCODER
 | 
