On Sat, Nov 19, 2022 at 9:13 AM "zhilizhao(赵志立)" <quinkbl...@foxmail.com> wrote:
> > > > On Nov 19, 2022, at 17:15, Aman Karmani <a...@tmm1.net> wrote: > > > > > > > > On Sun, Oct 23, 2022 at 8:17 PM Zhao Zhili <quinkbl...@foxmail.com> > wrote: > > From: Zhao Zhili <zhiliz...@tencent.com> > > > > Signed-off-by: Zhao Zhili <zhiliz...@tencent.com> > > --- > > Changelog | 1 + > > configure | 4 + > > libavcodec/Makefile | 2 + > > libavcodec/allcodecs.c | 2 + > > libavcodec/mediacodec_wrapper.c | 102 ++++++- > > libavcodec/mediacodec_wrapper.h | 8 + > > libavcodec/mediacodecenc.c | 495 ++++++++++++++++++++++++++++++++ > > libavcodec/version.h | 4 +- > > 8 files changed, 611 insertions(+), 7 deletions(-) > > create mode 100644 libavcodec/mediacodecenc.c > > > > diff --git a/Changelog b/Changelog > > index 9e203833aa..9e39a35972 100644 > > --- a/Changelog > > +++ b/Changelog > > @@ -19,6 +19,7 @@ version <next>: > > - DTS to PTS reorder bsf > > - ViewQuest VQC decoder > > - MediaCodec decoder via NDKMediaCodec > > +- MediaCodec encoder > > > > > > > > version 5.1: > > diff --git a/configure b/configure > > index ee2e3ba6ac..5114cda13f 100755 > > --- a/configure > > +++ b/configure > > @@ -3193,6 +3193,8 @@ h264_cuvid_decoder_select="h264_mp4toannexb_bsf" > > h264_mediacodec_decoder_deps="mediacodec" > > h264_mediacodec_decoder_extralibs="-landroid" > > h264_mediacodec_decoder_select="h264_mp4toannexb_bsf h264_parser" > > +h264_mediacodec_encoder_deps="mediacodec" > > +h264_mediacodec_encoder_extralibs="-landroid" > > h264_mf_encoder_deps="mediafoundation" > > h264_mmal_decoder_deps="mmal" > > h264_nvenc_encoder_deps="nvenc" > > @@ -3212,6 +3214,8 @@ hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" > > hevc_mediacodec_decoder_deps="mediacodec" > > hevc_mediacodec_decoder_extralibs="-landroid" > > hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" > > +hevc_mediacodec_encoder_deps="mediacodec" > > +hevc_mediacodec_encoder_extralibs="-landroid" > > hevc_mf_encoder_deps="mediafoundation" > > hevc_nvenc_encoder_deps="nvenc" > > hevc_nvenc_encoder_select="atsc_a53" > > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > > index 90c7f113a3..7d0b513eec 100644 > > --- a/libavcodec/Makefile > > +++ b/libavcodec/Makefile > > @@ -393,6 +393,7 @@ OBJS-$(CONFIG_H264_DECODER) += h264dec.o > h264_cabac.o h264_cavlc.o \ > > OBJS-$(CONFIG_H264_AMF_ENCODER) += amfenc_h264.o > > OBJS-$(CONFIG_H264_CUVID_DECODER) += cuviddec.o > > OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o > > +OBJS-$(CONFIG_H264_MEDIACODEC_ENCODER) += mediacodecenc.o > > OBJS-$(CONFIG_H264_MF_ENCODER) += mfenc.o mf_utils.o > > OBJS-$(CONFIG_H264_MMAL_DECODER) += mmaldec.o > > OBJS-$(CONFIG_H264_NVENC_ENCODER) += nvenc_h264.o nvenc.o > > @@ -417,6 +418,7 @@ OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o > hevc_mvs.o \ > > OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o > > OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o > > OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o > > +OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o > > OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o > > OBJS-$(CONFIG_HEVC_NVENC_ENCODER) += nvenc_hevc.o nvenc.o > > OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o > > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c > > index 46ad3b5a25..4c33a9ec3c 100644 > > --- a/libavcodec/allcodecs.c > > +++ b/libavcodec/allcodecs.c > > @@ -154,6 +154,7 @@ extern const FFCodec ff_h264_decoder; > > extern const FFCodec ff_h264_crystalhd_decoder; > > extern const FFCodec ff_h264_v4l2m2m_decoder; > > extern const FFCodec ff_h264_mediacodec_decoder; > > +extern const FFCodec ff_h264_mediacodec_encoder; > > extern const FFCodec ff_h264_mmal_decoder; > > extern const FFCodec ff_h264_qsv_decoder; > > extern const FFCodec ff_h264_rkmpp_decoder; > > @@ -842,6 +843,7 @@ extern const FFCodec ff_h264_videotoolbox_encoder; > > extern const FFCodec ff_hevc_amf_encoder; > > extern const FFCodec ff_hevc_cuvid_decoder; > > extern const FFCodec ff_hevc_mediacodec_decoder; > > +extern const FFCodec ff_hevc_mediacodec_encoder; > > extern const FFCodec ff_hevc_mf_encoder; > > extern const FFCodec ff_hevc_nvenc_encoder; > > extern const FFCodec ff_hevc_qsv_encoder; > > diff --git a/libavcodec/mediacodec_wrapper.c > b/libavcodec/mediacodec_wrapper.c > > index 284d615980..5d1a32031d 100644 > > --- a/libavcodec/mediacodec_wrapper.c > > +++ b/libavcodec/mediacodec_wrapper.c > > @@ -212,6 +212,9 @@ struct JNIAMediaCodecFields { > > jmethodID release_output_buffer_id; > > jmethodID release_output_buffer_at_time_id; > > > > + jmethodID set_input_surface_id; > > + jmethodID signal_end_of_input_stream_id; > > + > > jclass mediainfo_class; > > > > jmethodID init_id; > > @@ -261,6 +264,9 @@ static const struct FFJniField > jni_amediacodec_mapping[] = { > > { "android/media/MediaCodec", "releaseOutputBuffer", "(IZ)V", > FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, > release_output_buffer_id), 1 }, > > { "android/media/MediaCodec", "releaseOutputBuffer", "(IJ)V", > FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, > release_output_buffer_at_time_id), 0 }, > > > > + { "android/media/MediaCodec", "setInputSurface", > "(Landroid/view/Surface;)V", FF_JNI_METHOD, offsetof(struct > JNIAMediaCodecFields, set_input_surface_id), 0 }, > > + { "android/media/MediaCodec", "signalEndOfInputStream", "()V", > FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, > signal_end_of_input_stream_id), 0 }, > > + > > { "android/media/MediaCodec$BufferInfo", NULL, NULL, FF_JNI_CLASS, > offsetof(struct JNIAMediaCodecFields, mediainfo_class), 1 }, > > > > { "android/media/MediaCodec.BufferInfo", "<init>", "()V", > FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, init_id), 1 }, > > @@ -1385,7 +1391,26 @@ static int mediacodec_jni_configure(FFAMediaCodec > *ctx, > > > > JNI_GET_ENV_OR_RETURN(env, codec, AVERROR_EXTERNAL); > > > > - (*env)->CallVoidMethod(env, codec->object, > codec->jfields.configure_id, format->object, surface, NULL, flags); > > + if (flags & codec->CONFIGURE_FLAG_ENCODE) { > > + if (surface && !codec->jfields.set_input_surface_id) { > > + av_log(ctx, AV_LOG_ERROR, "System doesn't support > setInputSurface\n"); > > + return AVERROR_EXTERNAL; > > + } > > + > > + (*env)->CallVoidMethod(env, codec->object, > codec->jfields.configure_id, format->object, NULL, NULL, flags); > > + if (ff_jni_exception_check(env, 1, codec) < 0) > > + return AVERROR_EXTERNAL; > > + > > + if (!surface) > > + return 0; > > + > > + (*env)->CallVoidMethod(env, codec->object, > codec->jfields.set_input_surface_id, surface); > > + if (ff_jni_exception_check(env, 1, codec) < 0) > > + return AVERROR_EXTERNAL; > > + return 0; > > + } else { > > + (*env)->CallVoidMethod(env, codec->object, > codec->jfields.configure_id, format->object, surface, NULL, flags); > > + } > > if (ff_jni_exception_check(env, 1, codec) < 0) { > > ret = AVERROR_EXTERNAL; > > goto fail; > > @@ -1743,6 +1768,22 @@ fail: > > return ret; > > } > > > > +static int mediacodec_jni_signalEndOfInputStream(FFAMediaCodec *ctx) > > +{ > > + int ret = 0; > > + JNIEnv *env = NULL; > > + FFAMediaCodecJni *codec = (FFAMediaCodecJni *)ctx; > > + > > + JNI_GET_ENV_OR_RETURN(env, codec, AVERROR_EXTERNAL); > > + > > + (*env)->CallVoidMethod(env, codec->object, > codec->jfields.signal_end_of_input_stream_id); > > + if (ff_jni_exception_check(env, 1, codec) < 0) { > > + return AVERROR_EXTERNAL; > > + } > > + > > + return 0; > > +} > > + > > static const FFAMediaFormat media_format_jni = { > > .class = &amediaformat_class, > > > > @@ -1801,6 +1842,7 @@ static const FFAMediaCodec media_codec_jni = { > > > > .getConfigureFlagEncode = mediacodec_jni_getConfigureFlagEncode, > > .cleanOutputBuffers = mediacodec_jni_cleanOutputBuffers, > > + .signalEndOfInputStream = mediacodec_jni_signalEndOfInputStream, > > }; > > > > typedef struct FFAMediaFormatNdk { > > @@ -1866,6 +1908,10 @@ typedef struct FFAMediaCodecNdk { > > // Available since API level 28. > > media_status_t (*getName)(AMediaCodec*, char** out_name); > > void (*releaseName)(AMediaCodec*, char* name); > > + > > + // Available since API level 26. > > + media_status_t (*setInputSurface)(AMediaCodec*, ANativeWindow *); > > + media_status_t (*signalEndOfInputStream)(AMediaCodec *); > > } FFAMediaCodecNdk; > > > > static const FFAMediaFormat media_format_ndk; > > @@ -2098,6 +2144,9 @@ static inline FFAMediaCodec *ndk_codec_create(int > method, const char *arg) { > > GET_SYMBOL(getName, 0) > > GET_SYMBOL(releaseName, 0) > > > > + GET_SYMBOL(setInputSurface, 0) > > + GET_SYMBOL(signalEndOfInputStream, 0) > > + > > #undef GET_SYMBOL > > > > switch (method) { > > @@ -2184,10 +2233,32 @@ static int > mediacodec_ndk_configure(FFAMediaCodec* ctx, > > return AVERROR(EINVAL); > > } > > > > - status = codec->configure(codec->impl, format->impl, native_window, > NULL, flags); > > - if (status != AMEDIA_OK) { > > - av_log(codec, AV_LOG_ERROR, "configure failed, %d\n", status); > > - return AVERROR_EXTERNAL; > > + if (flags & AMEDIACODEC_CONFIGURE_FLAG_ENCODE) { > > + if (native_window && !codec->setInputSurface) { > > + av_log(ctx, AV_LOG_ERROR, "System doesn't support > setInputSurface\n"); > > + return AVERROR_EXTERNAL; > > + } > > + > > + status = codec->configure(codec->impl, format->impl, NULL, > NULL, flags); > > + if (status != AMEDIA_OK) { > > + av_log(codec, AV_LOG_ERROR, "Encoder configure failed, > %d\n", status); > > + return AVERROR_EXTERNAL; > > + } > > + > > + if (!native_window) > > + return 0; > > + > > + status = codec->setInputSurface(codec->impl, native_window); > > + if (status != AMEDIA_OK) { > > + av_log(codec, AV_LOG_ERROR, "Encoder set input surface > failed, %d\n", status); > > + return AVERROR_EXTERNAL; > > + } > > + } else { > > + status = codec->configure(codec->impl, format->impl, > native_window, NULL, flags); > > + if (status != AMEDIA_OK) { > > + av_log(codec, AV_LOG_ERROR, "Decoder configure failed, > %d\n", status); > > + return AVERROR_EXTERNAL; > > + } > > } > > > > return 0; > > @@ -2330,6 +2401,26 @@ static int > mediacodec_ndk_cleanOutputBuffers(FFAMediaCodec *ctx) > > return 0; > > } > > > > +static int mediacodec_ndk_signalEndOfInputStream(FFAMediaCodec *ctx) > > +{ > > + FFAMediaCodecNdk *codec = (FFAMediaCodecNdk *)ctx; > > + media_status_t status; > > + > > + if (!codec->signalEndOfInputStream) { > > + av_log(codec, AV_LOG_ERROR, "signalEndOfInputStream > unavailable\n"); > > + return AVERROR_EXTERNAL; > > + } > > + > > + status = codec->signalEndOfInputStream(codec->impl); > > + if (status != AMEDIA_OK) { > > + av_log(codec, AV_LOG_ERROR, "signalEndOfInputStream failed, > %d\n", status); > > + return AVERROR_EXTERNAL; > > + } > > + av_log(codec, AV_LOG_DEBUG, "signalEndOfInputStream success\n"); > > + > > + return 0; > > +} > > + > > static const FFAMediaFormat media_format_ndk = { > > .class = &amediaformat_ndk_class, > > > > @@ -2388,6 +2479,7 @@ static const FFAMediaCodec media_codec_ndk = { > > > > .getConfigureFlagEncode = mediacodec_ndk_getConfigureFlagEncode, > > .cleanOutputBuffers = mediacodec_ndk_cleanOutputBuffers, > > + .signalEndOfInputStream = mediacodec_ndk_signalEndOfInputStream, > > }; > > > > FFAMediaFormat *ff_AMediaFormat_new(int ndk) > > diff --git a/libavcodec/mediacodec_wrapper.h > b/libavcodec/mediacodec_wrapper.h > > index 7cf3f4aecd..f15ad66d83 100644 > > --- a/libavcodec/mediacodec_wrapper.h > > +++ b/libavcodec/mediacodec_wrapper.h > > @@ -192,6 +192,9 @@ struct FFAMediaCodec { > > int (*getConfigureFlagEncode)(FFAMediaCodec *codec); > > > > int (*cleanOutputBuffers)(FFAMediaCodec *codec); > > + > > + // For encoder with FFANativeWindow as input. > > + int (*signalEndOfInputStream)(FFAMediaCodec *); > > }; > > > > static inline char *ff_AMediaCodec_getName(FFAMediaCodec *codec) > > @@ -311,6 +314,11 @@ static inline int > ff_AMediaCodec_cleanOutputBuffers(FFAMediaCodec *codec) > > return codec->cleanOutputBuffers(codec); > > } > > > > +static inline int ff_AMediaCodec_signalEndOfInputStream(FFAMediaCodec > *codec) > > +{ > > + return codec->signalEndOfInputStream(codec); > > +} > > + > > int ff_Build_SDK_INT(AVCodecContext *avctx); > > > > #endif /* AVCODEC_MEDIACODEC_WRAPPER_H */ > > diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c > > new file mode 100644 > > index 0000000000..c81050ec80 > > --- /dev/null > > +++ b/libavcodec/mediacodecenc.c > > @@ -0,0 +1,495 @@ > > +/* > > + * Android MediaCodec encoders > > + * > > + * Copyright (c) 2022 Zhao Zhili <zhiliz...@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 "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 > > + > > +typedef struct MediaCodecEncContext { > > + AVClass *avclass; > > + FFAMediaCodec *codec; > > + int use_ndk_codec; > > + FFANativeWindow *window; > > + > > + int fps; > > + int width; > > + int height; > > + > > + uint8_t *extradata; > > + int extradata_size; > > + > > + // Since MediaCodec doesn't output DTS, use a timestamp queue to > save pts > > + // of AVFrame and generate DTS for AVPacket. > > + // > > + // This doesn't work when use Surface as input, in that case frames > can be > > + // sent to encoder without our notice. One exception is frames come > from > > + // our MediaCodec decoder wrapper, since we can control it's render > by > > + // av_mediacodec_release_buffer. > > + int64_t timestamps[32]; > > + int ts_head; > > + int ts_tail; > > + > > + int eof_sent; > > + > > + AVFrame *frame; > > +} 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 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); > > + } > > + > > + 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); > > + s->width = FFALIGN(avctx->width, 16); > > + s->height = avctx->height; > > + ff_AMediaFormat_setInt32(format, "width", s->width); > > + ff_AMediaFormat_setInt32(format, "height", s->height); > > > > Is it preferable to use constants like AMEDIAFORMAT_KEY_HEIGHT here? > > > > I don't have a preference either way. > > These KEYs aren’t string literal, they are global variables. > > extern const char* AMEDIAFORMAT_KEY_HEIGHT __INTRODUCED_IN(21); > > Some basic ones are introduced very late, e.g., > > extern const char* AMEDIAFORMAT_KEY_CSD __INTRODUCED_IN(28); > extern const char* AMEDIAFORMAT_KEY_CSD_0 __INTRODUCED_IN(28); > extern const char* AMEDIAFORMAT_KEY_CSD_1 __INTRODUCED_IN(28); > extern const char* AMEDIAFORMAT_KEY_CSD_2 __INTRODUCED_IN(28); > extern const char* AMEDIAFORMAT_KEY_DISPLAY_CROP __INTRODUCED_IN(28); > > So we can’t use these AMEDIAFORMAT_KEY_ directly. dlsym() these global > variables and with a fallback value is possible, just over-engineering. > > Google’s API design forced us to use string literal directly. > > > > > It may be worth also passing in the same values to > AMEDIAFORMAT_KEY_MAX_WIDTH and AMEDIAFORMAT_KEY_MAX_HEIGHT > > The documentation hints they are for decoder only: > > > A key describing the maximum expected width of the content in a video > decoder format, in case there are resolution changes in the video content. > > https://developer.android.com/reference/android/media/MediaFormat > > > > > And I think the unaligned width should be set into > AMEDIAFORMAT_KEY_STRIDE > > Technically, KEY_WIDTH should be unaligned width, and KEY_STRIDE should > be aligned width. However, > > 1. It’s well known that a lot of devices can’t handle width doesn’t aligned > to 16, they will crash, report error or produce broken files. > > 2. CTS tests only verify that 16 aligned resolutions are supported. > > 3. There are alignment info in OMX and exported via MediaCodecInfo, like > > <MediaCodec name="c2.qti.avc.encoder" type="video/avc"> > <Alias name="OMX.qcom.video.encoder.avc" /> > <Limit name="size" min="96x96" max="4096x2304" /> > <Limit name="alignment" value="2x2" /> > <Limit name="block-size" value="16x16" /> > > It can be missing on old devices, or worse, the info doesn’t reflect the > real requirement (Maybe it was just a copy-paste from some places). > > I have an idea to fix the issue: always encoding in aligned resolution, > then fix the crop setting by apply BSF (e.g., h264_metadata) on the > bitstream. > > > > > Finally, avctx->sample_aspect_ratio should be propagated into > aspect-width and aspect-height. Something like this: > > > > AVRational sar = avctx->sample_aspect_ratio; > > if (!sar.num || !sar.den) > > sar.num = sar.den = 1; > > av_reduce(&sar.num, &sar.den, sar.num, sar.den, 4096); > > AMediaFormat_setInt32(format, "aspect-width", sar.num); > > AMediaFormat_setInt32(format, "aspect-height", sar.den); > > > > You mean sar-width/sar-height? > > EXPORT const char* AMEDIAFORMAT_KEY_SAR_HEIGHT = "sar-height"; > EXPORT const char* AMEDIAFORMAT_KEY_SAR_WIDTH = "sar-width"; > > They are available in NDK since API level 29 (Android 10) > > extern const char* AMEDIAFORMAT_KEY_SAR_HEIGHT __INTRODUCED_IN(29); > extern const char* AMEDIAFORMAT_KEY_SAR_WIDTH __INTRODUCED_IN(29); > > And they were added to Java MediaFormat since API level 30. > > > https://developer.android.com/reference/android/media/MediaFormat#KEY_PIXEL_ASPECT_RATIO_WIDTH > > It’s uncommon for Java API got a feature late than NDK. I will keep > a note and do more test before setting them. > > > > > + > > + 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; > > + } > > + } > > + > > + 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; > > > > do we need error/fallback if no match is found? > > The supported pix_fmts is specified > > .priv_data_size = sizeof(MediaCodecEncContext), \ > .p.pix_fmts = avc_pix_fmts, \ > .init = mediacodec_init, \ > > It has been checked in encoder.c encode_preinit_video(), so check it again > is > optional but not necessary in my opinion. > > > > > > > + } > > + } > > + > > + if (avctx->bit_rate) > > + ff_AMediaFormat_setInt32(format, "bitrate", avctx->bit_rate); > > + // 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; > > + gop = round(avctx->gop_size / s->fps); > > + if (gop == 0) > > + gop = 2; > > > > can we read gop from avctx? in other implementations i have seen gop > hardcoded to 1 > > I think we should respect gop_size setting. > > I will add some log message and change default gop to 1 in patch v2. > > > > > for fps value, can we use av_q2d(avctx->framerate) > > q2d() is for double, fps is integer here. > > > > > > > + ff_AMediaFormat_setInt32(format, "frame-rate", s->fps); > > + ff_AMediaFormat_setInt32(format, "i-frame-interval", gop); > > + > > > > for H264 encoding, you can pass "profile" and "level". for example > profile=0x08 for High and level=0x4000 for 5.0 > > > > https://stackoverflow.com/a/38999412/332798 > > I’m planning to add profile/level support after the basic patch is applied. > There are some unresolved issue here: > > 1. I have a device which failed at configure() when profile has been set, > even for baseline. > Thanks for sharing this. Today I had the same experience, with OMX.rk.video_encoder.avc trying to pass profile/level. I see the following message in logcat. Did you see the same behavior? 11-20 21:55:16.132 5086 5098 W ACodec : [OMX.rk.video_encoder.avc] stopping checking profiles after 32: 8/1 11-20 21:55:16.132 5086 5098 E ACodec : [OMX.rk.video_encoder.avc] configureCodec returning error -1010 > > 2. DTS is missing in MediaCodec API. For the default baseline profile, > there > is no problem. Trouble comes when we set profile to mainline or high. If > Surface/ANativeWindow is used, and the frames come from our MediaCodec > decoder > wrapper, we can control it’s 'render' (send to encoder's surface) via > av_mediacodec_release_buffer(). A DTS generation strategy works in this > case. > However, if frames comes from other sources, like a camera, there is no way > to control the 'render' yet, so DTS is missing in this case. > > Configure profile/level and B frames should come together. > > > > > > > + > > + 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; > > + } > > + > > + 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->ts_tail != s->ts_head) { > > + pkt->dts = s->timestamps[s->ts_tail]; > > + s->ts_tail = (s->ts_tail + 1) % FF_ARRAY_ELEMS(s->timestamps); > > + } > > + > > + 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]) { > > + pts = av_rescale_q(frame->pts, avctx->time_base, > AV_TIME_BASE_Q); > > + s->timestamps[s->ts_head] = frame->pts; > > + s->ts_head = (s->ts_head + 1) % > FF_ARRAY_ELEMS(s->timestamps); > > + > > + 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); > > + > > + s->timestamps[s->ts_head] = frame->pts; > > + s->ts_head = (s->ts_head + 1) % FF_ARRAY_ELEMS(s->timestamps); > > + } else { > > + flags |= ff_AMediaCodec_getBufferFlagEndOfStream(codec); > > + s->eof_sent = 1; > > + } > > + > > > > it would be nice to propagate keyframes here (frame->pict_type == > AV_PICTURE_TYPE_I). it is only possible on API26 with > AMediaCodec_setParameters and PARAMETER_KEY_REQUEST_SYNC_FRAME > > It’s a nice feature. I’m planning to add support after the basic function > is working. > > > > > > > + 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) { > > + ret = mediacodec_receive(avctx, pkt, &got_packet); > > + if (!ret) > > + return 0; > > + else 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_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 > > +static const AVOption common_options[] = { > > + { "ndk_codec", "Use MediaCodec from NDK", > > + OFFSET(use_ndk_codec), AV_OPT_TYPE_BOOL, {.i64 = > -1}, -1, 1, VE }, > > + { NULL }, > > +}; > > + > > +#define MEDIACODEC_ENCODER_CLASS(name) \ > > +static const AVClass name ## _mediacodec_class = { \ > > + .class_name = #name "_mediacodec", \ > > + .item_name = av_default_item_name, \ > > + .option = common_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 > > +DECLARE_MEDIACODEC_ENCODER(h264, "H.264", AV_CODEC_ID_H264) > > +#endif > > + > > +#if CONFIG_HEVC_MEDIACODEC_ENCODER > > +DECLARE_MEDIACODEC_ENCODER(hevc, "H.265", AV_CODEC_ID_HEVC) > > +#endif > > diff --git a/libavcodec/version.h b/libavcodec/version.h > > index 43d0d9a9fc..86ac0f3871 100644 > > --- a/libavcodec/version.h > > +++ b/libavcodec/version.h > > @@ -29,8 +29,8 @@ > > > > #include "version_major.h" > > > > -#define LIBAVCODEC_VERSION_MINOR 51 > > -#define LIBAVCODEC_VERSION_MICRO 101 > > +#define LIBAVCODEC_VERSION_MINOR 52 > > +#define LIBAVCODEC_VERSION_MICRO 100 > > > > #define LIBAVCODEC_VERSION_INT > AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ > > > LIBAVCODEC_VERSION_MINOR, \ > > -- > > 2.25.1 > > > > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".