From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id C6ABC4CA67 for ; Tue, 28 Oct 2025 16:37:07 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'z2KPOWMb11mX/lUfqOYGPBCxm+RETJHtpr3gYu37i6A=', expected b'dNxTD1252r6Kp9NBRJHXz5Osgk6Z1yQRqN84La8+71w=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1761669412; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=z2KPOWMb11mX/lUfqOYGPBCxm+RETJHtpr3gYu37i6A=; b=4LfH4K/7c2DQAId5Q4fLPgp15SB45TkTjFVEIVukII2RN82sdjPXyGdg4sN0OrWyWgqZx vPCZfGbr342aicLpwfYlxCPeN3XzJNqbjlUdr2vGEN25XF472fx0hPIuOwHvuTLyfGR4sLa Vja5RSSqCvMiLGZzYcSpzGtOu7JVixALEAdOabGszlEpOJw+3M7ieIbAd8ybC9HajCbMH5t koSaOM3hxXvxAa2ee7Ekb/rohBEMXJJp+uDFW60DasX4LFVtTvXS8HNoV4HfxIzVGyVTqFw QQlG5KsuOnZKm0LpA3PM6MG8Q3U+biecgOXpronTsSANlTnY6DZfjGTibTAw== Received: from [172.19.0.2] (unknown [172.19.0.2]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id F31A168F64C; Tue, 28 Oct 2025 18:36:51 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1761669384; b=WIkRqyGPuefWEDCOT++57oezA3AMOwRzwTJ1gX3TBzVmx3X5ZsF1u6jtlGp2LgeyI2PwL JC8U0Eo1AYhO6T9Sz5Tn18LF+cVpBDV3p1D64CtnqWrCg1TcjwCOVwu9FysRGLBnj0sVuGY E/1Woib9IrwLzjjqj2ZgOIS1rDEme+Oi1Bb7F3d39OFlSlEuI2vsT52iQaduetGhkTqwo9p +hDLMxklMWjXdqS8aVr1rliy3mTRdpW8JKdSSKQkdUJJEjAJLra/JCJtO2gbbbUmN2USKI2 CWEIT0RmCqEZQGHFmHnuV3BVMcUugQrF+6u1fKeIVsFdcwYN0ik7CnIjNEGw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1761669384; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=ZD1g7aJ1Pxu5VhT1DGNe1EixtsMRVWboRkcxe5IWBJY=; b=txBHeBSw0tOJRAOM3hdTHJuL8VxvSpK0H5755wchobeLqfFy4J2tx9hfBr3pKuqm1AiyY ysWEDjFbT3GkR3miQmfxC5leuBPHJT7Q7u4yds340qZ2wu00n+/Y5D0TJoC4tsHSCRGrjmE EJs/BkhaLy/b7NyFtIMvyJDZNI46oVcsT3yiG9lHyUaYZYawLzbfAD2+3wZUIyseJB/jC+R 21yV5mNJhw/sLvA/rJMxjxUCkJhEf5ID7jltkpRGiA2e++jiBnq+mB5Uibq2Ak7WUPYjFwF 2z5Xydq4KkyR3/evdUEuMLW4JrxwnXmHlp4Pgy8PxWBUiFc5+AIoUmBaUxbg== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1761669378; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=dNxTD1252r6Kp9NBRJHXz5Osgk6Z1yQRqN84La8+71w=; b=Oro48x6ItaN0Y0GTikJcGL4hVTtY6M3vs1M46/somRCBAqZ24QS7G8BKVbKbRqpJPd+bb tlZQEaABke1ik1GlW9Vg6z4CGLgbPT9UbkjGuBYgN9X7kG2W0tRHi1aGkHBhn+BHwNgCgJT LBKUtY5q+bsj3+/DcbsiN33EypL+bOg2g4tbCXA6xQ6ePYtiULWC9Ef7W5zPLXC7mC4aubk vI5miEFj/viiKEy9lhCrt+dnSPxEaTU19brmuacioQ69ExMnHiI36P18TQ5EqRJSaMUBShm XpdAlQ094DNFZZ+X75yvUKw77ZuxW7Ty3upK71iCpQyAQGZ+TmPvrxRbTSLw== Received: from 02c22a36bd31 (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 1D90868F649 for ; Tue, 28 Oct 2025 18:36:17 +0200 (EET) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Tue, 28 Oct 2025 16:36:16 -0000 Message-ID: <176166937835.81.17312287888636357291@7d278768979e> Message-ID-Hash: 5BIKV2YUAEMZXSM6FLVGRKPSUAVSAKPQ X-Message-ID-Hash: 5BIKV2YUAEMZXSM6FLVGRKPSUAVSAKPQ X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] WIP: avcodec/rkmppenc: add h264/hevc rkmpp encoder (PR #20776) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Zhao Zhili via ffmpeg-devel Cc: Zhao Zhili Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #20776 opened by Zhao Zhili (quink) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20776 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20776.patch >>From 6d24cec555f0a43feda1f0b903548464494be293 Mon Sep 17 00:00:00 2001 From: Zhao Zhili Date: Tue, 7 Oct 2025 22:15:50 +0800 Subject: [PATCH] avcodec/rkmppenc: add h264/hevc rkmpp encoder --- configure | 2 + libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 2 + libavcodec/rkmppenc.c | 462 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 468 insertions(+) create mode 100644 libavcodec/rkmppenc.c diff --git a/configure b/configure index 764bbb0001..d9fb9cfb71 100755 --- a/configure +++ b/configure @@ -3481,6 +3481,7 @@ h264_qsv_decoder_select="h264_mp4toannexb_bsf qsvdec" h264_qsv_encoder_select="atsc_a53 qsvenc" h264_rkmpp_decoder_deps="rkmpp" h264_rkmpp_decoder_select="h264_mp4toannexb_bsf" +h264_rkmpp_encoder_deps="rkmpp" h264_vaapi_encoder_select="atsc_a53 cbs_h264 vaapi_encode" h264_vulkan_encoder_select="atsc_a53 cbs_h264 vulkan_encode" h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m" @@ -3505,6 +3506,7 @@ hevc_qsv_decoder_select="hevc_mp4toannexb_bsf qsvdec" hevc_qsv_encoder_select="hevcparse qsvenc" hevc_rkmpp_decoder_deps="rkmpp" hevc_rkmpp_decoder_select="hevc_mp4toannexb_bsf" +hevc_rkmpp_encoder_deps="rkmpp" hevc_vaapi_encoder_deps="VAEncPictureParameterBufferHEVC" hevc_vaapi_encoder_select="atsc_a53 cbs_h265 vaapi_encode" hevc_vulkan_encoder_select="atsc_a53 cbs_h265 vulkan_encode" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 972a17f060..1419628f44 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -445,6 +445,7 @@ OBJS-$(CONFIG_H264_OMX_ENCODER) += omx.o OBJS-$(CONFIG_H264_QSV_DECODER) += qsvdec.o OBJS-$(CONFIG_H264_QSV_ENCODER) += qsvenc_h264.o OBJS-$(CONFIG_H264_RKMPP_DECODER) += rkmppdec.o +OBJS-$(CONFIG_H264_RKMPP_ENCODER) += rkmppenc.o OBJS-$(CONFIG_H264_VAAPI_ENCODER) += vaapi_encode_h264.o h264_levels.o \ h2645data.o hw_base_encode_h264.o OBJS-$(CONFIG_H264_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h264.o \ @@ -474,6 +475,7 @@ OBJS-$(CONFIG_HEVC_OH_ENCODER) += ohcodec.o ohenc.o OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc/ps_enc.o OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o +OBJS-$(CONFIG_HEVC_RKMPP_ENCODER) += rkmppenc.o OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o h265_profile_level.o \ h2645data.o hw_base_encode_h265.o OBJS-$(CONFIG_HEVC_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h265.o \ diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 251ffae390..68e27e28a6 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -156,11 +156,13 @@ 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; +extern const FFCodec ff_h264_rkmpp_encoder; extern const FFCodec ff_hap_encoder; extern const FFCodec ff_hap_decoder; extern const FFCodec ff_hevc_decoder; extern const FFCodec ff_hevc_qsv_decoder; extern const FFCodec ff_hevc_rkmpp_decoder; +extern const FFCodec ff_hevc_rkmpp_encoder; extern const FFCodec ff_hevc_v4l2m2m_decoder; extern const FFCodec ff_hnm4_video_decoder; extern const FFCodec ff_hq_hqa_decoder; diff --git a/libavcodec/rkmppenc.c b/libavcodec/rkmppenc.c new file mode 100644 index 0000000000..0b6d0d08c1 --- /dev/null +++ b/libavcodec/rkmppenc.c @@ -0,0 +1,462 @@ +/* + * RockChip MPP Video Encoder + * + * This file is part of FFmpeg. + * + * Copyright (c) 2025 Zhao Zhili + * + * 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 +#include + +#include +#include +#include + +#include "libavutil/avassert.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_drm.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "encode.h" +#include "hwconfig.h" + +#define RKMPP_TIME_BASE AV_TIME_BASE_Q + +typedef struct RKMPPEncoderContext { + AVClass *av_class; + + MppCtx enc; + MppApi *mpi; + MppEncCfg cfg; + AVFrame *frame; + MppEncRcMode rc_mode; + bool eof_sent; +} RKMPPEncoderContext; + +static const enum AVPixelFormat rkmpp_pix_fmts[] = { + AV_PIX_FMT_DRM_PRIME, + AV_PIX_FMT_NONE +}; + +static av_cold int rkmpp_close_encoder(AVCodecContext *avctx) +{ + RKMPPEncoderContext *ctx = avctx->priv_data; + + if (ctx->enc) { + ctx->mpi->reset(ctx->enc); + mpp_destroy(ctx->enc); + ctx->enc = NULL; + } + + if (ctx->cfg) { + mpp_enc_cfg_deinit(ctx->cfg); + ctx->cfg = NULL; + } + + av_frame_free(&ctx->frame); + + return 0; +} + +static int rkmpp_export_extradata(AVCodecContext *avctx) +{ + RKMPPEncoderContext *ctx = avctx->priv_data; + MppEncHeaderMode mode = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? + MPP_ENC_HEADER_MODE_DEFAULT : MPP_ENC_HEADER_MODE_EACH_IDR; + + int ret = ctx->mpi->control(ctx->enc, MPP_ENC_SET_HEADER_MODE, &mode); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to set header mode: %d\n", ret); + return AVERROR_EXTERNAL; + } + + if (!(avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) + return 0; + + size_t size = 4096; + avctx->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) + return AVERROR(ENOMEM); + + MppPacket packet = NULL; + mpp_packet_init(&packet, avctx->extradata, size); + mpp_packet_set_length(packet, 0); + ret = ctx->mpi->control(ctx->enc, MPP_ENC_GET_HDR_SYNC, packet); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to get header: %d\n", ret); + ret = AVERROR_EXTERNAL; + goto out; + } + + avctx->extradata_size = mpp_packet_get_length(packet); + if (avctx->extradata_size == 0 || avctx->extradata_size > size) { + av_log(avctx, AV_LOG_ERROR, "Invalid extradata size %d\n", + avctx->extradata_size); + ret = AVERROR_EXTERNAL; + goto out; + } + + ret = 0; +out: + mpp_packet_deinit(&packet); + + return ret; +} + +static av_cold int rkmpp_init_encoder(AVCodecContext *avctx) +{ + RKMPPEncoderContext *ctx = avctx->priv_data; + int ret; + + MppCodingType codectype; + switch (avctx->codec_id) { + case AV_CODEC_ID_H264: + codectype = MPP_VIDEO_CodingAVC; + break; + case AV_CODEC_ID_HEVC: + codectype = MPP_VIDEO_CodingHEVC; + break; + default: + av_unreachable("Invalid codec_id"); + } + + ret = mpp_check_support_format(MPP_CTX_ENC, codectype); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "The device doesn't support %s\n", + avcodec_get_name(avctx->codec_id)); + return AVERROR_EXTERNAL; + } + + ret = mpp_create(&ctx->enc, &ctx->mpi); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to create MPP context (%d).\n", ret); + return AVERROR_EXTERNAL; + } + + ret = mpp_init(ctx->enc, MPP_CTX_ENC, codectype); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize MPP context (%d).\n", ret); + return AVERROR_EXTERNAL; + } + + ret = mpp_enc_cfg_init(&ctx->cfg); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize config (%d).\n", ret); + return AVERROR_EXTERNAL; + } + + MppEncCfg cfg = ctx->cfg; + ret = ctx->mpi->control(ctx->enc, MPP_ENC_GET_CFG, cfg); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to get encoder config: %d\n", ret); + return AVERROR_EXTERNAL; + } + + mpp_enc_cfg_set_s32(cfg, "prep:width", avctx->width); + mpp_enc_cfg_set_s32(cfg, "prep:height", avctx->height); + mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", FFALIGN(avctx->width, 16)); + mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", FFALIGN(avctx->height, 16)); + mpp_enc_cfg_set_s32(cfg, "prep:format", MPP_FMT_YUV420SP); + + if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED) + mpp_enc_cfg_set_s32(cfg, "prep:colorspace", avctx->colorspace); + if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED) + mpp_enc_cfg_set_s32(cfg, "prep:colorprim", avctx->color_primaries); + if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED) + mpp_enc_cfg_set_s32(cfg, "prep:colortrc", avctx->color_trc); + static_assert((int)AVCOL_RANGE_MPEG == (int)MPP_FRAME_RANGE_MPEG && + (int)AVCOL_RANGE_JPEG == (int)MPP_FRAME_RANGE_JPEG && + (int)AVCOL_RANGE_UNSPECIFIED == (int) MPP_FRAME_RANGE_UNSPECIFIED, + "MppFrameColorRange not equal to AVColorRange"); + mpp_enc_cfg_set_s32(cfg, "prep:colorrange", avctx->color_range); + + /* These two options sound like variable frame rate from the doc, but they + * are not. When they are false, bitrate control is based on frame numbers + * and framerate. But when they are true, bitrate control is based on wall + * clock time, not based on frame timestamps, which makes these options + * almost useless, except in certain rare realtime case. + */ + mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", 0); + mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", 0); + if (avctx->framerate.den > 0 && avctx->framerate.num > 0) { + mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", avctx->framerate.num); + mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denom", avctx->framerate.den); + mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", avctx->framerate.num); + mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denom", avctx->framerate.den); + } + + if (avctx->gop_size >= 0) + mpp_enc_cfg_set_s32(cfg, "rc:gop", avctx->gop_size); + + mpp_enc_cfg_set_u32(cfg, "rc:mode", ctx->rc_mode); + if (avctx->bit_rate > 0) { + mpp_enc_cfg_set_s32(cfg, "rc:bps_target", avctx->bit_rate); + if (avctx->rc_buffer_size >= avctx->bit_rate) { + int seconds = round((double)avctx->rc_buffer_size / avctx->bit_rate); + // 60 is the upper bound from the doc + seconds = FFMIN(seconds, 60); + mpp_enc_cfg_set_s32(cfg, "rc:stats_time", seconds); + } + } + if (avctx->rc_max_rate > 0) + mpp_enc_cfg_set_s32(cfg, "rc:bps_max", avctx->rc_max_rate); + if (avctx->rc_min_rate > 0) + mpp_enc_cfg_set_s32(cfg, "rc:bps_min", avctx->rc_min_rate); + + mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED); + + ret = ctx->mpi->control(ctx->enc, MPP_ENC_SET_CFG, cfg); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to set config: %d\n", ret); + return AVERROR_EXTERNAL; + } + + ctx->frame = av_frame_alloc(); + if (!ctx->frame) + return AVERROR(ENOMEM); + + ret = rkmpp_export_extradata(avctx); + if (ret < 0) + return ret; + + return 0; +} + +static int rkmpp_output_pkt(AVCodecContext *avctx, AVPacket *pkt, MppPacket packet) +{ + if (mpp_packet_get_eos(packet)) { + av_log(avctx, AV_LOG_INFO, "Receive eos packet\n"); + return AVERROR_EOF; + } + + size_t size = mpp_packet_get_length(packet); + void *data = mpp_packet_get_pos(packet); + + if (!size || !data) { + av_log(avctx, AV_LOG_ERROR, "Encoder return empty packet\n"); + return AVERROR_EXTERNAL; + } + + int ret = ff_get_encode_buffer(avctx, pkt, size, 0); + if (ret < 0) + return ret; + memcpy(pkt->data, data, size); + + int64_t pts = mpp_packet_get_pts(packet); + int64_t dts = mpp_packet_get_dts(packet); + + pkt->pts = av_rescale_q(pts, RKMPP_TIME_BASE, avctx->time_base); + /* dts is always zero currently, since rkmpp copy dts from MppFrame to + * MppPacket, and we don't set dts for MppFrame (it make no sense for + * encoder). rkmpp encoder doesn't support reordering, so we can just + * set dts as pts. + * + * TODO: remove this workaround once rkmpp fixed the issue. + */ + if (dts) + pkt->dts = av_rescale_q(dts, RKMPP_TIME_BASE, avctx->time_base); + else + pkt->dts = pkt->pts; + + MppMeta meta = mpp_packet_get_meta(packet); + if (!meta) { + av_log(avctx, AV_LOG_ERROR, "Failed to get meta from mpp packet\n"); + return AVERROR_EXTERNAL; + } + + int key_frame = 0; + ret = mpp_meta_get_s32(meta, KEY_OUTPUT_INTRA, &key_frame); + if (ret != MPP_OK) { + av_log(avctx, AV_LOG_ERROR, "Failed to get key frame info\n"); + return AVERROR_EXTERNAL; + } + + if (key_frame) + pkt->flags |= AV_PKT_FLAG_KEY; + + return 0; +} + +static int rkmpp_send_frame(AVCodecContext *avctx) +{ + RKMPPEncoderContext *ctx = avctx->priv_data; + MppFrame frame = NULL; + int ret = 0; + + ret = mpp_frame_init(&frame); + if (ret != MPP_OK) { + ret = AVERROR_EXTERNAL; + goto out; + } + + if (ctx->frame->buf[0]) { + AVBufferRef *hw_ref = ctx->frame->hw_frames_ctx; + if (!hw_ref) { + ret = AVERROR(EINVAL); + goto out; + } + AVHWFramesContext *hwframes = (AVHWFramesContext *)hw_ref->data; + if (hwframes->sw_format != AV_PIX_FMT_NV12) { + ret = AVERROR(EINVAL); + goto out; + } + + mpp_frame_set_width(frame, ctx->frame->width); + mpp_frame_set_height(frame, ctx->frame->height); + mpp_frame_set_pts(frame, av_rescale_q(ctx->frame->pts, avctx->time_base, RKMPP_TIME_BASE)); + + const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)ctx->frame->data[0]; + const AVDRMLayerDescriptor *layer = &desc->layers[0]; + + int stride = layer->planes[0].pitch; + int vertical = layer->planes[1].offset / stride; + mpp_frame_set_hor_stride(frame, stride); + mpp_frame_set_ver_stride(frame, vertical); + mpp_frame_set_fmt(frame, MPP_FMT_YUV420SP); + + MppBuffer buffer = {0}; + MppBufferInfo info = { + .type = MPP_BUFFER_TYPE_DRM, + .size = desc->objects[0].size, + .fd = desc->objects[0].fd, + }; + ret = mpp_buffer_import(&buffer, &info); + if (ret != MPP_OK) { + ret = AVERROR_EXTERNAL; + goto out; + } + mpp_frame_set_buffer(frame, buffer); + mpp_buffer_put(buffer); + } else { + mpp_frame_set_buffer(frame, NULL); + mpp_frame_set_eos(frame, 1); + } + + ret = ctx->mpi->encode_put_frame(ctx->enc, frame); + if (ret != MPP_OK) + ret = AVERROR_EXTERNAL; + +out: + if (frame) + mpp_frame_deinit(&frame); + + return ret; +} + +static int rkmpp_receive(AVCodecContext *avctx, AVPacket *pkt) +{ + RKMPPEncoderContext *ctx = avctx->priv_data; + + while (true) { + MppPacket packet = NULL; + int ret = ctx->mpi->encode_get_packet(ctx->enc, &packet); + + if (ret == MPP_OK && packet) { + ret = rkmpp_output_pkt(avctx, pkt, packet); + mpp_packet_deinit(&packet); + return ret; + } + + if (ctx->eof_sent) + continue; + + if (!ctx->frame->buf[0]) { + ret = ff_encode_get_frame(avctx, ctx->frame); + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } + + ret = rkmpp_send_frame(avctx); + if (ret < 0) + return ret; + + if (!ctx->frame->buf[0]) + ctx->eof_sent = true; + else + av_frame_unref(ctx->frame); + } +} + +static av_cold void rkmpp_flush(AVCodecContext *avctx) +{ + RKMPPEncoderContext *ctx = avctx->priv_data; + ctx->mpi->reset(ctx->enc); + ctx->eof_sent = true; +} + +static const AVCodecHWConfigInternal *const rkmpp_hw_configs[] = { + HW_CONFIG_ENCODER_FRAMES(DRM_PRIME, DRM), + NULL +}; + +#define OFFSET(x) offsetof(RKMPPEncoderContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption rkmpp_options[] = { + {"rc", "rate-control mode", + OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = MPP_ENC_RC_MODE_VBR }, MPP_ENC_RC_MODE_VBR, INT_MAX, VE, .unit = "rc"}, + {"vbr", "Variable bitrate mode", + 0, AV_OPT_TYPE_CONST, {.i64 = MPP_ENC_RC_MODE_VBR}, 0, 0, VE, .unit = "rc"}, + {"cbr", "Constant bitrate mode", + 0, AV_OPT_TYPE_CONST, {.i64 = MPP_ENC_RC_MODE_CBR}, 0, 0, VE, .unit = "rc"}, + {"avbr", "Adaptive bit rate mode", + 0, AV_OPT_TYPE_CONST, {.i64 = MPP_ENC_RC_MODE_AVBR}, 0, 0, VE, .unit = "rc"}, + {NULL}, +}; + +#define RKMPP_ENC_CLASS(NAME) \ + static const AVClass rkmpp_##NAME##_enc_class = { \ + .class_name = "rkmpp_" #NAME "_enc", \ + .version = LIBAVUTIL_VERSION_INT, \ + .option = rkmpp_options, \ + }; + +#define RKMPP_ENC(NAME, ID) \ + RKMPP_ENC_CLASS(NAME) \ + const FFCodec ff_##NAME##_rkmpp_encoder = { \ + .p.name = #NAME "_rkmpp", \ + CODEC_LONG_NAME(#NAME " (rkmpp)"), \ + .p.type = AVMEDIA_TYPE_VIDEO, \ + .p.id = ID, \ + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | \ + AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_ENCODER_FLUSH, \ + .priv_data_size = sizeof(RKMPPEncoderContext), \ + CODEC_PIXFMTS_ARRAY(rkmpp_pix_fmts), \ + .color_ranges = AVCOL_RANGE_MPEG | AVCOL_RANGE_JPEG, \ + .init = rkmpp_init_encoder, \ + FF_CODEC_RECEIVE_PACKET_CB(rkmpp_receive), \ + .close = rkmpp_close_encoder, \ + .flush = rkmpp_flush, \ + .p.priv_class = &rkmpp_##NAME##_enc_class, \ + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, \ + .p.wrapper_name = "rkmpp", \ + .hw_configs = rkmpp_hw_configs, \ + }; + +#if CONFIG_H264_RKMPP_ENCODER +RKMPP_ENC(h264, AV_CODEC_ID_H264) +#endif + +#if CONFIG_HEVC_RKMPP_ENCODER +RKMPP_ENC(hevc, AV_CODEC_ID_HEVC) +#endif -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org