singe/thirdparty/rockchip-mpp/kmpp/test/kmpp_venc_test.c
2026-09-10 20:17:28 -05:00

546 lines
18 KiB
C

/* SPDX-License-Identifier: Apache-2.0 OR MIT */
/*
* Copyright (c) 2025 Rockchip Electronics Co., Ltd.
*/
#define MODULE_TAG "kmpp_venc_test"
#include "mpp_mem.h"
#include "mpp_debug.h"
#include "mpp_common.h"
#include "kmpp_meta.h"
#include "kmpp_frame.h"
#include "kmpp_packet.h"
#include "kmpp_buffer.h"
#include "kmpp_obj.h"
#include "kmpp_venc.h"
#include "mpp_enc_ref.h"
#include "mpi_enc_utils.h"
#include "kmpp_venc_utils.h"
typedef struct KmppVencTestCtx_t {
MpiEncTestArgs *cmd;
KmppVenc venc;
FILE *fp_in;
FILE *fp_out;
RK_U32 frame_size;
RK_U32 width;
RK_U32 height;
RK_U32 hor_stride;
RK_U32 ver_stride;
RK_S32 frame_cnt;
KmppBufGrp buf_grp;
RK_U8 *ud_buf;
RK_U32 ud_buf_size;
} KmppVencTestCtx;
static MPP_RET venc_cfg_setup(KmppVencTestCtx *ctx)
{
MppVencKcfg cfg;
RK_S32 ret = rk_ok;
MpiEncTestArgs *cmd = ctx->cmd;
KmppVenc venc = ctx->venc;
if (cmd->fps_in_den == 0)
cmd->fps_in_den = 1;
if (cmd->fps_in_num == 0)
cmd->fps_in_num = 30;
if (cmd->fps_out_den == 0)
cmd->fps_out_den = 1;
if (cmd->fps_out_num == 0)
cmd->fps_out_num = 30;
if (!cmd->bps_target)
cmd->bps_target = cmd->width * cmd->height / 8 * (cmd->fps_out_num / cmd->fps_out_den);
mpp_venc_kcfg_init(&cfg, MPP_VENC_KCFG_TYPE_ST_CFG);
ret = kmpp_venc_get_cfg(venc, cfg);
if (ret) {
mpp_loge("get venc cfg failed\n");
return ret;
}
mpp_venc_kcfg_set_s32(cfg, "codec:type", cmd->type);
mpp_venc_kcfg_set_s32(cfg, "prep:width", cmd->width);
mpp_venc_kcfg_set_s32(cfg, "prep:height", cmd->height);
mpp_venc_kcfg_set_s32(cfg, "prep:hor_stride", cmd->hor_stride);
mpp_venc_kcfg_set_s32(cfg, "prep:ver_stride", cmd->ver_stride);
mpp_venc_kcfg_set_s32(cfg, "prep:format", cmd->format);
mpp_venc_kcfg_set_s32(cfg, "prep:range", 0);
mpp_venc_kcfg_set_s32(cfg, "rc:fps_in_flex", 0);
mpp_venc_kcfg_set_s32(cfg, "rc:fps_in_num", cmd->fps_in_num ? cmd->fps_in_num : 30);
mpp_venc_kcfg_set_s32(cfg, "rc:fps_in_denom", 1);
mpp_venc_kcfg_set_s32(cfg, "rc:fps_out_flex", 0);
mpp_venc_kcfg_set_s32(cfg, "rc:fps_out_num", cmd->fps_out_num ? cmd->fps_out_num : 30);
mpp_venc_kcfg_set_s32(cfg, "rc:fps_out_denom", 1);
mpp_venc_kcfg_set_s32(cfg, "rc:mode", cmd->rc_mode);
mpp_venc_kcfg_set_s32(cfg, "rc:bps_target", cmd->bps_target ? cmd->bps_target : 2000000);
mpp_venc_kcfg_set_s32(cfg, "rc:bps_max", cmd->bps_max ? cmd->bps_max : 4000000);
mpp_venc_kcfg_set_s32(cfg, "rc:bps_min", cmd->bps_min ? cmd->bps_min : 1000000);
mpp_venc_kcfg_set_s32(cfg, "rc:gop", cmd->gop_len ? cmd->gop_len : 60);
switch (cmd->type) {
case MPP_VIDEO_CodingAVC : {
mpp_venc_kcfg_set_s32(cfg, "h264:profile", 100);
mpp_venc_kcfg_set_s32(cfg, "h264:level", 40);
mpp_venc_kcfg_set_s32(cfg, "h264:cabac_en", 1);
mpp_venc_kcfg_set_s32(cfg, "h264:cabac_idc", 0);
mpp_venc_kcfg_set_s32(cfg, "h264:trans8x8", 1);
} break;
case MPP_VIDEO_CodingMJPEG : {
mpp_venc_kcfg_set_s32(cfg, "jpeg:q_factor", cmd->qp_init ? cmd->qp_init : 80);
mpp_venc_kcfg_set_s32(cfg, "jpeg:qf_max", cmd->qp_max ? cmd->qp_max : 99);
mpp_venc_kcfg_set_s32(cfg, "jpeg:qf_min", cmd->qp_min ? cmd->qp_min : 1);
} break;
default: {
} break;
}
switch (cmd->type) {
case MPP_VIDEO_CodingHEVC :
case MPP_VIDEO_CodingAVC : {
switch (cmd->rc_mode) {
case MPP_ENC_RC_MODE_FIXQP : {
RK_S32 fix_qp = cmd->qp_init;
mpp_venc_kcfg_set_s32(cfg, "rc:qp_init", fix_qp);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_max", fix_qp);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_min", fix_qp);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_max_i", fix_qp);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_min_i", fix_qp);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_ip", 0);
mpp_venc_kcfg_set_s32(cfg, "rc:fqp_min_i", fix_qp);
mpp_venc_kcfg_set_s32(cfg, "rc:fqp_max_i", fix_qp);
mpp_venc_kcfg_set_s32(cfg, "rc:fqp_min_p", fix_qp);
mpp_venc_kcfg_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_venc_kcfg_set_s32(cfg, "rc:qp_init", cmd->qp_init ? cmd->qp_init : -1);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_max", cmd->qp_max ? cmd->qp_max : 51);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_min", cmd->qp_min ? cmd->qp_min : 10);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_max_i", cmd->qp_max_i ? cmd->qp_max_i : 51);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_min_i", cmd->qp_min_i ? cmd->qp_min_i : 10);
mpp_venc_kcfg_set_s32(cfg, "rc:qp_ip", 2);
mpp_venc_kcfg_set_s32(cfg, "rc:fqp_min_i", cmd->fqp_min_i ? cmd->fqp_min_i : 10);
mpp_venc_kcfg_set_s32(cfg, "rc:fqp_max_i", cmd->fqp_max_i ? cmd->fqp_max_i : 45);
mpp_venc_kcfg_set_s32(cfg, "rc:fqp_min_p", cmd->fqp_min_p ? cmd->fqp_min_p : 10);
mpp_venc_kcfg_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", cmd->rc_mode);
} break;
}
} break;
case MPP_VIDEO_CodingMJPEG : {
mpp_venc_kcfg_set_s32(cfg, "jpeg:q_factor", cmd->qp_init ? cmd->qp_init : 80);
mpp_venc_kcfg_set_s32(cfg, "jpeg:qf_max", cmd->qp_max ? cmd->qp_max : 99);
mpp_venc_kcfg_set_s32(cfg, "jpeg:qf_min", cmd->qp_min ? cmd->qp_min : 1);
} break;
default: {
} break;
}
kmpp_venc_set_cfg(ctx->venc, cfg);
mpp_venc_kcfg_deinit(cfg);
return ret;
}
static MPP_RET venc_encode_oneframe(KmppVencTestCtx *ctx)
{
KmppVenc venc = ctx->venc;
KmppPacket packet = NULL;
KmppFrame frame = NULL;
KmppBuffer kbuf = NULL;
KmppBufCfg buf_cfg = NULL;
KmppShmPtr sptr, grp_sptr;
rk_s32 frame_cnt = ctx->frame_cnt;
rk_s32 ud_sent = 0;
/* allocate KmppBuffer and fill pixel data */
kmpp_buffer_get(&kbuf);
buf_cfg = kmpp_buffer_to_cfg(kbuf);
grp_sptr = *kmpp_obj_to_shm(ctx->buf_grp);
kmpp_buf_cfg_set_group(buf_cfg, &grp_sptr);
kmpp_buf_cfg_set_size(buf_cfg, ctx->frame_size);
kmpp_buffer_setup(kbuf);
/* get data pointer for filling pixel data */
kmpp_buf_cfg_get_sptr(buf_cfg, &sptr);
fill_image(sptr.uptr, ctx->width, ctx->height,
ctx->hor_stride, ctx->ver_stride, 0, frame_cnt);
/* attach KmppBuffer object to frame via shm ptr */
sptr = *kmpp_obj_to_shm(kbuf);
kmpp_frame_get(&frame);
kmpp_frame_set_buffer(frame, &sptr);
kmpp_frame_set_width(frame, ctx->width);
kmpp_frame_set_height(frame, ctx->height);
kmpp_frame_set_hor_stride(frame, ctx->hor_stride);
kmpp_frame_set_ver_stride(frame, ctx->ver_stride);
kmpp_frame_set_fmt(frame, MPP_FMT_YUV420SP);
kmpp_frame_set_eos(frame, 0);
/* set USER_DATA / USER_DATAS into frame meta for SEI verification:
* frame 0 -> KEY_USER_DATAS (set variant)
* frame 1 -> KEY_USER_DATA (single variant)
* frame 2+ -> single variant with different data
*
* NOTE: use raw kmpp_obj_get_by_sptr_f + kmpp_obj_impl_put_f
* instead of kmpp_frame_get_meta_obj(). The latter caches the meta
* impl in frame->priv and releases it in frame deinit. In ref_cfg
* mode the kernel copies frame's meta sptr to the output packet,
* causing kmpp_packet_get_meta_obj() to hit the same priv_offset
* cache and return the identical impl. Two owners -> double-put.
* Explicit put here clears priv_offset before the packet path. */
{
KmppShmPtr frm_meta_sptr;
if (!kmpp_frame_get_meta(frame, &frm_meta_sptr) &&
(frm_meta_sptr.uptr || frm_meta_sptr.kptr)) {
KmppMeta frm_meta = NULL;
kmpp_obj_get_by_sptr_f(&frm_meta, &frm_meta_sptr);
if (frm_meta) {
MppEncFrmCfg tmp_entry;
const MppEncFrmCfg *entry;
entry = mpp_enc_frm_cfg_lookup(&mpp_enc_test_frm_cfg,
frame_cnt);
if (entry) {
if (entry->ud_buf) {
kmpp_venc_gen_frame_meta(frm_meta, ctx->width,
ctx->height, entry);
} else {
/* fill test userdata buffer */
memset(ctx->ud_buf, 'A' + (frame_cnt % 26),
ctx->ud_buf_size);
tmp_entry = *entry;
tmp_entry.ud_uuid = venc_test_uuid;
tmp_entry.ud_buf = ctx->ud_buf;
tmp_entry.ud_buf_size = ctx->ud_buf_size;
kmpp_venc_gen_frame_meta(frm_meta, ctx->width,
ctx->height, &tmp_entry);
}
ud_sent = 1;
}
kmpp_obj_impl_put_f(frm_meta);
}
}
}
kmpp_venc_put_frm(venc, frame);
kmpp_venc_get_pkt(venc, &packet);
if (packet) {
KmppFrame frame_out = NULL;
KmppMeta meta = NULL;
char log_buf[128];
rk_s32 log_size = sizeof(log_buf) - 1;
rk_s32 log_len = 0;
rk_s32 len = 0;
kmpp_packet_get_length(packet, &len);
log_len += snprintf(log_buf + log_len, log_size - log_len,
"frame %d get packet length %d", frame_cnt, len);
/* scan SEI in the output bitstream to verify userdata crossed the
* user/kernel boundary correctly (user set uptr -> kernel get fix_kptr
* -> SEI NAL written by update_user_data). */
if (ud_sent) {
KmppShmPtr pos;
kmpp_packet_get_pos(packet, &pos);
if (pos.uptr && len > 0) {
char expect_char = 'A' + (frame_cnt % 26);
char expect[4];
rk_s32 sei_ok;
memset(expect, expect_char, sizeof(expect));
sei_ok = kmpp_venc_scan_sei_userdata((RK_U8 *)pos.uptr, len,
expect, sizeof(expect));
if (sei_ok)
log_len += snprintf(log_buf + log_len, log_size - log_len,
" SEI ud");
}
}
mpp_logi("%s\n", log_buf);
if (ctx->fp_out) {
KmppShmPtr pos;
kmpp_packet_get_pos(packet, &pos);
fwrite(pos.uptr, 1, len, ctx->fp_out);
fflush(ctx->fp_out);
}
if (kmpp_packet_has_meta(packet)) {
KmppMeta pkt_meta = NULL;
kmpp_packet_get_meta_obj(packet, &pkt_meta);
meta = pkt_meta;
}
if (meta) {
kmpp_meta_get_obj(meta, KEY_INPUT_FRAME, (KmppObj *)&frame_out);
if (frame_out) {
KmppShmPtr *frm_sptr = kmpp_obj_to_shm(frame);
KmppShmPtr *inf_sptr = kmpp_obj_to_shm(frame_out);
if (frm_sptr && inf_sptr && frm_sptr->kptr != inf_sptr->kptr)
mpp_loge("frame %d shm mismatch: frame kptr %p in_frame kptr %p\n",
frame_cnt, frm_sptr->kptr, inf_sptr->kptr);
/* frame_out returned by kmpp_obj_get_by_sptr is the same
* impl as 'frame' (priv_offset cache hit on shared shm).
* Do NOT put it separately — kmpp_frame_put() handles it. */
}
}
kmpp_packet_put(packet);
} else {
mpp_loge("frame %d get no packet\n", frame_cnt);
}
kmpp_frame_put(frame);
kmpp_buffer_put(kbuf);
return rk_ok;
}
static MPP_RET venc_test_ctx_init(KmppVencTestCtx *ctx)
{
MpiEncTestArgs *cmd = ctx->cmd;
ctx->width = cmd->width;
ctx->height = cmd->height;
ctx->hor_stride = cmd->hor_stride ? cmd->hor_stride : MPP_ALIGN(cmd->width, 16);
ctx->ver_stride = cmd->ver_stride ? cmd->ver_stride : MPP_ALIGN(cmd->height, 16);
switch (cmd->format & MPP_FRAME_FMT_MASK) {
case MPP_FMT_YUV420SP:
case MPP_FMT_YUV420P: {
ctx->frame_size = MPP_ALIGN(ctx->hor_stride, 64) * MPP_ALIGN(ctx->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 : {
ctx->frame_size = MPP_ALIGN(ctx->hor_stride, 64) * MPP_ALIGN(ctx->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 : {
ctx->frame_size = MPP_ALIGN(ctx->hor_stride, 64) * MPP_ALIGN(ctx->ver_stride, 64);
} break;
default: {
ctx->frame_size = MPP_ALIGN(ctx->hor_stride, 64) * MPP_ALIGN(ctx->ver_stride, 64) * 4;
} break;
}
/* allocate userdata test buffer */
ctx->ud_buf_size = 64;
ctx->ud_buf = mpp_malloc(rk_u8, ctx->ud_buf_size);
if (!ctx->ud_buf) {
mpp_err_f("alloc ud_buf failed\n");
return MPP_NOK;
}
if (cmd->file_output) {
FILE *fp = fopen(cmd->file_output, "w+b");
if (!fp) {
mpp_err_f("open file %s failed\n", cmd->file_output);
return MPP_NOK;
}
ctx->fp_out = fp;
}
return MPP_OK;
}
int main(int argc, char **argv)
{
MppEncTestObjSet *obj_set = NULL;
MpiEncTestArgs *cmd = NULL;
KmppVencTestCtx ctx;
KmppVenc venc = NULL;
MppVencKcfg cfg = NULL;
MppEncRefCfg ref_cfg = NULL;
rk_u32 max_lt_cnt = 0;
rk_s32 ret = rk_ok;
memset(&ctx, 0, sizeof(ctx));
ret = mpi_enc_test_objset_get(&obj_set);
if (ret)
goto DONE;
// parse the cmd option
ret = mpi_enc_test_objset_update_by_args(obj_set, argc, argv, MODULE_TAG);
if (ret)
goto DONE;
cmd = obj_set->cmd;
ctx.cmd = cmd;
ret = venc_test_ctx_init(&ctx);
if (ret)
goto DONE;
/* load ref_cfg from json early: cpb_info.max_lt_cnt drives the init kcfg,
* and the cfg is applied via MPP_ENC_SET_REF_CFG after start.
* Use kernel objdef (KmppVencRefCfg) for SET_REF_CFG compatibility;
* mpi_enc_load_ref_cfg / mpp_enc_ref_cfg_check use objdef API, safe. */
if (cmd->file_ref_cfg) {
mpp_enc_ref_cfg_create(&ref_cfg, 2);
if (!ref_cfg) {
mpp_loge("create ref_cfg failed\n");
goto DONE;
}
ret = mpi_enc_load_ref_cfg(ref_cfg, cmd->file_ref_cfg);
if (ret) {
mpp_loge("load ref_cfg %s failed\n", cmd->file_ref_cfg);
goto DONE;
}
mpp_enc_ref_cfg_check(ref_cfg);
/* cpb_info: use objdef API instead of direct struct access */
kmpp_obj_get_u32(ref_cfg, "lt_cfg_cnt", &max_lt_cnt);
mpp_logi("ref_cfg %s loaded: lt_cfg_cnt %u\n",
cmd->file_ref_cfg, max_lt_cnt);
}
mpp_logi("start\n");
ret = kmpp_venc_get(&venc);
if (ret) {
mpp_loge("get venc failed\n");
goto DONE;
}
ctx.venc = venc;
mpp_venc_kcfg_init(&cfg, MPP_VENC_KCFG_TYPE_INIT);
mpp_venc_kcfg_set_u32(cfg, "type", MPP_CTX_ENC);
mpp_venc_kcfg_set_u32(cfg, "coding", cmd->type);
mpp_venc_kcfg_set_s32(cfg, "chan_id", 0);
mpp_venc_kcfg_set_u32(cfg, "max_width", cmd->width);
mpp_venc_kcfg_set_u32(cfg, "max_height", cmd->height);
mpp_venc_kcfg_set_u32(cfg, "max_lt_cnt", max_lt_cnt);
mpp_venc_kcfg_set_s32(cfg, "input_timeout", -1);
mpp_venc_kcfg_set_s32(cfg, "ntfy_mode", 0);
ret = kmpp_venc_init(venc, cfg);
if (ret) {
mpp_loge("init venc failed\n");
mpp_venc_kcfg_deinit(cfg);
goto DONE;
}
mpp_venc_kcfg_deinit(cfg);
cfg = NULL;
ret = venc_cfg_setup(&ctx);
if (ret) {
mpp_loge("setup venc cfg failed\n");
goto DONE;
}
ret = kmpp_venc_start(venc);
if (ret) {
mpp_loge("start venc failed\n");
goto DONE;
}
/* apply ref_cfg via the obj-path control: ref_cfg is a MppEncRefCfg shm,
* carried in ctrl->arg (flags=SHM); kernel routes it to the kcfg ref path */
if (ref_cfg) {
ret = kmpp_venc_ctrl(venc, MPP_ENC_SET_REF_CFG, ref_cfg);
mpp_logi("SET_REF_CFG (shm) ret %d\n", ret);
if (ret)
goto DONE;
}
/* init KmppBufGrp for KmppBuffer allocation */
kmpp_buf_grp_get(&ctx.buf_grp);
{
KmppBufGrpCfg grp_cfg = kmpp_buf_grp_to_cfg(ctx.buf_grp);
kmpp_buf_grp_cfg_set_count(grp_cfg, 16);
kmpp_buf_grp_cfg_set_size(grp_cfg, ctx.frame_size);
kmpp_buf_grp_setup(ctx.buf_grp);
}
while (ctx.frame_cnt < cmd->frame_num) {
ret = venc_encode_oneframe(&ctx);
if (ret) {
mpp_loge("encode one frame failed\n");
break;
}
ctx.frame_cnt++;
}
DONE:
if (ref_cfg) {
mpp_enc_ref_cfg_deinit(&ref_cfg);
ref_cfg = NULL;
}
if (ctx.buf_grp) {
kmpp_buf_grp_put(ctx.buf_grp);
ctx.buf_grp = NULL;
}
MPP_FREE(ctx.ud_buf);
if (ctx.fp_out) {
fclose(ctx.fp_out);
ctx.fp_out = NULL;
}
if (ctx.venc) {
ret = kmpp_venc_stop(venc);
if (ret)
mpp_loge("stop venc failed\n");
ret = kmpp_venc_deinit(venc);
if (ret)
mpp_loge("deinit venc failed\n");
kmpp_venc_put(ctx.venc);
}
mpi_enc_test_objset_put(obj_set);
mpp_logi("%s\n", ret ? "failed" : "success");
return ret;
}