Files
mpp/test/mpi_enc_test.c
Johnson Ding 16ab9b0d4d feat[test]: Add PSNR info for video encoder
Note that the PSNR derived from SSE may not equals to PSNR from video
analyser. Because SSE returned from hardware is consist of both luma and
chroma samples. And SSE of chroma samples multiplied by some weight.
But the difference is within 1db, usually no more than 0.5db.

Change-Id: I294d473c656f39ce98dd0768cd6945ee38e89b14
Signed-off-by: Johnson Ding <johnson.ding@rock-chips.com>
2024-12-23 09:28:04 +08:00

1218 lines
39 KiB
C

/*
* Copyright 2015 Rockchip Electronics Co. LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(_WIN32)
#include "vld.h"
#endif
#define MODULE_TAG "mpi_enc_test"
#include <string.h>
#include <math.h>
#include "rk_mpi.h"
#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_time.h"
#include "mpp_debug.h"
#include "mpp_common.h"
#include "mpp_soc.h"
#include "utils.h"
#include "mpi_enc_utils.h"
#include "camera_source.h"
#include "mpp_enc_roi_utils.h"
#include "mpp_rc_api.h"
static RK_S32 aq_thd_smart[16] = {
1, 3, 3, 3, 3, 3, 5, 5,
8, 8, 8, 15, 15, 20, 25, 28
};
static RK_S32 aq_step_smart[16] = {
-8, -7, -6, -5, -4, -3, -2, -1,
0, 1, 2, 3, 4, 6, 8, 10
};
typedef struct {
// base flow context
MppCtx ctx;
MppApi *mpi;
RK_S32 chn;
// global flow control flag
RK_U32 frm_eos;
RK_U32 pkt_eos;
RK_U32 frm_pkt_cnt;
RK_S32 frame_num;
RK_S32 frame_count;
RK_U64 stream_size;
/* end of encoding flag when set quit the loop */
volatile RK_U32 loop_end;
// src and dst
FILE *fp_input;
FILE *fp_output;
FILE *fp_verify;
/* encoder config set */
MppEncCfg cfg;
MppEncPrepCfg prep_cfg;
MppEncRcCfg rc_cfg;
MppEncCodecCfg codec_cfg;
MppEncSliceSplit split_cfg;
MppEncOSDPltCfg osd_plt_cfg;
MppEncOSDPlt osd_plt;
MppEncOSDData osd_data;
RoiRegionCfg roi_region;
MppEncROICfg roi_cfg;
// input / output
MppBufferGroup buf_grp;
MppBuffer frm_buf;
MppBuffer pkt_buf;
MppBuffer md_info;
MppEncSeiMode sei_mode;
MppEncHeaderMode header_mode;
// paramter for resource malloc
RK_U32 width;
RK_U32 height;
RK_U32 hor_stride;
RK_U32 ver_stride;
MppFrameFormat fmt;
MppCodingType type;
RK_S32 loop_times;
CamSource *cam_ctx;
MppEncRoiCtx roi_ctx;
// resources
size_t header_size;
size_t frame_size;
size_t mdinfo_size;
/* NOTE: packet buffer may overflow */
size_t packet_size;
RK_U32 osd_enable;
RK_U32 osd_mode;
RK_U32 split_mode;
RK_U32 split_arg;
RK_U32 split_out;
RK_U32 user_data_enable;
RK_U32 roi_enable;
// rate control runtime parameter
RK_S32 fps_in_flex;
RK_S32 fps_in_den;
RK_S32 fps_in_num;
RK_S32 fps_out_flex;
RK_S32 fps_out_den;
RK_S32 fps_out_num;
RK_S32 bps;
RK_S32 bps_max;
RK_S32 bps_min;
RK_S32 rc_mode;
RK_S32 gop_mode;
RK_S32 gop_len;
RK_S32 vi_len;
RK_S32 scene_mode;
RK_S32 cu_qp_delta_depth;
RK_S32 anti_flicker_str;
RK_S32 atr_str_i;
RK_S32 atr_str_p;
RK_S32 atl_str;
RK_S32 sao_str_i;
RK_S32 sao_str_p;
RK_S64 first_frm;
RK_S64 first_pkt;
} MpiEncTestData;
/* For each instance thread return value */
typedef struct {
float frame_rate;
RK_U64 bit_rate;
RK_S64 elapsed_time;
RK_S32 frame_count;
RK_S64 stream_size;
RK_S64 delay;
} MpiEncMultiCtxRet;
typedef struct {
MpiEncTestArgs *cmd; // pointer to global command line info
const char *name;
RK_S32 chn;
pthread_t thd; // thread for for each instance
MpiEncTestData ctx; // context of encoder
MpiEncMultiCtxRet ret; // return of encoder
} MpiEncMultiCtxInfo;
static RK_S32 aq_thd[16] = {
0, 0, 0, 0,
3, 3, 5, 5,
8, 8, 8, 15,
15, 20, 25, 25
};
static RK_S32 aq_step_i_ipc[16] = {
-8, -7, -6, -5,
-4, -3, -2, -1,
0, 1, 2, 3,
5, 7, 7, 8,
};
static RK_S32 aq_step_p_ipc[16] = {
-8, -7, -6, -5,
-4, -2, -1, -1,
0, 2, 3, 4,
6, 8, 9, 10,
};
static RK_S32 get_mdinfo_size(MpiEncTestData *p, MppCodingType type)
{
RockchipSocType soc_type = mpp_get_soc_type();
RK_S32 md_size;
RK_U32 w = p->hor_stride, h = p->ver_stride;
if (soc_type == ROCKCHIP_SOC_RK3588) {
md_size = (MPP_ALIGN(w, 64) >> 6) * (MPP_ALIGN(h, 64) >> 6) * 32;
} else {
md_size = (MPP_VIDEO_CodingHEVC == type) ?
(MPP_ALIGN(w, 32) >> 5) * (MPP_ALIGN(h, 32) >> 5) * 16 :
(MPP_ALIGN(w, 64) >> 6) * (MPP_ALIGN(h, 16) >> 4) * 16;
}
return md_size;
}
MPP_RET test_ctx_init(MpiEncMultiCtxInfo *info)
{
MpiEncTestArgs *cmd = info->cmd;
MpiEncTestData *p = &info->ctx;
MPP_RET ret = MPP_OK;
// get paramter from cmd
p->width = cmd->width;
p->height = cmd->height;
p->hor_stride = (cmd->hor_stride) ? (cmd->hor_stride) :
(MPP_ALIGN(cmd->width, 16));
p->ver_stride = (cmd->ver_stride) ? (cmd->ver_stride) :
(MPP_ALIGN(cmd->height, 16));
p->fmt = cmd->format;
p->type = cmd->type;
p->bps = cmd->bps_target;
p->bps_min = cmd->bps_min;
p->bps_max = cmd->bps_max;
p->rc_mode = cmd->rc_mode;
p->frame_num = cmd->frame_num;
if (cmd->type == MPP_VIDEO_CodingMJPEG && p->frame_num == 0) {
mpp_log("jpege default encode only one frame. Use -n [num] for rc case\n");
p->frame_num = 1;
}
p->gop_mode = cmd->gop_mode;
p->gop_len = cmd->gop_len;
p->vi_len = cmd->vi_len;
p->fps_in_flex = cmd->fps_in_flex;
p->fps_in_den = cmd->fps_in_den;
p->fps_in_num = cmd->fps_in_num;
p->fps_out_flex = cmd->fps_out_flex;
p->fps_out_den = cmd->fps_out_den;
p->fps_out_num = cmd->fps_out_num;
p->scene_mode = cmd->scene_mode;
p->cu_qp_delta_depth = cmd->cu_qp_delta_depth;
p->anti_flicker_str = cmd->anti_flicker_str;
p->atr_str_i = cmd->atr_str_i;
p->atr_str_p = cmd->atr_str_p;
p->atl_str = cmd->atl_str;
p->sao_str_i = cmd->sao_str_i;
p->sao_str_p = cmd->sao_str_p;
p->mdinfo_size = get_mdinfo_size(p, cmd->type);
if (cmd->file_input) {
if (!strncmp(cmd->file_input, "/dev/video", 10)) {
mpp_log("open camera device");
p->cam_ctx = camera_source_init(cmd->file_input, 4, p->width, p->height, p->fmt);
mpp_log("new framecap ok");
if (p->cam_ctx == NULL)
mpp_err("open %s fail", cmd->file_input);
} else {
p->fp_input = fopen(cmd->file_input, "rb");
if (NULL == p->fp_input) {
mpp_err("failed to open input file %s\n", cmd->file_input);
mpp_err("create default yuv image for test\n");
}
}
}
if (cmd->file_output) {
p->fp_output = fopen(cmd->file_output, "w+b");
if (NULL == p->fp_output) {
mpp_err("failed to open output file %s\n", cmd->file_output);
ret = MPP_ERR_OPEN_FILE;
}
}
if (cmd->file_slt) {
p->fp_verify = fopen(cmd->file_slt, "wt");
if (!p->fp_verify)
mpp_err("failed to open verify file %s\n", cmd->file_slt);
}
// update resource parameter
switch (p->fmt & MPP_FRAME_FMT_MASK) {
case MPP_FMT_YUV420SP:
case MPP_FMT_YUV420P: {
p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 3 / 2;
} break;
case MPP_FMT_YUV422_YUYV :
case MPP_FMT_YUV422_YVYU :
case MPP_FMT_YUV422_UYVY :
case MPP_FMT_YUV422_VYUY :
case MPP_FMT_YUV422P :
case MPP_FMT_YUV422SP : {
p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 2;
} break;
case MPP_FMT_YUV400:
case MPP_FMT_RGB444 :
case MPP_FMT_BGR444 :
case MPP_FMT_RGB555 :
case MPP_FMT_BGR555 :
case MPP_FMT_RGB565 :
case MPP_FMT_BGR565 :
case MPP_FMT_RGB888 :
case MPP_FMT_BGR888 :
case MPP_FMT_RGB101010 :
case MPP_FMT_BGR101010 :
case MPP_FMT_ARGB8888 :
case MPP_FMT_ABGR8888 :
case MPP_FMT_BGRA8888 :
case MPP_FMT_RGBA8888 : {
p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64);
} break;
default: {
p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 4;
} break;
}
if (MPP_FRAME_FMT_IS_FBC(p->fmt)) {
if ((p->fmt & MPP_FRAME_FBC_MASK) == MPP_FRAME_FBC_AFBC_V1)
p->header_size = MPP_ALIGN(MPP_ALIGN(p->width, 16) * MPP_ALIGN(p->height, 16) / 16, SZ_4K);
else
p->header_size = MPP_ALIGN(p->width, 16) * MPP_ALIGN(p->height, 16) / 16;
} else {
p->header_size = 0;
}
return ret;
}
MPP_RET test_ctx_deinit(MpiEncTestData *p)
{
if (p) {
if (p->cam_ctx) {
camera_source_deinit(p->cam_ctx);
p->cam_ctx = NULL;
}
if (p->fp_input) {
fclose(p->fp_input);
p->fp_input = NULL;
}
if (p->fp_output) {
fclose(p->fp_output);
p->fp_output = NULL;
}
if (p->fp_verify) {
fclose(p->fp_verify);
p->fp_verify = NULL;
}
}
return MPP_OK;
}
MPP_RET test_mpp_enc_cfg_setup(MpiEncMultiCtxInfo *info)
{
MpiEncTestArgs *cmd = info->cmd;
MpiEncTestData *p = &info->ctx;
MppApi *mpi = p->mpi;
MppCtx ctx = p->ctx;
MppEncCfg cfg = p->cfg;
RK_U32 quiet = cmd->quiet;
MPP_RET ret;
RK_U32 rotation;
RK_U32 mirroring;
RK_U32 flip;
RK_U32 gop_mode = p->gop_mode;
MppEncRefCfg ref = NULL;
/* setup default parameter */
if (p->fps_in_den == 0)
p->fps_in_den = 1;
if (p->fps_in_num == 0)
p->fps_in_num = 30;
if (p->fps_out_den == 0)
p->fps_out_den = 1;
if (p->fps_out_num == 0)
p->fps_out_num = 30;
if (!p->bps)
p->bps = p->width * p->height / 8 * (p->fps_out_num / p->fps_out_den);
if (cmd->rc_mode == MPP_ENC_RC_MODE_SMTRC) {
mpp_enc_cfg_set_st(cfg, "hw:aq_thrd_i", aq_thd_smart);
mpp_enc_cfg_set_st(cfg, "hw:aq_thrd_p", aq_thd_smart);
mpp_enc_cfg_set_st(cfg, "hw:aq_step_i", aq_step_smart);
mpp_enc_cfg_set_st(cfg, "hw:aq_step_p", aq_step_smart);
} else {
mpp_enc_cfg_set_st(cfg, "hw:aq_thrd_i", aq_thd);
mpp_enc_cfg_set_st(cfg, "hw:aq_thrd_p", aq_thd);
mpp_enc_cfg_set_st(cfg, "hw:aq_step_i", aq_step_i_ipc);
mpp_enc_cfg_set_st(cfg, "hw:aq_step_p", aq_step_p_ipc);
}
mpp_enc_cfg_set_s32(cfg, "rc:max_reenc_times", 0);
mpp_enc_cfg_set_s32(cfg, "rc:cu_qp_delta_depth", p->cu_qp_delta_depth);
mpp_enc_cfg_set_s32(cfg, "tune:anti_flicker_str", p->anti_flicker_str);
mpp_enc_cfg_set_s32(cfg, "tune:atr_str_i", p->atr_str_i);
mpp_enc_cfg_set_s32(cfg, "tune:atr_str_p", p->atr_str_p);
mpp_enc_cfg_set_s32(cfg, "tune:atl_str", p->atl_str);
mpp_enc_cfg_set_s32(cfg, "tune:sao_str_i", p->sao_str_i);
mpp_enc_cfg_set_s32(cfg, "tune:sao_str_p", p->sao_str_p);
mpp_enc_cfg_set_s32(cfg, "tune:scene_mode", p->scene_mode);
mpp_enc_cfg_set_s32(cfg, "tune:deblur_en", cmd->deblur_en);
mpp_enc_cfg_set_s32(cfg, "tune:deblur_str", cmd->deblur_str);
mpp_enc_cfg_set_s32(cfg, "tune:rc_container", cmd->rc_container);
mpp_enc_cfg_set_s32(cfg, "tune:vmaf_opt", 0);
mpp_enc_cfg_set_s32(cfg, "hw:qbias_en", 1);
mpp_enc_cfg_set_s32(cfg, "hw:qbias_i", cmd->bias_i);
mpp_enc_cfg_set_s32(cfg, "hw:qbias_p", cmd->bias_p);
mpp_enc_cfg_set_s32(cfg, "hw:skip_bias_en", 0);
mpp_enc_cfg_set_s32(cfg, "hw:skip_bias", 4);
mpp_enc_cfg_set_s32(cfg, "hw:skip_sad", 8);
mpp_enc_cfg_set_s32(cfg, "prep:width", p->width);
mpp_enc_cfg_set_s32(cfg, "prep:height", p->height);
mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", p->hor_stride);
mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", p->ver_stride);
mpp_enc_cfg_set_s32(cfg, "prep:format", p->fmt);
mpp_enc_cfg_set_s32(cfg, "prep:range", MPP_FRAME_RANGE_JPEG);
mpp_enc_cfg_set_s32(cfg, "rc:mode", p->rc_mode);
mpp_enc_cfg_set_u32(cfg, "rc:max_reenc_times", 0);
mpp_enc_cfg_set_u32(cfg, "rc:super_mode", 0);
/* fix input / output frame rate */
mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", p->fps_in_flex);
mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", p->fps_in_num);
mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denom", p->fps_in_den);
mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", p->fps_out_flex);
mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", p->fps_out_num);
mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denom", p->fps_out_den);
/* drop frame or not when bitrate overflow */
mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED);
mpp_enc_cfg_set_u32(cfg, "rc:drop_thd", 20); /* 20% of max bps */
mpp_enc_cfg_set_u32(cfg, "rc:drop_gap", 1); /* Do not continuous drop frame */
/* setup bitrate for different rc_mode */
mpp_enc_cfg_set_s32(cfg, "rc:bps_target", p->bps);
switch (p->rc_mode) {
case MPP_ENC_RC_MODE_FIXQP : {
/* do not setup bitrate on FIXQP mode */
} break;
case MPP_ENC_RC_MODE_CBR : {
/* CBR mode has narrow bound */
mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16);
} break;
case MPP_ENC_RC_MODE_VBR :
case MPP_ENC_RC_MODE_AVBR : {
/* VBR mode has wide bound */
mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 1 / 16);
} break;
default : {
/* default use CBR mode */
mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16);
} break;
}
/* setup qp for different codec and rc_mode */
switch (p->type) {
case MPP_VIDEO_CodingAVC :
case MPP_VIDEO_CodingHEVC : {
switch (p->rc_mode) {
case MPP_ENC_RC_MODE_FIXQP : {
RK_S32 fix_qp = cmd->qp_init;
mpp_enc_cfg_set_s32(cfg, "rc:qp_init", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:qp_max", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:qp_min", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 0);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_min_i", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_max_i", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_min_p", fix_qp);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_max_p", fix_qp);
} break;
case MPP_ENC_RC_MODE_CBR :
case MPP_ENC_RC_MODE_VBR :
case MPP_ENC_RC_MODE_AVBR :
case MPP_ENC_RC_MODE_SMTRC : {
mpp_enc_cfg_set_s32(cfg, "rc:qp_init", cmd->qp_init ? cmd->qp_init : -1);
mpp_enc_cfg_set_s32(cfg, "rc:qp_max", cmd->qp_max ? cmd->qp_max : 51);
mpp_enc_cfg_set_s32(cfg, "rc:qp_min", cmd->qp_min ? cmd->qp_min : 10);
mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", cmd->qp_max_i ? cmd->qp_max_i : 51);
mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", cmd->qp_min_i ? cmd->qp_min_i : 10);
mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 2);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_min_i", cmd->fqp_min_i ? cmd->fqp_min_i : 10);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_max_i", cmd->fqp_max_i ? cmd->fqp_max_i : 45);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_min_p", cmd->fqp_min_p ? cmd->fqp_min_p : 10);
mpp_enc_cfg_set_s32(cfg, "rc:fqp_max_p", cmd->fqp_max_p ? cmd->fqp_max_p : 45);
} break;
default : {
mpp_err_f("unsupport encoder rc mode %d\n", p->rc_mode);
} break;
}
} break;
case MPP_VIDEO_CodingVP8 : {
/* vp8 only setup base qp range */
mpp_enc_cfg_set_s32(cfg, "rc:qp_init", cmd->qp_init ? cmd->qp_init : 40);
mpp_enc_cfg_set_s32(cfg, "rc:qp_max", cmd->qp_max ? cmd->qp_max : 127);
mpp_enc_cfg_set_s32(cfg, "rc:qp_min", cmd->qp_min ? cmd->qp_min : 0);
mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", cmd->qp_max_i ? cmd->qp_max_i : 127);
mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", cmd->qp_min_i ? cmd->qp_min_i : 0);
mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 6);
} break;
case MPP_VIDEO_CodingMJPEG : {
/* jpeg use special codec config to control qtable */
mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", cmd->qp_init ? cmd->qp_init : 80);
mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max", cmd->qp_max ? cmd->qp_max : 99);
mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min", cmd->qp_min ? cmd->qp_min : 1);
} break;
default : {
} break;
}
/* setup codec */
mpp_enc_cfg_set_s32(cfg, "codec:type", p->type);
switch (p->type) {
case MPP_VIDEO_CodingAVC : {
RK_U32 constraint_set;
/*
* H.264 profile_idc parameter
* 66 - Baseline profile
* 77 - Main profile
* 100 - High profile
*/
mpp_enc_cfg_set_s32(cfg, "h264:profile", 100);
/*
* 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
*/
mpp_enc_cfg_set_s32(cfg, "h264:level", 40);
mpp_enc_cfg_set_s32(cfg, "h264:cabac_en", 1);
mpp_enc_cfg_set_s32(cfg, "h264:cabac_idc", 0);
mpp_enc_cfg_set_s32(cfg, "h264:trans8x8", 1);
mpp_env_get_u32("constraint_set", &constraint_set, 0);
if (constraint_set & 0x3f0000)
mpp_enc_cfg_set_s32(cfg, "h264:constraint_set", constraint_set);
} break;
case MPP_VIDEO_CodingHEVC :
case MPP_VIDEO_CodingMJPEG :
case MPP_VIDEO_CodingVP8 : {
} break;
default : {
mpp_err_f("unsupport encoder coding type %d\n", p->type);
} break;
}
p->split_mode = 0;
p->split_arg = 0;
p->split_out = 0;
mpp_env_get_u32("split_mode", &p->split_mode, MPP_ENC_SPLIT_NONE);
mpp_env_get_u32("split_arg", &p->split_arg, 0);
mpp_env_get_u32("split_out", &p->split_out, 0);
if (p->split_mode) {
mpp_log_q(quiet, "%p split mode %d arg %d out %d\n", ctx,
p->split_mode, p->split_arg, p->split_out);
mpp_enc_cfg_set_s32(cfg, "split:mode", p->split_mode);
mpp_enc_cfg_set_s32(cfg, "split:arg", p->split_arg);
mpp_enc_cfg_set_s32(cfg, "split:out", p->split_out);
}
mpp_env_get_u32("mirroring", &mirroring, 0);
mpp_env_get_u32("rotation", &rotation, 0);
mpp_env_get_u32("flip", &flip, 0);
mpp_enc_cfg_set_s32(cfg, "prep:mirroring", mirroring);
mpp_enc_cfg_set_s32(cfg, "prep:rotation", rotation);
mpp_enc_cfg_set_s32(cfg, "prep:flip", flip);
// config gop_len and ref cfg
mpp_enc_cfg_set_s32(cfg, "rc:gop", p->gop_len ? p->gop_len : p->fps_out_num * 2);
mpp_env_get_u32("gop_mode", &gop_mode, gop_mode);
if (gop_mode) {
mpp_enc_ref_cfg_init(&ref);
if (p->gop_mode < 4)
mpi_enc_gen_ref_cfg(ref, gop_mode);
else
mpi_enc_gen_smart_gop_ref_cfg(ref, p->gop_len, p->vi_len);
mpp_enc_cfg_set_ptr(cfg, "rc:ref_cfg", ref);
}
ret = mpi->control(ctx, MPP_ENC_SET_CFG, cfg);
if (ret) {
mpp_err("mpi control enc set cfg failed ret %d\n", ret);
goto RET;
}
if (cmd->type == MPP_VIDEO_CodingAVC || cmd->type == MPP_VIDEO_CodingHEVC) {
RcApiBrief rc_api_brief;
rc_api_brief.type = cmd->type;
rc_api_brief.name = (cmd->rc_mode == MPP_ENC_RC_MODE_SMTRC) ?
"smart" : "default";
ret = mpi->control(ctx, MPP_ENC_SET_RC_API_CURRENT, &rc_api_brief);
if (ret) {
mpp_err("mpi control enc set rc api failed ret %d\n", ret);
goto RET;
}
}
if (ref)
mpp_enc_ref_cfg_deinit(&ref);
/* optional */
{
RK_U32 sei_mode;
mpp_env_get_u32("sei_mode", &sei_mode, MPP_ENC_SEI_MODE_DISABLE);
p->sei_mode = sei_mode;
ret = mpi->control(ctx, MPP_ENC_SET_SEI_CFG, &p->sei_mode);
if (ret) {
mpp_err("mpi control enc set sei cfg failed ret %d\n", ret);
goto RET;
}
}
if (p->type == MPP_VIDEO_CodingAVC || p->type == MPP_VIDEO_CodingHEVC) {
p->header_mode = MPP_ENC_HEADER_MODE_EACH_IDR;
ret = mpi->control(ctx, MPP_ENC_SET_HEADER_MODE, &p->header_mode);
if (ret) {
mpp_err("mpi control enc set header mode failed ret %d\n", ret);
goto RET;
}
}
/* setup test mode by env */
mpp_env_get_u32("osd_enable", &p->osd_enable, 0);
mpp_env_get_u32("osd_mode", &p->osd_mode, MPP_ENC_OSD_PLT_TYPE_DEFAULT);
mpp_env_get_u32("roi_enable", &p->roi_enable, 0);
mpp_env_get_u32("user_data_enable", &p->user_data_enable, 0);
if (p->roi_enable) {
mpp_enc_roi_init(&p->roi_ctx, p->width, p->height, p->type, 4);
mpp_assert(p->roi_ctx);
}
RET:
return ret;
}
MPP_RET test_mpp_run(MpiEncMultiCtxInfo *info)
{
MpiEncTestArgs *cmd = info->cmd;
MpiEncTestData *p = &info->ctx;
MppApi *mpi = p->mpi;
MppCtx ctx = p->ctx;
RK_U32 quiet = cmd->quiet;
RK_S32 chn = info->chn;
RK_U32 cap_num = 0;
DataCrc checkcrc;
MPP_RET ret = MPP_OK;
RK_FLOAT psnr_const = 0;
RK_U32 sse_unit_in_pixel = 0;
memset(&checkcrc, 0, sizeof(checkcrc));
checkcrc.sum = mpp_malloc(RK_ULONG, 512);
if (p->type == MPP_VIDEO_CodingAVC || p->type == MPP_VIDEO_CodingHEVC) {
MppPacket packet = NULL;
/*
* Can use packet with normal malloc buffer as input not pkt_buf.
* Please refer to vpu_api_legacy.cpp for normal buffer case.
* Using pkt_buf buffer here is just for simplifing demo.
*/
mpp_packet_init_with_buffer(&packet, p->pkt_buf);
/* NOTE: It is important to clear output packet length!! */
mpp_packet_set_length(packet, 0);
ret = mpi->control(ctx, MPP_ENC_GET_HDR_SYNC, packet);
if (ret) {
mpp_err("mpi control enc get extra info failed\n");
goto RET;
} else {
/* get and write sps/pps for H.264 */
void *ptr = mpp_packet_get_pos(packet);
size_t len = mpp_packet_get_length(packet);
if (p->fp_output)
fwrite(ptr, 1, len, p->fp_output);
}
mpp_packet_deinit(&packet);
sse_unit_in_pixel = p->type == MPP_VIDEO_CodingAVC ? 16 : 8;
psnr_const = (16 + log2(MPP_ALIGN(p->width, sse_unit_in_pixel) *
MPP_ALIGN(p->height, sse_unit_in_pixel)));
}
while (!p->pkt_eos) {
MppMeta meta = NULL;
MppFrame frame = NULL;
MppPacket packet = NULL;
void *buf = mpp_buffer_get_ptr(p->frm_buf);
RK_S32 cam_frm_idx = -1;
MppBuffer cam_buf = NULL;
RK_U32 eoi = 1;
if (p->fp_input) {
mpp_buffer_sync_begin(p->frm_buf);
ret = read_image(buf, p->fp_input, p->width, p->height,
p->hor_stride, p->ver_stride, p->fmt);
if (ret == MPP_NOK || feof(p->fp_input)) {
p->frm_eos = 1;
if (p->frame_num < 0) {
clearerr(p->fp_input);
rewind(p->fp_input);
p->frm_eos = 0;
mpp_log_q(quiet, "chn %d loop times %d\n", chn, ++p->loop_times);
continue;
}
mpp_log_q(quiet, "chn %d found last frame. feof %d\n", chn, feof(p->fp_input));
} else if (ret == MPP_ERR_VALUE)
goto RET;
mpp_buffer_sync_end(p->frm_buf);
} else {
if (p->cam_ctx == NULL) {
mpp_buffer_sync_begin(p->frm_buf);
ret = fill_image(buf, p->width, p->height, p->hor_stride,
p->ver_stride, p->fmt, p->frame_count);
if (ret)
goto RET;
mpp_buffer_sync_end(p->frm_buf);
} else {
cam_frm_idx = camera_source_get_frame(p->cam_ctx);
mpp_assert(cam_frm_idx >= 0);
/* skip unstable frames */
if (cap_num++ < 50) {
camera_source_put_frame(p->cam_ctx, cam_frm_idx);
continue;
}
cam_buf = camera_frame_to_buf(p->cam_ctx, cam_frm_idx);
mpp_assert(cam_buf);
}
}
ret = mpp_frame_init(&frame);
if (ret) {
mpp_err_f("mpp_frame_init failed\n");
goto RET;
}
mpp_frame_set_width(frame, p->width);
mpp_frame_set_height(frame, p->height);
mpp_frame_set_hor_stride(frame, p->hor_stride);
mpp_frame_set_ver_stride(frame, p->ver_stride);
mpp_frame_set_fmt(frame, p->fmt);
mpp_frame_set_eos(frame, p->frm_eos);
if (p->fp_input && feof(p->fp_input))
mpp_frame_set_buffer(frame, NULL);
else if (cam_buf)
mpp_frame_set_buffer(frame, cam_buf);
else
mpp_frame_set_buffer(frame, p->frm_buf);
meta = mpp_frame_get_meta(frame);
mpp_packet_init_with_buffer(&packet, p->pkt_buf);
/* NOTE: It is important to clear output packet length!! */
mpp_packet_set_length(packet, 0);
mpp_meta_set_packet(meta, KEY_OUTPUT_PACKET, packet);
mpp_meta_set_buffer(meta, KEY_MOTION_INFO, p->md_info);
if (p->osd_enable || p->user_data_enable || p->roi_enable) {
if (p->user_data_enable) {
MppEncUserData user_data;
char *str = "this is user data\n";
if ((p->frame_count & 10) == 0) {
user_data.pdata = str;
user_data.len = strlen(str) + 1;
mpp_meta_set_ptr(meta, KEY_USER_DATA, &user_data);
}
static RK_U8 uuid_debug_info[16] = {
0x57, 0x68, 0x97, 0x80, 0xe7, 0x0c, 0x4b, 0x65,
0xa9, 0x06, 0xae, 0x29, 0x94, 0x11, 0xcd, 0x9a
};
MppEncUserDataSet data_group;
MppEncUserDataFull datas[2];
char *str1 = "this is user data 1\n";
char *str2 = "this is user data 2\n";
data_group.count = 2;
datas[0].len = strlen(str1) + 1;
datas[0].pdata = str1;
datas[0].uuid = uuid_debug_info;
datas[1].len = strlen(str2) + 1;
datas[1].pdata = str2;
datas[1].uuid = uuid_debug_info;
data_group.datas = datas;
mpp_meta_set_ptr(meta, KEY_USER_DATAS, &data_group);
}
if (p->osd_enable) {
/* gen and cfg osd plt */
mpi_enc_gen_osd_plt(&p->osd_plt, p->frame_count);
p->osd_plt_cfg.change = MPP_ENC_OSD_PLT_CFG_CHANGE_ALL;
p->osd_plt_cfg.type = MPP_ENC_OSD_PLT_TYPE_USERDEF;
p->osd_plt_cfg.plt = &p->osd_plt;
ret = mpi->control(ctx, MPP_ENC_SET_OSD_PLT_CFG, &p->osd_plt_cfg);
if (ret) {
mpp_err("mpi control enc set osd plt failed ret %d\n", ret);
goto RET;
}
/* gen and cfg osd plt */
mpi_enc_gen_osd_data(&p->osd_data, p->buf_grp, p->width,
p->height, p->frame_count);
mpp_meta_set_ptr(meta, KEY_OSD_DATA, (void*)&p->osd_data);
}
if (p->roi_enable) {
RoiRegionCfg *region = &p->roi_region;
/* calculated in pixels */
region->x = MPP_ALIGN(p->width / 8, 16);
region->y = MPP_ALIGN(p->height / 8, 16);
region->w = 128;
region->h = 256;
region->force_intra = 0;
region->qp_mode = 1;
region->qp_val = 24;
mpp_enc_roi_add_region(p->roi_ctx, region);
region->x = MPP_ALIGN(p->width / 2, 16);
region->y = MPP_ALIGN(p->height / 4, 16);
region->w = 256;
region->h = 128;
region->force_intra = 1;
region->qp_mode = 1;
region->qp_val = 10;
mpp_enc_roi_add_region(p->roi_ctx, region);
/* send roi info by metadata */
mpp_enc_roi_setup_meta(p->roi_ctx, meta);
}
}
if (!p->first_frm)
p->first_frm = mpp_time();
/*
* NOTE: in non-block mode the frame can be resent.
* The default input timeout mode is block.
*
* User should release the input frame to meet the requirements of
* resource creator must be the resource destroyer.
*/
ret = mpi->encode_put_frame(ctx, frame);
if (ret) {
mpp_err("chn %d encode put frame failed\n", chn);
mpp_frame_deinit(&frame);
goto RET;
}
mpp_frame_deinit(&frame);
do {
ret = mpi->encode_get_packet(ctx, &packet);
if (ret) {
mpp_err("chn %d encode get packet failed\n", chn);
goto RET;
}
mpp_assert(packet);
if (packet) {
// write packet to file here
void *ptr = mpp_packet_get_pos(packet);
size_t len = mpp_packet_get_length(packet);
char log_buf[256];
RK_S32 log_size = sizeof(log_buf) - 1;
RK_S32 log_len = 0;
if (!p->first_pkt)
p->first_pkt = mpp_time();
p->pkt_eos = mpp_packet_get_eos(packet);
if (p->fp_output)
fwrite(ptr, 1, len, p->fp_output);
if (p->fp_verify && !p->pkt_eos) {
calc_data_crc((RK_U8 *)ptr, (RK_U32)len, &checkcrc);
mpp_log("p->frame_count=%d, len=%d\n", p->frame_count, len);
write_data_crc(p->fp_verify, &checkcrc);
}
log_len += snprintf(log_buf + log_len, log_size - log_len,
"encoded frame %-4d", p->frame_count);
/* for low delay partition encoding */
if (mpp_packet_is_partition(packet)) {
eoi = mpp_packet_is_eoi(packet);
log_len += snprintf(log_buf + log_len, log_size - log_len,
" pkt %d", p->frm_pkt_cnt);
p->frm_pkt_cnt = (eoi) ? (0) : (p->frm_pkt_cnt + 1);
}
log_len += snprintf(log_buf + log_len, log_size - log_len,
" size %-7zu", len);
if (mpp_packet_has_meta(packet)) {
meta = mpp_packet_get_meta(packet);
RK_S32 temporal_id = 0;
RK_S32 lt_idx = -1;
RK_S32 avg_qp = -1, bps_rt = -1;
RK_S32 use_lt_idx = -1;
RK_S64 sse = 0;
RK_FLOAT psnr = 0;
if (MPP_OK == mpp_meta_get_s32(meta, KEY_TEMPORAL_ID, &temporal_id))
log_len += snprintf(log_buf + log_len, log_size - log_len,
" tid %d", temporal_id);
if (MPP_OK == mpp_meta_get_s32(meta, KEY_LONG_REF_IDX, &lt_idx))
log_len += snprintf(log_buf + log_len, log_size - log_len,
" lt %d", lt_idx);
if (MPP_OK == mpp_meta_get_s32(meta, KEY_ENC_AVERAGE_QP, &avg_qp))
log_len += snprintf(log_buf + log_len, log_size - log_len,
" qp %2d", avg_qp);
if (MPP_OK == mpp_meta_get_s32(meta, KEY_ENC_BPS_RT, &bps_rt))
log_len += snprintf(log_buf + log_len, log_size - log_len,
" bps_rt %d", bps_rt);
if (MPP_OK == mpp_meta_get_s32(meta, KEY_ENC_USE_LTR, &use_lt_idx))
log_len += snprintf(log_buf + log_len, log_size - log_len, " vi");
if (MPP_OK == mpp_meta_get_s64(meta, KEY_ENC_SSE, &sse)) {
psnr = 3.01029996 * (psnr_const - log2(sse));
log_len += snprintf(log_buf + log_len, log_size - log_len,
" psnr %.4f", psnr);
}
}
mpp_log_q(quiet, "chn %d %s\n", chn, log_buf);
mpp_packet_deinit(&packet);
fps_calc_inc(cmd->fps);
p->stream_size += len;
p->frame_count += eoi;
if (p->pkt_eos) {
mpp_log_q(quiet, "chn %d found last packet\n", chn);
mpp_assert(p->frm_eos);
}
}
} while (!eoi);
if (cam_frm_idx >= 0)
camera_source_put_frame(p->cam_ctx, cam_frm_idx);
if (p->frame_num > 0 && p->frame_count >= p->frame_num)
break;
if (p->loop_end)
break;
if (p->frm_eos && p->pkt_eos)
break;
}
RET:
MPP_FREE(checkcrc.sum);
return ret;
}
void *enc_test(void *arg)
{
MpiEncMultiCtxInfo *info = (MpiEncMultiCtxInfo *)arg;
MpiEncTestArgs *cmd = info->cmd;
MpiEncTestData *p = &info->ctx;
MpiEncMultiCtxRet *enc_ret = &info->ret;
MppPollType timeout = MPP_POLL_BLOCK;
RK_U32 quiet = cmd->quiet;
MPP_RET ret = MPP_OK;
RK_S64 t_s = 0;
RK_S64 t_e = 0;
mpp_log_q(quiet, "%s start\n", info->name);
ret = test_ctx_init(info);
if (ret) {
mpp_err_f("test data init failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = mpp_buffer_group_get_internal(&p->buf_grp, MPP_BUFFER_TYPE_DRM | MPP_BUFFER_FLAGS_CACHABLE);
if (ret) {
mpp_err_f("failed to get mpp buffer group ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = mpp_buffer_get(p->buf_grp, &p->frm_buf, p->frame_size + p->header_size);
if (ret) {
mpp_err_f("failed to get buffer for input frame ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = mpp_buffer_get(p->buf_grp, &p->pkt_buf, p->frame_size);
if (ret) {
mpp_err_f("failed to get buffer for output packet ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = mpp_buffer_get(p->buf_grp, &p->md_info, p->mdinfo_size);
if (ret) {
mpp_err_f("failed to get buffer for motion info output packet ret %d\n", ret);
goto MPP_TEST_OUT;
}
// encoder demo
ret = mpp_create(&p->ctx, &p->mpi);
if (ret) {
mpp_err("mpp_create failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
mpp_log_q(quiet, "%p encoder test start w %d h %d type %d\n",
p->ctx, p->width, p->height, p->type);
ret = p->mpi->control(p->ctx, MPP_SET_OUTPUT_TIMEOUT, &timeout);
if (MPP_OK != ret) {
mpp_err("mpi control set output timeout %d ret %d\n", timeout, ret);
goto MPP_TEST_OUT;
}
ret = mpp_init(p->ctx, MPP_CTX_ENC, p->type);
if (ret) {
mpp_err("mpp_init failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = mpp_enc_cfg_init(&p->cfg);
if (ret) {
mpp_err_f("mpp_enc_cfg_init failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = p->mpi->control(p->ctx, MPP_ENC_GET_CFG, p->cfg);
if (ret) {
mpp_err_f("get enc cfg failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = test_mpp_enc_cfg_setup(info);
if (ret) {
mpp_err_f("test mpp setup failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
t_s = mpp_time();
ret = test_mpp_run(info);
t_e = mpp_time();
if (ret) {
mpp_err_f("test mpp run failed ret %d\n", ret);
goto MPP_TEST_OUT;
}
ret = p->mpi->reset(p->ctx);
if (ret) {
mpp_err("mpi->reset failed\n");
goto MPP_TEST_OUT;
}
enc_ret->elapsed_time = t_e - t_s;
enc_ret->frame_count = p->frame_count;
enc_ret->stream_size = p->stream_size;
enc_ret->frame_rate = (float)p->frame_count * 1000000 / enc_ret->elapsed_time;
enc_ret->bit_rate = (p->stream_size * 8 * (p->fps_out_num / p->fps_out_den)) / p->frame_count;
enc_ret->delay = p->first_pkt - p->first_frm;
MPP_TEST_OUT:
if (p->ctx) {
mpp_destroy(p->ctx);
p->ctx = NULL;
}
if (p->cfg) {
mpp_enc_cfg_deinit(p->cfg);
p->cfg = NULL;
}
if (p->frm_buf) {
mpp_buffer_put(p->frm_buf);
p->frm_buf = NULL;
}
if (p->pkt_buf) {
mpp_buffer_put(p->pkt_buf);
p->pkt_buf = NULL;
}
if (p->md_info) {
mpp_buffer_put(p->md_info);
p->md_info = NULL;
}
if (p->osd_data.buf) {
mpp_buffer_put(p->osd_data.buf);
p->osd_data.buf = NULL;
}
if (p->buf_grp) {
mpp_buffer_group_put(p->buf_grp);
p->buf_grp = NULL;
}
if (p->roi_ctx) {
mpp_enc_roi_deinit(p->roi_ctx);
p->roi_ctx = NULL;
}
test_ctx_deinit(p);
return NULL;
}
int enc_test_multi(MpiEncTestArgs* cmd, const char *name)
{
MpiEncMultiCtxInfo *ctxs = NULL;
float total_rate = 0.0;
RK_S32 ret = MPP_NOK;
RK_S32 i = 0;
ctxs = mpp_calloc(MpiEncMultiCtxInfo, cmd->nthreads);
if (NULL == ctxs) {
mpp_err("failed to alloc context for instances\n");
return -1;
}
for (i = 0; i < cmd->nthreads; i++) {
ctxs[i].cmd = cmd;
ctxs[i].name = name;
ctxs[i].chn = i;
ret = pthread_create(&ctxs[i].thd, NULL, enc_test, &ctxs[i]);
if (ret) {
mpp_err("failed to create thread %d\n", i);
return ret;
}
}
if (cmd->frame_num < 0) {
// wait for input then quit encoding
mpp_log("*******************************************\n");
mpp_log("**** Press Enter to stop loop encoding ****\n");
mpp_log("*******************************************\n");
getc(stdin);
for (i = 0; i < cmd->nthreads; i++)
ctxs[i].ctx.loop_end = 1;
}
for (i = 0; i < cmd->nthreads; i++)
pthread_join(ctxs[i].thd, NULL);
for (i = 0; i < cmd->nthreads; i++) {
MpiEncMultiCtxRet *enc_ret = &ctxs[i].ret;
mpp_log("chn %d encode %d frames time %lld ms delay %3d ms fps %3.2f bps %lld\n",
i, enc_ret->frame_count, (RK_S64)(enc_ret->elapsed_time / 1000),
(RK_S32)(enc_ret->delay / 1000), enc_ret->frame_rate, enc_ret->bit_rate);
total_rate += enc_ret->frame_rate;
}
MPP_FREE(ctxs);
total_rate /= cmd->nthreads;
mpp_log("%s average frame rate %.2f\n", name, total_rate);
return ret;
}
int main(int argc, char **argv)
{
RK_S32 ret = MPP_NOK;
MpiEncTestArgs* cmd = mpi_enc_test_cmd_get();
// parse the cmd option
ret = mpi_enc_test_cmd_update_by_args(cmd, argc, argv);
if (ret)
goto DONE;
mpi_enc_test_cmd_show_opt(cmd);
ret = enc_test_multi(cmd, argv[0]);
DONE:
mpi_enc_test_cmd_put(cmd);
return ret;
}