/* * Copyright 2020 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. */ #define MODULE_TAG "vpu_api_mlvec" #include #include "string.h" #include "mpp_mem.h" #include "mpp_debug.h" #include "mpp_common.h" #include "vpu_api_mlvec.h" #include "vpu_api_legacy.h" #define VPU_API_DBG_MLVEC_FUNC (0x00010000) #define VPU_API_DBG_MLVEC_FLOW (0x00020000) #define mlvec_dbg_func(fmt, ...) vpu_api_dbg_f(VPU_API_DBG_MLVEC_FUNC, fmt, ## __VA_ARGS__) #define mlvec_dbg_flow(fmt, ...) vpu_api_dbg_f(VPU_API_DBG_MLVEC_FLOW, fmt, ## __VA_ARGS__) typedef struct VpuApiMlvecImpl_t { MppCtx mpp; MppApi *mpi; MppEncCfg enc_cfg; VpuApiMlvecStaticCfg st_cfg; VpuApiMlvecDynamicCfg dy_cfg; } VpuApiMlvecImpl; MPP_RET vpu_api_mlvec_init(VpuApiMlvec *ctx) { if (NULL == ctx) { mpp_err_f("invalid NULL input\n"); return MPP_ERR_NULL_PTR; } mlvec_dbg_func("enter %p\n", ctx); VpuApiMlvecImpl *impl = mpp_calloc(VpuApiMlvecImpl, 1); if (NULL == impl) mpp_err_f("failed to create MLVEC context\n"); mpp_assert(sizeof(VpuApiMlvecStaticCfg) == sizeof(EncParameter_t)); /* default disable frame_qp setup */ impl->dy_cfg.frame_qp = -1; *ctx = impl; mlvec_dbg_func("leave %p %p\n", ctx, impl); return (impl) ? (MPP_OK) : (MPP_NOK); } MPP_RET vpu_api_mlvec_deinit(VpuApiMlvec ctx) { mlvec_dbg_func("enter %p\n", ctx); MPP_FREE(ctx); mlvec_dbg_func("leave %p\n", ctx); return MPP_OK; } MPP_RET vpu_api_mlvec_setup(VpuApiMlvec ctx, MppCtx mpp, MppApi *mpi, MppEncCfg enc_cfg) { if (NULL == ctx || NULL == mpp || NULL == mpi || NULL == enc_cfg) { mpp_err_f("invalid NULL input ctx %p mpp %p mpi %p cfg %p\n", ctx, mpp, mpi, enc_cfg); return MPP_ERR_NULL_PTR; } mlvec_dbg_func("enter %p\n", ctx); VpuApiMlvecImpl *impl = (VpuApiMlvecImpl *)ctx; impl->mpp = mpp; impl->mpi = mpi; impl->enc_cfg = enc_cfg; mlvec_dbg_func("leave %p\n", ctx); return MPP_OK; } MPP_RET vpu_api_mlvec_check_cfg(void *p) { if (NULL == p) { mpp_err_f("invalid NULL input\n"); return MPP_ERR_NULL_PTR; } VpuApiMlvecStaticCfg *cfg = (VpuApiMlvecStaticCfg *)p; RK_U32 magic = cfg->magic; MPP_RET ret = MPP_OK; if ((((magic >> 24) & 0xff) != MLVEC_MAGIC) || (((magic >> 16) & 0xff) != MLVEC_VERSION)) ret = MPP_NOK; mlvec_dbg_flow("check mlvec cfg magic %08x %s\n", magic, (ret == MPP_OK) ? "success" : "failed"); return ret; } MPP_RET vpu_api_mlvec_set_st_cfg(VpuApiMlvec ctx, VpuApiMlvecStaticCfg *cfg) { if (NULL == ctx || NULL == cfg) { mpp_err_f("invalid NULL input ctx %p cfg %p\n"); return MPP_ERR_NULL_PTR; } mlvec_dbg_func("enter ctx %p cfg %p\n", ctx, cfg); /* check mlvec magic word */ if (vpu_api_mlvec_check_cfg(cfg)) return MPP_NOK; MPP_RET ret = MPP_OK; /* update static configure */ VpuApiMlvecImpl *impl = (VpuApiMlvecImpl *)ctx; memcpy(&impl->st_cfg, cfg, sizeof(impl->st_cfg)); cfg = &impl->st_cfg; /* get mpp context and check */ MppCtx mpp_ctx = impl->mpp; MppApi *mpi = impl->mpi; MppEncCfg enc_cfg = impl->enc_cfg; mpp_assert(mpp_ctx); mpp_assert(mpi); mpp_assert(enc_cfg); /* start control mpp */ mlvec_dbg_flow("hdr_on_idr %d\n", cfg->hdr_on_idr); MppEncHeaderMode mode = cfg->hdr_on_idr ? MPP_ENC_HEADER_MODE_EACH_IDR : MPP_ENC_HEADER_MODE_DEFAULT; ret = mpi->control(mpp_ctx, MPP_ENC_SET_HEADER_MODE, &mode); if (ret) mpp_err("setup enc header mode %d failed ret %d\n", mode, ret); mlvec_dbg_flow("add_prefix %d\n", cfg->add_prefix); mpp_enc_cfg_set_s32(enc_cfg, "h264:prefix_mode", cfg->add_prefix); mlvec_dbg_flow("slice_mbs %d\n", cfg->slice_mbs); if (cfg->slice_mbs) { mpp_enc_cfg_set_u32(enc_cfg, "split:mode", MPP_ENC_SPLIT_BY_CTU); mpp_enc_cfg_set_u32(enc_cfg, "split:arg", cfg->slice_mbs); } else mpp_enc_cfg_set_u32(enc_cfg, "split:mode", MPP_ENC_SPLIT_NONE); /* NOTE: ltr_frames is already configured */ vpu_api_mlvec_set_dy_max_tid(ctx, cfg->max_tid); mlvec_dbg_func("leave ctx %p ret %d\n", ctx, ret); return ret; } MPP_RET vpu_api_mlvec_set_dy_cfg(VpuApiMlvec ctx, VpuApiMlvecDynamicCfg *cfg, MppMeta meta) { if (NULL == ctx || NULL == cfg || NULL == meta) { mpp_err_f("invalid NULL input ctx %p cfg %p meta %p\n", ctx, cfg, meta); return MPP_ERR_NULL_PTR; } mlvec_dbg_func("enter ctx %p cfg %p meta %p\n", ctx, cfg, meta); MPP_RET ret = MPP_OK; VpuApiMlvecImpl *impl = (VpuApiMlvecImpl *)ctx; VpuApiMlvecDynamicCfg *dst = &impl->dy_cfg; /* clear non-sticky flag first */ dst->mark_ltr = -1; dst->use_ltr = -1; /* frame qp and base layer pid is sticky flag */ /* update flags */ if (cfg->updated) { if (cfg->updated & VPU_API_ENC_MARK_LTR_UPDATED) dst->mark_ltr = cfg->mark_ltr; if (cfg->updated & VPU_API_ENC_USE_LTR_UPDATED) dst->use_ltr = cfg->use_ltr; if (cfg->updated & VPU_API_ENC_FRAME_QP_UPDATED) dst->frame_qp = cfg->frame_qp; if (cfg->updated & VPU_API_ENC_BASE_PID_UPDATED) dst->base_layer_pid = cfg->base_layer_pid; /* dynamic max temporal layer count updated go through mpp ref cfg */ cfg->updated = 0; } mlvec_dbg_flow("ltr mark %2d use %2d frm qp %2d blpid %d\n", dst->mark_ltr, dst->use_ltr, dst->frame_qp, dst->base_layer_pid); /* setup next frame configure */ if (dst->mark_ltr >= 0) mpp_meta_set_s32(meta, KEY_ENC_MARK_LTR, dst->mark_ltr); if (dst->use_ltr >= 0) mpp_meta_set_s32(meta, KEY_ENC_USE_LTR, dst->use_ltr); if (dst->frame_qp >= 0) mpp_meta_set_s32(meta, KEY_ENC_FRAME_QP, dst->frame_qp); if (dst->base_layer_pid >= 0) mpp_meta_set_s32(meta, KEY_ENC_BASE_LAYER_PID, dst->base_layer_pid); mlvec_dbg_func("leave ctx %p ret %d\n", ctx, ret); return ret; } MPP_RET vpu_api_mlvec_set_dy_max_tid(VpuApiMlvec ctx, RK_S32 max_tid) { if (NULL == ctx) { mpp_err_f("invalid NULL input\n"); return MPP_ERR_NULL_PTR; } mlvec_dbg_func("enter ctx %p max_tid %d\n", ctx, max_tid); MPP_RET ret = MPP_OK; VpuApiMlvecImpl *impl = (VpuApiMlvecImpl *)ctx; MppCtx mpp_ctx = impl->mpp; MppApi *mpi = impl->mpi; MppEncCfg enc_cfg = impl->enc_cfg; mpp_assert(mpp_ctx); mpp_assert(mpi); mpp_assert(enc_cfg); MppEncRefLtFrmCfg lt_ref[16]; MppEncRefStFrmCfg st_ref[16]; RK_S32 lt_cfg_cnt = 0; RK_S32 st_cfg_cnt = 0; RK_S32 tid0_loop = 0; RK_S32 ltr_frames = impl->st_cfg.ltr_frames; memset(lt_ref, 0, sizeof(lt_ref)); memset(st_ref, 0, sizeof(st_ref)); mlvec_dbg_flow("ltr_frames %d\n", ltr_frames); mlvec_dbg_flow("max_tid %d\n", max_tid); switch (max_tid) { case 0 : { st_ref[0].is_non_ref = 0; st_ref[0].temporal_id = 0; st_ref[0].ref_mode = REF_TO_PREV_REF_FRM; st_ref[0].ref_arg = 0; st_ref[0].repeat = 0; st_cfg_cnt = 1; tid0_loop = 1; mlvec_dbg_flow("no tsvc\n"); } break; case 1 : { /* set tsvc2 st-ref struct */ /* st 0 layer 0 - ref */ st_ref[0].is_non_ref = 0; st_ref[0].temporal_id = 0; st_ref[0].ref_mode = REF_TO_PREV_REF_FRM; st_ref[0].ref_arg = 0; st_ref[0].repeat = 0; /* st 1 layer 1 - non-ref */ st_ref[1].is_non_ref = 1; st_ref[1].temporal_id = 1; st_ref[1].ref_mode = REF_TO_PREV_REF_FRM; st_ref[1].ref_arg = 0; st_ref[1].repeat = 0; /* st 2 layer 0 - ref */ st_ref[2].is_non_ref = 0; st_ref[2].temporal_id = 0; st_ref[2].ref_mode = REF_TO_PREV_REF_FRM; st_ref[2].ref_arg = 0; st_ref[2].repeat = 0; st_cfg_cnt = 3; tid0_loop = 2; mlvec_dbg_flow("tsvc2\n"); } break; case 2 : { /* set tsvc3 st-ref struct */ /* st 0 layer 0 - ref */ st_ref[0].is_non_ref = 0; st_ref[0].temporal_id = 0; st_ref[0].ref_mode = REF_TO_TEMPORAL_LAYER; st_ref[0].ref_arg = 0; st_ref[0].repeat = 0; /* st 1 layer 2 - non-ref */ st_ref[1].is_non_ref = 0; st_ref[1].temporal_id = 2; st_ref[1].ref_mode = REF_TO_TEMPORAL_LAYER; st_ref[1].ref_arg = 0; st_ref[1].repeat = 0; /* st 2 layer 1 - ref */ st_ref[2].is_non_ref = 0; st_ref[2].temporal_id = 1; st_ref[2].ref_mode = REF_TO_TEMPORAL_LAYER; st_ref[2].ref_arg = 0; st_ref[2].repeat = 0; /* st 3 layer 2 - non-ref */ st_ref[3].is_non_ref = 0; st_ref[3].temporal_id = 2; st_ref[3].ref_mode = REF_TO_TEMPORAL_LAYER; st_ref[3].ref_arg = 1; st_ref[3].repeat = 0; /* st 4 layer 0 - ref */ st_ref[4].is_non_ref = 0; st_ref[4].temporal_id = 0; st_ref[4].ref_mode = REF_TO_TEMPORAL_LAYER; st_ref[4].ref_arg = 0; st_ref[4].repeat = 0; st_cfg_cnt = 5; tid0_loop = 4; mlvec_dbg_flow("tsvc3\n"); } break; case 3 : { /* set tsvc3 st-ref struct */ /* st 0 layer 0 - ref */ st_ref[0].is_non_ref = 0; st_ref[0].temporal_id = 0; st_ref[0].ref_mode = REF_TO_TEMPORAL_LAYER; st_ref[0].ref_arg = 0; st_ref[0].repeat = 0; /* st 1 layer 3 - non-ref */ st_ref[1].is_non_ref = 1; st_ref[1].temporal_id = 3; st_ref[1].ref_mode = REF_TO_PREV_REF_FRM; st_ref[1].ref_arg = 0; st_ref[1].repeat = 0; /* st 2 layer 2 - ref */ st_ref[2].is_non_ref = 0; st_ref[2].temporal_id = 2; st_ref[2].ref_mode = REF_TO_PREV_REF_FRM; st_ref[2].ref_arg = 0; st_ref[2].repeat = 0; /* st 3 layer 3 - non-ref */ st_ref[3].is_non_ref = 1; st_ref[3].temporal_id = 3; st_ref[3].ref_mode = REF_TO_PREV_REF_FRM; st_ref[3].ref_arg = 0; st_ref[3].repeat = 0; /* st 4 layer 1 - ref */ st_ref[4].is_non_ref = 0; st_ref[4].temporal_id = 1; st_ref[4].ref_mode = REF_TO_TEMPORAL_LAYER; st_ref[4].ref_arg = 0; st_ref[4].repeat = 0; /* st 5 layer 3 - non-ref */ st_ref[5].is_non_ref = 1; st_ref[5].temporal_id = 3; st_ref[5].ref_mode = REF_TO_PREV_REF_FRM; st_ref[5].ref_arg = 0; st_ref[5].repeat = 0; /* st 6 layer 2 - ref */ st_ref[6].is_non_ref = 0; st_ref[6].temporal_id = 2; st_ref[6].ref_mode = REF_TO_PREV_REF_FRM; st_ref[6].ref_arg = 0; st_ref[6].repeat = 0; /* st 7 layer 3 - non-ref */ st_ref[7].is_non_ref = 1; st_ref[7].temporal_id = 3; st_ref[7].ref_mode = REF_TO_PREV_REF_FRM; st_ref[7].ref_arg = 0; st_ref[7].repeat = 0; /* st 8 layer 0 - ref */ st_ref[8].is_non_ref = 0; st_ref[8].temporal_id = 0; st_ref[8].ref_mode = REF_TO_PREV_REF_FRM; st_ref[8].ref_arg = 0; st_ref[8].repeat = 0; st_cfg_cnt = 9; tid0_loop = 8; mlvec_dbg_flow("tsvc4\n"); } break; default : { mpp_err("invalid max temporal layer id %d\n", max_tid); } break; } if (ltr_frames) { RK_S32 i; lt_cfg_cnt = ltr_frames; mpp_assert(ltr_frames <= MPP_ENC_MAX_LT_REF_NUM); for (i = 0; i < ltr_frames; i++) { lt_ref[i].lt_idx = i; lt_ref[i].temporal_id = 0; lt_ref[i].ref_mode = REF_TO_PREV_LT_REF; lt_ref[i].lt_gap = 0; lt_ref[i].lt_delay = tid0_loop * i; } } if (lt_cfg_cnt) mpp_assert(st_cfg_cnt); mlvec_dbg_flow("lt_cfg_cnt %d st_cfg_cnt %d\n", lt_cfg_cnt, st_cfg_cnt); if (lt_cfg_cnt || st_cfg_cnt) { MppEncRefCfg ref = NULL; mpp_enc_ref_cfg_init(&ref); ret = mpp_enc_ref_cfg_set_cfg_cnt(ref, lt_cfg_cnt, st_cfg_cnt); ret = mpp_enc_ref_cfg_add_lt_cfg(ref, lt_cfg_cnt, lt_ref); ret = mpp_enc_ref_cfg_add_st_cfg(ref, st_cfg_cnt, st_ref); ret = mpp_enc_ref_cfg_set_keep_cpb(ref, 1); ret = mpp_enc_ref_cfg_check(ref); ret = mpi->control(mpp_ctx, MPP_ENC_SET_REF_CFG, ref); if (ret) mpp_err("mpi control enc set ref cfg failed ret %d\n", ret); mpp_enc_ref_cfg_deinit(&ref); } else { ret = mpi->control(mpp_ctx, MPP_ENC_SET_REF_CFG, NULL); if (ret) mpp_err("mpi control enc set ref cfg failed ret %d\n", ret); } mlvec_dbg_func("leave ctx %p ret %d\n", ctx, ret); return ret; }