From e52b71adc27688eb7b755c90681274e0c5bcdb91 Mon Sep 17 00:00:00 2001
|
From: Hertz Wang <wangh@rock-chips.com>
|
Date: Wed, 31 Oct 2018 16:53:23 +0800
|
Subject: [PATCH 04/11] add h264 rkmppenc
|
|
TODO: separate send_frame and receive packet if mpp api support well
|
|
Change-Id: Ib3586d5488551ca421cb81488d1b85602a862c14
|
Signed-off-by: Hertz Wang <wangh@rock-chips.com>
|
---
|
configure | 1 +
|
libavcodec/Makefile | 1 +
|
libavcodec/allcodecs.c | 1 +
|
libavcodec/rkmppenc.c | 695 +++++++++++++++++++++++++++++++++++++++++++++++++
|
4 files changed, 698 insertions(+)
|
create mode 100644 libavcodec/rkmppenc.c
|
|
diff --git a/configure b/configure
|
index a9644e2..66f2497 100755
|
--- a/configure
|
+++ b/configure
|
@@ -2956,6 +2956,7 @@ h264_qsv_decoder_select="h264_mp4toannexb_bsf h264_parser qsvdec"
|
h264_qsv_encoder_select="qsvenc"
|
h264_rkmpp_decoder_deps="rkmpp"
|
h264_rkmpp_decoder_select="h264_mp4toannexb_bsf"
|
+h264_rkmpp_encoder_deps="rkmpp"
|
h264_vaapi_encoder_select="cbs_h264 vaapi_encode"
|
h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m"
|
h264_v4l2m2m_decoder_select="h264_mp4toannexb_bsf"
|
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
|
index 3e41497..cb17848 100644
|
--- a/libavcodec/Makefile
|
+++ b/libavcodec/Makefile
|
@@ -356,6 +356,7 @@ OBJS-$(CONFIG_H264_OMX_ENCODER) += omx.o
|
OBJS-$(CONFIG_H264_QSV_DECODER) += qsvdec_h2645.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
|
OBJS-$(CONFIG_H264_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o
|
OBJS-$(CONFIG_H264_V4L2M2M_DECODER) += v4l2_m2m_dec.o
|
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
|
index a8fa898..302272d 100644
|
--- a/libavcodec/allcodecs.c
|
+++ b/libavcodec/allcodecs.c
|
@@ -141,6 +141,7 @@ extern AVCodec ff_h264_mediacodec_decoder;
|
extern AVCodec ff_h264_mmal_decoder;
|
extern AVCodec ff_h264_qsv_decoder;
|
extern AVCodec ff_h264_rkmpp_decoder;
|
+extern AVCodec ff_h264_rkmpp_encoder;
|
extern AVCodec ff_hap_encoder;
|
extern AVCodec ff_hap_decoder;
|
extern AVCodec ff_hevc_decoder;
|
diff --git a/libavcodec/rkmppenc.c b/libavcodec/rkmppenc.c
|
new file mode 100644
|
index 0000000..0ebb766
|
--- /dev/null
|
+++ b/libavcodec/rkmppenc.c
|
@@ -0,0 +1,695 @@
|
+/*
|
+ * RockChip MPP Video Encoder
|
+ * Copyright (c) 2018 hertz.wang@rock-chips.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 <drm_fourcc.h>
|
+#include <pthread.h>
|
+#include <rockchip/mpp_buffer.h>
|
+#include <rockchip/rk_mpi.h>
|
+#include <time.h>
|
+#include <unistd.h>
|
+
|
+#include "avcodec.h"
|
+#include "hwaccel.h"
|
+#include "internal.h"
|
+#include "libavutil/avassert.h"
|
+#include "libavutil/buffer.h"
|
+#include "libavutil/frame.h"
|
+#include "libavutil/hwcontext.h"
|
+#include "libavutil/hwcontext_drm.h"
|
+#include "libavutil/log.h"
|
+
|
+// copy from mpp/base/inc/mpp_packet_impl.h
|
+#define MPP_PACKET_FLAG_INTRA (0x00000008)
|
+
|
+#define SEND_FRAME_TIMEOUT 100
|
+#define RECEIVE_PACKET_TIMEOUT 100
|
+
|
+typedef struct {
|
+ MppCtx ctx;
|
+ MppApi *mpi;
|
+
|
+ char eos_reached;
|
+} RKMPPEncoder;
|
+
|
+typedef struct {
|
+ AVClass *av_class;
|
+ AVBufferRef *encoder_ref;
|
+} RKMPPEncodeContext;
|
+
|
+typedef struct {
|
+ MppPacket packet;
|
+ AVBufferRef *encoder_ref;
|
+} RKMPPPacketContext;
|
+
|
+static MppCodingType rkmpp_get_codingtype(AVCodecContext *avctx)
|
+{
|
+ switch (avctx->codec_id) {
|
+ case AV_CODEC_ID_H264: return MPP_VIDEO_CodingAVC;
|
+ default: return MPP_VIDEO_CodingUnused;
|
+ }
|
+}
|
+
|
+static MppFrameFormat rkmpp_get_mppformat(enum AVPixelFormat avformat)
|
+{
|
+ switch (avformat) {
|
+ case AV_PIX_FMT_NV12: return MPP_FMT_YUV420SP;
|
+ case AV_PIX_FMT_YUV420P: return MPP_FMT_YUV420P;
|
+ case AV_PIX_FMT_YUYV422: return MPP_FMT_YUV422_YUYV;
|
+ case AV_PIX_FMT_UYVY422: return MPP_FMT_YUV422_UYVY;
|
+#ifdef DRM_FORMAT_NV12_10
|
+ case AV_PIX_FMT_P010: return MPP_FMT_YUV420SP_10BIT;
|
+#endif
|
+ default: return -1;
|
+ }
|
+}
|
+
|
+static int rkmpp_close_encoder(AVCodecContext *avctx)
|
+{
|
+ RKMPPEncodeContext *rk_context = avctx->priv_data;
|
+ av_buffer_unref(&rk_context->encoder_ref);
|
+ return 0;
|
+}
|
+
|
+static void rkmpp_release_encoder(void *opaque, uint8_t *data)
|
+{
|
+ RKMPPEncoder *encoder = (RKMPPEncoder *)data;
|
+
|
+ if (encoder->mpi) {
|
+ encoder->mpi->reset(encoder->ctx);
|
+ mpp_destroy(encoder->ctx);
|
+ encoder->ctx = NULL;
|
+ }
|
+
|
+ av_free(encoder);
|
+}
|
+
|
+static int rkmpp_preg_config(AVCodecContext *avctx, RKMPPEncoder *encoder,
|
+ MppEncPrepCfg *prep_cfg)
|
+{
|
+ int ret;
|
+
|
+ memset(prep_cfg, 0, sizeof(*prep_cfg));
|
+ prep_cfg->change = MPP_ENC_PREP_CFG_CHANGE_INPUT |
|
+ MPP_ENC_PREP_CFG_CHANGE_ROTATION |
|
+ MPP_ENC_PREP_CFG_CHANGE_FORMAT;
|
+ prep_cfg->width = avctx->width;
|
+ prep_cfg->height = avctx->height;
|
+ prep_cfg->hor_stride = avctx->width;
|
+ prep_cfg->ver_stride = avctx->height;
|
+ prep_cfg->format = rkmpp_get_mppformat(avctx->sw_pix_fmt);
|
+ prep_cfg->rotation = MPP_ENC_ROT_0;
|
+
|
+ ret = encoder->mpi->control(encoder->ctx, MPP_ENC_SET_PREP_CFG, prep_cfg);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to set prep cfg on MPI (code = %d).\n", ret);
|
+ return AVERROR_UNKNOWN;
|
+ }
|
+
|
+ return 0;
|
+}
|
+
|
+static int rkmpp_rc_config(AVCodecContext *avctx, RKMPPEncoder *encoder,
|
+ MppEncRcCfg *rc_cfg)
|
+{
|
+ int ret;
|
+ int fps;
|
+
|
+ memset(rc_cfg, 0, sizeof(*rc_cfg));
|
+ rc_cfg->change = MPP_ENC_RC_CFG_CHANGE_ALL;
|
+ // TODO: set by AVOption
|
+ rc_cfg->rc_mode = MPP_ENC_RC_MODE_CBR;
|
+ rc_cfg->quality = MPP_ENC_RC_QUALITY_MEDIUM;
|
+
|
+ if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_CBR) {
|
+ /* constant bitrate has very small bps range of 1/16 bps */
|
+ rc_cfg->bps_target = avctx->bit_rate;
|
+ rc_cfg->bps_max = avctx->bit_rate * 17 / 16;
|
+ rc_cfg->bps_min = avctx->bit_rate * 15 / 16;
|
+ } else if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_VBR) {
|
+ if (rc_cfg->quality == MPP_ENC_RC_QUALITY_CQP) {
|
+ /* constant QP does not have bps */
|
+ rc_cfg->bps_target = -1;
|
+ rc_cfg->bps_max = -1;
|
+ rc_cfg->bps_min = -1;
|
+ } else {
|
+ /* variable bitrate has large bps range */
|
+ rc_cfg->bps_target = avctx->bit_rate;
|
+ rc_cfg->bps_max = avctx->bit_rate * 17 / 16;
|
+ rc_cfg->bps_min = avctx->bit_rate * 1 / 16;
|
+ }
|
+ }
|
+
|
+ fps = avctx->time_base.den / avctx->time_base.num;
|
+ /* fix input / output frame rate */
|
+ rc_cfg->fps_in_flex = 0;
|
+ rc_cfg->fps_in_num = fps;
|
+ rc_cfg->fps_in_denorm = 1;
|
+ rc_cfg->fps_out_flex = 0;
|
+ rc_cfg->fps_out_num = fps;
|
+ rc_cfg->fps_out_denorm = 1;
|
+
|
+ rc_cfg->gop = avctx->gop_size;
|
+ rc_cfg->skip_cnt = 0;
|
+
|
+ ret = encoder->mpi->control(encoder->ctx, MPP_ENC_SET_RC_CFG, rc_cfg);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to set rc cfg on MPI (code = %d).\n", ret);
|
+ return AVERROR_UNKNOWN;
|
+ }
|
+
|
+ return 0;
|
+}
|
+
|
+static int rkmpp_codec_config(AVCodecContext *avctx, RKMPPEncoder *encoder,
|
+ MppCodingType codectype, MppEncRcCfg *rc_cfg,
|
+ MppEncCodecCfg *codec_cfg)
|
+{
|
+ int ret;
|
+
|
+ memset(codec_cfg, 0, sizeof(*codec_cfg));
|
+ codec_cfg->coding = codectype;
|
+ switch (codectype) {
|
+ case MPP_VIDEO_CodingAVC: {
|
+ int qp_min = avctx->qmin,
|
+ qp_max = avctx->qmax,
|
+ qp_step = avctx->max_qdiff;
|
+ int qp_init = 26;
|
+ codec_cfg->h264.change = MPP_ENC_H264_CFG_CHANGE_PROFILE |
|
+ MPP_ENC_H264_CFG_CHANGE_ENTROPY |
|
+ MPP_ENC_H264_CFG_CHANGE_TRANS_8x8 |
|
+ MPP_ENC_H264_CFG_CHANGE_QP_LIMIT;
|
+ /*
|
+ * H.264 profile_idc parameter
|
+ * Support: Baseline profile
|
+ * Main profile
|
+ * High profile
|
+ */
|
+ if (avctx->profile != FF_PROFILE_H264_BASELINE &&
|
+ avctx->profile != FF_PROFILE_H264_MAIN &&
|
+ avctx->profile != FF_PROFILE_H264_HIGH) {
|
+ av_log(avctx, AV_LOG_INFO, "Unsupport profile %d, force set to %d\n",
|
+ avctx->profile, FF_PROFILE_H264_HIGH);
|
+ avctx->profile = FF_PROFILE_H264_HIGH;
|
+ }
|
+ codec_cfg->h264.profile = avctx->profile;
|
+
|
+ /*
|
+ * H.264 level_idc parameter
|
+ * 10 / 11 / 12 / 13 - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
|
+ * 20 / 21 / 22 - cif@30fps / half-D1@@25fps / D1@12.5fps
|
+ * 30 / 31 / 32 - D1@25fps / 720p@30fps / 720p@60fps
|
+ * 40 / 41 / 42 - 1080p@30fps / 1080p@30fps / 1080p@60fps
|
+ * 50 / 51 / 52 - 4K@30fps
|
+ */
|
+ if (avctx->level == FF_LEVEL_UNKNOWN) {
|
+ av_log(avctx, AV_LOG_INFO, "Unsupport level %d, force set to %d\n",
|
+ avctx->level, 51);
|
+ avctx->level = 51;
|
+ }
|
+ codec_cfg->h264.level = avctx->level;
|
+ codec_cfg->h264.entropy_coding_mode =
|
+ (codec_cfg->h264.profile == FF_PROFILE_H264_HIGH) ? 1 : 0;
|
+ codec_cfg->h264.cabac_init_idc = 0;
|
+ codec_cfg->h264.transform8x8_mode = 1;
|
+
|
+ if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_CBR) {
|
+ /* constant bitrate do not limit qp range */
|
+ qp_max = 48;
|
+ qp_min = 4;
|
+ qp_step = 16;
|
+ qp_init = 0;
|
+ } else if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_VBR) {
|
+ if (rc_cfg->quality == MPP_ENC_RC_QUALITY_CQP) {
|
+ /* constant QP mode qp is fixed */
|
+ qp_max = qp_init;
|
+ qp_min = qp_init;
|
+ qp_step = 0;
|
+ } else {
|
+ /* variable bitrate has qp min limit */
|
+ qp_max = 40;
|
+ qp_min = 12;
|
+ qp_step = 8;
|
+ qp_init = 0;
|
+ }
|
+ }
|
+
|
+ codec_cfg->h264.qp_max = qp_max;
|
+ codec_cfg->h264.qp_min = qp_min;
|
+ codec_cfg->h264.qp_max_step = qp_step;
|
+ codec_cfg->h264.qp_init = qp_init;
|
+ } break;
|
+ case MPP_VIDEO_CodingMJPEG:
|
+ codec_cfg->jpeg.change = MPP_ENC_JPEG_CFG_CHANGE_QP;
|
+ codec_cfg->jpeg.quant = 10; // 1 ~ 10
|
+ break;
|
+ default:
|
+ av_log(avctx, AV_LOG_ERROR, "TODO encoder coding type %d\n", codectype);
|
+ return AVERROR_UNKNOWN;
|
+ }
|
+
|
+ ret = encoder->mpi->control(encoder->ctx, MPP_ENC_SET_CODEC_CFG, codec_cfg);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to set codec cfg on MPI (code = %d).\n", ret);
|
+ return AVERROR_UNKNOWN;
|
+ }
|
+
|
+ return 0;
|
+}
|
+
|
+static int rkmpp_init_encoder(AVCodecContext *avctx)
|
+{
|
+ int ret;
|
+ MppCodingType codectype;
|
+ RKMPPEncodeContext *rk_context;
|
+ RKMPPEncoder *encoder;
|
+ MppEncPrepCfg prep_cfg;
|
+ MppEncRcCfg rc_cfg;
|
+ MppEncCodecCfg codec_cfg;
|
+ RK_S64 paramS64;
|
+ MppEncSeiMode sei_mode;
|
+ MppPacket packet = NULL;
|
+
|
+ rk_context = avctx->priv_data;
|
+ rk_context->encoder_ref = NULL;
|
+ codectype = rkmpp_get_codingtype(avctx);
|
+ if (codectype == MPP_VIDEO_CodingUnused) {
|
+ av_log(avctx, AV_LOG_ERROR, "Unsupport codec type (%d).\n", avctx->codec_id);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ ret = mpp_check_support_format(MPP_CTX_ENC, codectype);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Codec type (%d) unsupported by MPP\n", avctx->codec_id);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ // create a encoder and a ref to it
|
+ encoder = av_mallocz(sizeof(RKMPPEncoder));
|
+ if (!encoder) {
|
+ ret = AVERROR(ENOMEM);
|
+ goto fail;
|
+ }
|
+
|
+ rk_context->encoder_ref =
|
+ av_buffer_create((uint8_t *)encoder, sizeof(*encoder),
|
+ rkmpp_release_encoder, NULL, AV_BUFFER_FLAG_READONLY);
|
+ if (!rk_context->encoder_ref) {
|
+ av_free(encoder);
|
+ ret = AVERROR(ENOMEM);
|
+ goto fail;
|
+ }
|
+
|
+ av_log(avctx, AV_LOG_DEBUG, "Initializing RKMPP encoder.\n");
|
+
|
+ // mpp init
|
+ ret = mpp_create(&encoder->ctx, &encoder->mpi);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to create MPP context (code = %d).\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ ret = mpp_init(encoder->ctx, MPP_CTX_ENC, codectype);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to initialize MPP context (code = %d).\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ // mpp setup
|
+ ret = rkmpp_preg_config(avctx, encoder, &prep_cfg);
|
+ if (ret)
|
+ goto fail;
|
+
|
+ ret = rkmpp_rc_config(avctx, encoder, &rc_cfg);
|
+ if (ret)
|
+ goto fail;
|
+
|
+ ret = rkmpp_codec_config(avctx, encoder, codectype, &rc_cfg, &codec_cfg);
|
+ if (ret)
|
+ goto fail;
|
+
|
+ sei_mode = MPP_ENC_SEI_MODE_ONE_FRAME;
|
+ ret = encoder->mpi->control(encoder->ctx, MPP_ENC_SET_SEI_CFG, &sei_mode);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to set sei cfg on MPI (code = %d).\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ // TODO: osd if hardware support
|
+
|
+ paramS64 = SEND_FRAME_TIMEOUT;
|
+ ret = encoder->mpi->control(encoder->ctx, MPP_SET_INPUT_TIMEOUT, ¶mS64);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to set input timeout on MPI (code = %d).\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ paramS64 = RECEIVE_PACKET_TIMEOUT;
|
+ ret = encoder->mpi->control(encoder->ctx, MPP_SET_OUTPUT_TIMEOUT, ¶mS64);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to set output timeout on MPI (code = %d).\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ ret = encoder->mpi->control(encoder->ctx, MPP_ENC_GET_EXTRA_INFO, &packet);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to get extra info on MPI (code = %d).\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ if (packet) {
|
+ /* get and write sps/pps for H.264 */
|
+ void *ptr = mpp_packet_get_pos(packet);
|
+ size_t len = mpp_packet_get_length(packet);
|
+
|
+ if (avctx->extradata != NULL && avctx->extradata_size != len) {
|
+ av_free(avctx->extradata);
|
+ avctx->extradata = NULL;
|
+ }
|
+ if (!avctx->extradata)
|
+ avctx->extradata = av_malloc(len);
|
+ if (avctx->extradata == NULL) {
|
+ ret = AVERROR(ENOMEM);
|
+ goto fail;
|
+ }
|
+ avctx->extradata_size = len;
|
+ memcpy(avctx->extradata, ptr, len);
|
+
|
+ packet = NULL;
|
+ }
|
+
|
+ av_log(avctx, AV_LOG_DEBUG, "RKMPP encoder initialized successfully.\n");
|
+
|
+ return 0;
|
+
|
+fail:
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to initialize RKMPP encoder.\n");
|
+ rkmpp_close_encoder(avctx);
|
+ return ret;
|
+}
|
+
|
+static int rkmpp_queue_frame(AVCodecContext *avctx, RKMPPEncoder *encoder,
|
+ const AVFrame *avframe, MppFrame *out_frame)
|
+{
|
+ int ret;
|
+ MppFrameFormat mppformat;
|
+ MppCtx ctx;
|
+ MppApi *mpi;
|
+ MppBuffer buffer = NULL;
|
+ MppBufferInfo info;
|
+ MppFrame frame = NULL;
|
+ MppTask task = NULL;
|
+
|
+ // check format
|
+ if (avframe) {
|
+ enum AVPixelFormat swformat;
|
+ if(avframe->format != AV_PIX_FMT_DRM_PRIME) {
|
+ av_log(avctx, AV_LOG_ERROR, "RKMPPEncoder only support fmt DRM\n");
|
+ return AVERROR(EINVAL);
|
+ }
|
+ swformat = ((AVHWFramesContext*)avframe->hw_frames_ctx->data)->sw_format;
|
+ mppformat = rkmpp_get_mppformat(swformat);
|
+ if (mppformat < 0) {
|
+ av_log(avctx, AV_LOG_ERROR, "Unsupport av format %d\n", swformat);
|
+ return AVERROR(EINVAL);
|
+ }
|
+ }
|
+
|
+ ret = mpp_frame_init(&frame);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed init mpp frame on encoder (code = %d)\n", ret);
|
+ return AVERROR_UNKNOWN;
|
+ }
|
+ mpp_frame_set_eos(frame, encoder->eos_reached);
|
+
|
+ if (avframe) {
|
+ AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)avframe->data[0];
|
+ AVDRMLayerDescriptor *layer = &desc->layers[0];
|
+
|
+ mpp_frame_set_pts(frame, avframe->pts);
|
+ mpp_frame_set_dts(frame, avframe->pkt_dts);
|
+ mpp_frame_set_width(frame, avframe->width);
|
+ mpp_frame_set_height(frame, avframe->height);
|
+ if (mppformat == MPP_FMT_YUV422_YUYV || mppformat == MPP_FMT_YUV422_UYVY) {
|
+ mpp_frame_set_hor_stride(frame, 2 * layer->planes[0].pitch/* /bpp */);
|
+ } else {
|
+ // nv12 or yuv420p
|
+ mpp_frame_set_hor_stride(frame, layer->planes[0].pitch/* /bpp */);
|
+ }
|
+ if (layer->nb_planes > 1)
|
+ mpp_frame_set_ver_stride(frame,
|
+ layer->planes[1].offset / layer->planes[0].pitch);
|
+ else
|
+ mpp_frame_set_ver_stride(frame, avframe->height);
|
+ mpp_frame_set_fmt(frame, mppformat);
|
+
|
+ memset(&info, 0, sizeof(info));
|
+ info.type = MPP_BUFFER_TYPE_ION;
|
+ info.size = desc->objects[0].size;
|
+ info.fd = desc->objects[0].fd;
|
+ info.ptr = desc->objects[0].ptr;
|
+ ret = mpp_buffer_import(&buffer, &info);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to import buffer\n");
|
+ ret = AVERROR(EINVAL);
|
+ goto out;
|
+ }
|
+ mpp_frame_set_buffer(frame, buffer);
|
+ }
|
+
|
+ ctx = encoder->ctx;
|
+ mpi = encoder->mpi;
|
+ ret = mpi->poll(ctx, MPP_PORT_INPUT, MPP_POLL_BLOCK);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to poll task input (ret = %d)\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto out;
|
+ }
|
+
|
+ ret = mpi->dequeue(ctx, MPP_PORT_INPUT, &task);
|
+ if (ret != MPP_OK || NULL == task) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to dequeue task input (ret = %d)\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto out;
|
+ }
|
+
|
+ mpp_task_meta_set_frame (task, KEY_INPUT_FRAME, frame);
|
+ ret = mpi->enqueue(ctx, MPP_PORT_INPUT, task);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to enqueue task input (ret = %d)\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto out;
|
+ }
|
+ *out_frame = frame;
|
+ frame = NULL;
|
+
|
+out:
|
+ if (buffer)
|
+ mpp_buffer_put(buffer);
|
+ if (frame)
|
+ mpp_frame_deinit(&frame);
|
+ return 0;
|
+}
|
+
|
+static int rkmpp_send_frame(AVCodecContext *avctx, const AVFrame *frame,
|
+ MppFrame *mpp_frame)
|
+{
|
+ int ret;
|
+ RKMPPEncodeContext *rk_context = avctx->priv_data;
|
+ RKMPPEncoder *encoder = (RKMPPEncoder *)rk_context->encoder_ref->data;
|
+
|
+ if (!frame) {
|
+ av_log(avctx, AV_LOG_DEBUG, "End of stream.\n");
|
+ encoder->eos_reached = 1;
|
+ ret = rkmpp_queue_frame(avctx, encoder, NULL, mpp_frame);
|
+ if (ret)
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to send EOS to encoder (code = %d)\n", ret);
|
+ return ret;
|
+ }
|
+
|
+ ret = rkmpp_queue_frame(avctx, encoder, frame, mpp_frame);
|
+ if (ret && ret != AVERROR(EAGAIN))
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to send frame to encoder (code = %d)\n", ret);
|
+
|
+ return ret;
|
+}
|
+
|
+static void rkmpp_release_packet(void *opaque, uint8_t *data)
|
+{
|
+ RKMPPPacketContext *pkt_ctx = (RKMPPPacketContext *)opaque;
|
+
|
+ mpp_packet_deinit(&pkt_ctx->packet);
|
+ av_buffer_unref(&pkt_ctx->encoder_ref);
|
+ av_free(pkt_ctx);
|
+}
|
+
|
+static int rkmpp_receive_packet(AVCodecContext *avctx, AVPacket *pkt,
|
+ MppFrame *mpp_frame)
|
+{
|
+ int ret;
|
+ RKMPPEncodeContext *rk_context = avctx->priv_data;
|
+ RKMPPEncoder *encoder = (RKMPPEncoder *)rk_context->encoder_ref->data;
|
+ MppCtx ctx = encoder->ctx;
|
+ MppApi *mpi = encoder->mpi;
|
+ MppTask task = NULL;
|
+ MppPacket packet = NULL;
|
+
|
+ ret = mpi->poll(ctx, MPP_PORT_OUTPUT, MPP_POLL_BLOCK);
|
+ if (ret) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to poll task output (ret = %d)\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ ret = mpi->dequeue(ctx, MPP_PORT_OUTPUT, &task);
|
+ if (ret || NULL == task) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to dequeue task output (ret = %d)\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+
|
+ if (task) {
|
+ mpp_task_meta_get_packet(task, KEY_OUTPUT_PACKET, &packet);
|
+ ret = mpi->enqueue(ctx, MPP_PORT_OUTPUT, task);
|
+ if (ret != MPP_OK) {
|
+ av_log(avctx, AV_LOG_ERROR, "Failed to enqueue task output (ret = %d)\n", ret);
|
+ ret = AVERROR_UNKNOWN;
|
+ goto fail;
|
+ }
|
+ }
|
+
|
+ if (packet) {
|
+ RKMPPPacketContext *pkt_ctx;
|
+ RK_U32 flag;
|
+
|
+ if (mpp_packet_get_eos(packet)) {
|
+ av_log(avctx, AV_LOG_DEBUG, "Received a EOS packet.\n");
|
+ if (encoder->eos_reached) {
|
+ ret = AVERROR_EOF;
|
+ goto fail;
|
+ }
|
+ }
|
+ pkt_ctx = av_mallocz(sizeof(*pkt_ctx));
|
+ if (!pkt_ctx) {
|
+ ret = AVERROR(ENOMEM);
|
+ goto fail;
|
+ }
|
+ pkt_ctx->packet = packet;
|
+ pkt_ctx->encoder_ref = av_buffer_ref(rk_context->encoder_ref);
|
+
|
+ // TODO: outside need fd from mppbuffer?
|
+ pkt->data = mpp_packet_get_data(packet);
|
+ pkt->size = mpp_packet_get_length(packet);
|
+ pkt->buf = av_buffer_create((uint8_t*)pkt->data, pkt->size,
|
+ rkmpp_release_packet, pkt_ctx, AV_BUFFER_FLAG_READONLY);
|
+ if (!pkt->buf) {
|
+ av_buffer_unref(&pkt_ctx->encoder_ref);
|
+ av_free(pkt_ctx);
|
+ ret = AVERROR(ENOMEM);
|
+ goto fail;
|
+ }
|
+ pkt->pts = mpp_packet_get_pts(packet);
|
+ pkt->dts = mpp_packet_get_dts(packet);
|
+ if (pkt->pts <= 0)
|
+ pkt->pts = pkt->dts;
|
+ if (pkt->dts <= 0)
|
+ pkt->dts = pkt->pts;
|
+ flag = mpp_packet_get_flag(packet);
|
+ if (flag & MPP_PACKET_FLAG_INTRA)
|
+ pkt->flags |= AV_PKT_FLAG_KEY;
|
+
|
+ packet = NULL;
|
+ }
|
+
|
+fail:
|
+ if (packet)
|
+ mpp_packet_deinit(&packet);
|
+ if (*mpp_frame) {
|
+ mpp_frame_deinit(mpp_frame);
|
+ *mpp_frame = NULL;
|
+ }
|
+ return ret;
|
+}
|
+
|
+static int rkmpp_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
|
+ const AVFrame *frame, int *got_packet)
|
+{
|
+ int ret;
|
+ MppFrame mpp_frame = NULL;
|
+
|
+ ret = rkmpp_send_frame(avctx, frame, &mpp_frame);
|
+ if (ret)
|
+ return ret;
|
+
|
+ ret = rkmpp_receive_packet(avctx, pkt, &mpp_frame);
|
+ av_assert0(mpp_frame == NULL);
|
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
+ *got_packet = 0;
|
+ } else if (ret) {
|
+ return ret;
|
+ } else {
|
+ *got_packet = 1;
|
+ }
|
+
|
+ return 0;
|
+}
|
+
|
+static const AVCodecHWConfigInternal *rkmpp_hw_configs[] = {
|
+ HW_CONFIG_INTERNAL(DRM_PRIME),
|
+ NULL
|
+};
|
+
|
+#define RKMPP_ENC_CLASS(NAME) \
|
+ static const AVClass rkmpp_##NAME##_enc_class = { \
|
+ .class_name = "rkmpp_" #NAME "_enc", \
|
+ .version = LIBAVUTIL_VERSION_INT, \
|
+ };
|
+
|
+// TODO: .send_frame .receive_packet
|
+#define RKMPP_ENC(NAME, ID, BSFS) \
|
+ RKMPP_ENC_CLASS(NAME) \
|
+ AVCodec ff_##NAME##_rkmpp_encoder = { \
|
+ .name = #NAME "_rkmpp", \
|
+ .long_name = NULL_IF_CONFIG_SMALL(#NAME " (rkmpp)"), \
|
+ .type = AVMEDIA_TYPE_VIDEO, \
|
+ .id = ID, \
|
+ .init = rkmpp_init_encoder, \
|
+ .close = rkmpp_close_encoder, \
|
+ .encode2 = rkmpp_encode_frame, \
|
+ .priv_data_size = sizeof(RKMPPEncodeContext), \
|
+ .priv_class = &rkmpp_##NAME##_enc_class, \
|
+ .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, \
|
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, \
|
+ .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_DRM_PRIME, \
|
+ AV_PIX_FMT_NONE }, \
|
+ .hw_configs = rkmpp_hw_configs, \
|
+ .bsfs = BSFS, \
|
+ .wrapper_name = "rkmpp", \
|
+ };
|
+
|
+RKMPP_ENC(h264, AV_CODEC_ID_H264, NULL)
|
--
|
2.7.4
|