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

491 lines
13 KiB
C

/* SPDX-License-Identifier: Apache-2.0 OR MIT */
/*
* Copyright (c) 2025 Rockchip Electronics Co., Ltd.
*
* kmpp_venc_obj.c - kmpp_obj path operations via /dev/kmpp_objs + /dev/kmpp_ioctl
*/
#define MODULE_TAG "kmpp_venc_obj"
#include <string.h>
#include "rk_mpi.h"
#include "mpp_2str.h"
#include "mpp_frame_impl.h"
#include "mpp_packet_impl.h"
#include "kmpp.h"
#include "kmpp_obj.h"
#include "kmpp_frame.h"
#include "kmpp_packet.h"
#include "kmpp_meta_impl.h"
#include "kmpp_venc.h"
/* helper: verify param is a kobj, return NOK if not */
#define VENC_KOBJ_CHECK(param, name) \
do { \
if (!kmpp_obj_is_kobj((KmppObj)(param))) { \
mpp_loge_f(name " needs a kobj, got %p\n", (param)); \
return rk_nok; \
} \
} while(0)
static KmppFrame kmpp_convert_frame(Kmpp *ctx, MppFrame frame, RK_U32 *is_kframe)
{
if (!__check_is_mpp_frame(frame)) {
MppFrameImpl *impl = (MppFrameImpl *)frame;
if (ctx->mKframe == NULL)
kmpp_frame_get(&ctx->mKframe);
kmpp_frame_set_width(ctx->mKframe, impl->width);
kmpp_frame_set_height(ctx->mKframe, impl->height);
kmpp_frame_set_hor_stride(ctx->mKframe, impl->hor_stride);
kmpp_frame_set_ver_stride(ctx->mKframe, impl->ver_stride);
kmpp_frame_set_fmt(ctx->mKframe, impl->fmt);
kmpp_frame_set_eos(ctx->mKframe, impl->eos);
kmpp_frame_set_pts(ctx->mKframe, impl->pts);
kmpp_frame_set_dts(ctx->mKframe, impl->dts);
kmpp_frame_set_offset_x(ctx->mKframe, impl->offset_x);
kmpp_frame_set_offset_y(ctx->mKframe, impl->offset_y);
kmpp_frame_set_hor_stride_pixel(ctx->mKframe, impl->hor_stride_pixel);
if (impl->buffer) {
KmppShmPtr *buf_shm = kmpp_obj_to_shm(impl->buffer);
if (buf_shm) {
kmpp_frame_set_buffer(ctx->mKframe, buf_shm);
} else {
kmpp_frame_set_buf_fd(ctx->mKframe, mpp_buffer_get_fd(impl->buffer));
}
}
if (mpp_frame_has_meta(frame)) {
MppMeta meta = mpp_frame_get_meta(frame);
MppPacket packet = NULL;
mpp_meta_get_packet(meta, KEY_OUTPUT_PACKET, &packet);
ctx->mPacket = packet;
/* copy ROI / OSD into kframe's meta so kernel reads them
* per-frame (flex inline) instead of async global control */
{
KmppMeta kMeta = NULL;
kmpp_frame_get_meta_obj(ctx->mKframe, &kMeta);
if (kMeta) {
MppEncROICfg *roi = NULL;
mpp_meta_get_ptr(meta, KEY_ROI_DATA, (void **)&roi);
if (roi)
kmpp_meta_set_ptr(kMeta, KEY_ROI_DATA, roi);
MppEncOSDData3 *osd = NULL;
kmpp_meta_get_osd(meta, &osd);
if (osd)
kmpp_meta_set_osd(kMeta, osd);
}
}
}
*is_kframe = 0;
return ctx->mKframe;
}
*is_kframe = 1;
return (KmppFrame)frame;
}
static MPP_RET init(Kmpp *ctx, MppCtxType type, MppCodingType coding)
{
KmppVenc venc = NULL;
MppVencKcfg kcfg = NULL;
rk_s32 ret;
if (!ctx)
return MPP_ERR_VALUE;
if (mpp_check_support_format(type, coding)) {
mpp_loge("unable to create %s %s for mpp unsupported\n",
strof_ctx_type(type), strof_coding_type(coding));
return MPP_NOK;
}
/* get KmppVenc kernel object */
ret = kmpp_venc_get(&venc);
if (ret || !venc) {
mpp_loge("kmpp_venc_get failed ret %d\n", ret);
return MPP_NOK;
}
ctx->mVenc = venc;
/* create init config and copy from mVencInitKcfg */
mpp_venc_kcfg_init(&kcfg, MPP_VENC_KCFG_TYPE_INIT);
ret = kmpp_obj_copy(kcfg, ctx->mVencInitKcfg);
if (ret) {
mpp_loge("kmpp_obj_copy failed ret %d\n", ret);
goto done;
}
kmpp_obj_get_u32(ctx->mVencInitKcfg, "chan_dup", &ctx->mChanDup);
/* init encoder */
ret = kmpp_venc_init(venc, kcfg);
if (ret) {
mpp_loge("kmpp_venc_init failed ret %d\n", ret);
goto done;
}
if (!ctx->mChanDup) {
ret = kmpp_venc_start(venc);
if (ret) {
mpp_loge("kmpp_venc_start failed ret %d\n", ret);
goto done;
}
}
if (ctx->mPacketGroup == NULL)
mpp_buffer_group_get_internal(&ctx->mPacketGroup, MPP_BUFFER_TYPE_ION);
done:
if (ret) {
if (kcfg)
mpp_venc_kcfg_deinit(kcfg);
if (venc)
kmpp_venc_put(venc);
ctx->mVenc = NULL;
} else {
ctx->mInitDone = 1;
ctx->mType = type;
ctx->mOutputTimeout = MPP_POLL_BLOCK;
}
return (MPP_RET)ret;
}
static void clear(Kmpp *ctx)
{
if (!ctx)
return;
if (ctx->mVenc) {
kmpp_venc_stop(ctx->mVenc);
kmpp_venc_deinit(ctx->mVenc);
kmpp_venc_put(ctx->mVenc);
ctx->mVenc = NULL;
}
if (ctx->mPacketGroup) {
mpp_buffer_group_put(ctx->mPacketGroup);
ctx->mPacketGroup = NULL;
}
if (ctx->mKframe) {
kmpp_frame_put(ctx->mKframe);
ctx->mKframe = NULL;
}
}
static MPP_RET put_frame(Kmpp *ctx, MppFrame frame)
{
MPP_RET ret = MPP_OK;
RK_U32 is_kframe = 0;
KmppFrame kframe;
if (!ctx)
return MPP_ERR_VALUE;
if (!ctx->mInitDone)
return MPP_ERR_INIT;
kframe = kmpp_convert_frame(ctx, frame, &is_kframe);
if (!kframe) {
mpp_loge("kmpp_convert_frame failed\n");
return MPP_NOK;
}
/* check buffer before sending */
if (!is_kframe) {
MppFrameImpl *impl = (MppFrameImpl *)frame;
if (!impl->buffer) {
mpp_loge_f("kmpp put_frame buf is NULL\n");
return MPP_NOK;
}
}
ret = kmpp_venc_put_frm(ctx->mVenc, kframe);
if (ret)
mpp_loge("kmpp_venc_put_frm failed ret %d\n", ret);
return ret;
}
static MPP_RET get_packet(Kmpp *ctx, MppPacket *packet)
{
KmppPacket kmpp_pkt = NULL;
MppPacket pkt = NULL;
RK_S32 len;
RK_U32 flag;
KmppShmPtr pos;
RK_S64 dts;
RK_S64 pts;
rk_s32 ret;
if (!ctx)
return MPP_ERR_VALUE;
if (!ctx->mInitDone)
return MPP_ERR_INIT;
/* blocking get - kmpp_venc_get_pkt will block until packet is ready */
ret = kmpp_venc_get_pkt(ctx->mVenc, &kmpp_pkt);
if (ret || !kmpp_pkt) {
mpp_loge("kmpp_venc_get_pkt failed ret %d\n", ret);
return MPP_NOK;
}
kmpp_packet_get_flag(kmpp_pkt, &flag);
kmpp_packet_get_length(kmpp_pkt, &len);
kmpp_packet_get_pos(kmpp_pkt, &pos);
kmpp_packet_get_dts(kmpp_pkt, &dts);
kmpp_packet_get_pts(kmpp_pkt, &pts);
if (ctx->mPacket) {
void *dst;
pkt = ctx->mPacket;
ctx->mPacket = NULL;
if (pos.uptr) {
dst = mpp_packet_get_pos(pkt);
memcpy(dst, pos.uptr, len);
}
mpp_packet_set_length(pkt, len);
} else {
mpp_packet_init(&pkt, pos.uptr, len);
}
mpp_packet_set_release(pkt, kmpp_release_venc_packet, ctx, kmpp_pkt);
mpp_packet_set_dts(pkt, dts);
mpp_packet_set_pts(pkt, pts);
mpp_packet_set_flag(pkt, flag);
if (flag & MPP_PACKET_FLAG_INTRA) {
MppMeta meta = mpp_packet_get_meta(pkt);
mpp_meta_set_s32(meta, KEY_OUTPUT_INTRA, 1);
}
*packet = pkt;
return MPP_OK;
}
static rk_s32 kmpp_venc_ctrl_exec(KmppVenc venc, KmppObj ctrl, MpiCmd cmd, MppParam param)
{
rk_s32 ret;
kmpp_obj_set_s32(ctrl, "cmd", cmd);
kmpp_obj_set_u32(ctrl, "flags", KMPP_CTRL_FLAG_NONE);
switch (cmd) {
case MPP_ENC_SET_IDR_FRAME: {
kmpp_obj_set_u64(ctrl, "val", 0);
} break;
case MPP_ENC_SET_HEADER_MODE:
case MPP_ENC_SET_SEI_CFG: {
RK_U32 val = param ? *(RK_U32 *)param : 0;
kmpp_obj_set_u64(ctrl, "val", (RK_U64)val);
} break;
case MPP_SET_INPUT_TIMEOUT:
case MPP_SET_OUTPUT_TIMEOUT: {
RK_S64 val = param ? *(RK_S64 *)param : 0;
kmpp_obj_set_u64(ctrl, "val", (rk_u64)val);
} break;
case MPP_ENC_SET_ROI_CFG: {
MppEncROICfg *roi = (MppEncROICfg *)param;
MppEncROICfgLegacy legacy;
legacy.change = 1;
legacy.number = roi->number > 8 ? 8 : roi->number;
memcpy(legacy.regions, roi->regions, legacy.number * sizeof(MppEncROIRegion));
if (mpp_venc_ctrl_set_flex(ctrl, &legacy, sizeof(MppEncROICfgLegacy)))
return rk_nok;
} break;
case MPP_ENC_GET_ROI_CFG: {
kmpp_obj_set_u32(ctrl, "flags", KMPP_CTRL_FLAG_FLEX);
kmpp_obj_set_u32(ctrl, "size", sizeof(MppEncROICfgLegacy));
if (kmpp_obj_resize_f(ctrl, sizeof(MppEncROICfgLegacy)))
return rk_nok;
} break;
case MPP_ENC_SET_OSD_DATA_CFG: {
if (mpp_venc_ctrl_set_flex(ctrl, param, sizeof(MppEncOSDData3)))
return rk_nok;
} break;
case MPP_ENC_SET_USERDATA: {
MppEncUserDataShm u = { .len = 0, .data = {.kaddr = 0, .uaddr = 0} };
if (param) {
MppEncUserData *ud = (MppEncUserData *)param;
u.len = ud->len && ud->pdata ? ud->len : 0;
u.data.uaddr = ud->len && ud->pdata ? (rk_u64)(intptr_t)ud->pdata : 0;
}
kmpp_obj_set_st(ctrl, "arg", &u);
} break;
case MPP_ENC_GET_HDR_SYNC: {
} break;
default: {
return rk_ok;
} break;
}
ret = kmpp_venc_control(venc, ctrl);
if (ret)
mpp_loge_f("kmpp_venc_control cmd %d failed ret %d\n", cmd, ret);
/* GET: read back kernel-written output */
if (!ret) {
switch (cmd) {
case MPP_ENC_GET_ROI_CFG: {
MppEncROICfgLegacy *legacy = (MppEncROICfgLegacy *)mpp_venc_ctrl_flex_base(ctrl);
MppEncROICfg *roi = (MppEncROICfg *)param;
if (param && roi) {
roi->number = legacy->number;
if (legacy->number)
memcpy(roi->regions, legacy->regions,
legacy->number * sizeof(MppEncROIRegion));
} else {
mpp_loge_f("invalid roi ptr %p -> %p\n", legacy, roi);
return rk_nok;
}
} break;
case MPP_ENC_GET_HDR_SYNC: {
KmppShmPtr ret_sptr;
KmppPacket hdr_pkt = NULL;
kmpp_obj_get_st(ctrl, "ret", &ret_sptr);
if (ret_sptr.uaddr)
kmpp_obj_get_by_sptr_f((KmppObj *)&hdr_pkt, &ret_sptr);
if (hdr_pkt) {
KmppShmPtr pos;
RK_S32 hlen = 0;
kmpp_packet_get_pos(hdr_pkt, &pos);
kmpp_packet_get_length(hdr_pkt, &hlen);
if (pos.uptr && hlen > 0) {
memcpy(mpp_packet_get_pos((MppPacket)param), pos.uptr, hlen);
mpp_packet_set_length((MppPacket)param, hlen);
}
kmpp_obj_impl_put_f(hdr_pkt);
}
} break;
default: {
} break;
}
}
return ret;
}
static MPP_RET control(Kmpp *ctx, MpiCmd cmd, MppParam param)
{
if (!ctx || !ctx->mVenc)
return MPP_ERR_VALUE;
if (cmd == MPP_SET_SELECT_TIMEOUT)
return MPP_OK;
return kmpp_venc_ctrl(ctx->mVenc, cmd, param);
}
rk_s32 kmpp_venc_ctrl(KmppVenc venc, MpiCmd cmd, MppParam param)
{
rk_s32 ret = rk_nok;
if (!venc)
return ret;
switch (cmd) {
case MPP_ENC_SET_CFG: {
VENC_KOBJ_CHECK(param, "SET_CFG");
ret = kmpp_venc_set_cfg(venc, param);
} break;
case MPP_ENC_GET_CFG: {
VENC_KOBJ_CHECK(param, "GET_CFG");
ret = kmpp_venc_get_cfg(venc, param);
} break;
case MPP_ENC_SET_REF_CFG: {
MppVencKcfg def_ref = NULL;
if (param) {
VENC_KOBJ_CHECK(param, "SET_REF_CFG");
} else {
/* NULL → apply default IPPP (1 st frame, forward ref, no lt) */
mpp_venc_kcfg_init(&def_ref, MPP_VENC_KCFG_TYPE_REF_CFG);
if (!def_ref) {
mpp_loge("failed to create default ref_cfg\n");
return MPP_NOK;
}
param = def_ref;
}
ret = kmpp_venc_set_ref_cfg(venc, param);
if (def_ref)
mpp_venc_kcfg_deinit(def_ref);
} break;
case MPP_ENC_GET_REF_CFG: {
VENC_KOBJ_CHECK(param, "GET_REF_CFG");
ret = kmpp_venc_get_ref_cfg(venc, param);
} break;
case MPP_SET_VENC_INIT_KCFG: {
VENC_KOBJ_CHECK(param, "SET_INIT_CFG");
ret = kmpp_venc_init(venc, param);
} break;
default: {
MppVencKcfg ctrl = NULL;
mpp_venc_kcfg_init(&ctrl, MPP_VENC_KCFG_TYPE_CTRL_CFG);
if (ctrl) {
ret = kmpp_venc_ctrl_exec(venc, ctrl, cmd, param);
mpp_venc_kcfg_deinit(ctrl);
} else {
mpp_loge_f("can not create valid ctrl_cfg object\n");
}
} break;
}
return ret;
}
static MPP_RET reset(Kmpp *ctx)
{
if (!ctx)
return MPP_ERR_VALUE;
if (ctx->mChanDup)
return MPP_OK;
if (!ctx->mInitDone)
return MPP_ERR_INIT;
return kmpp_venc_reset(ctx->mVenc);
}
KmppOps kmpp_venc_obj_ops = {
.init = init,
.start = NULL,
.stop = NULL,
.pause = NULL,
.resume = NULL,
.put_frame = put_frame,
.get_packet = get_packet,
.control = control,
.reset = reset,
.clear = clear,
};