From c2069f33455b56b074a6ac01c77ac89c19a339a7 Mon Sep 17 00:00:00 2001 From: Herman Chen Date: Tue, 26 May 2020 15:04:44 +0800 Subject: [PATCH] [mpp_enc_ref]: Add MppEncRef/MppEncRefs module Add MppEncRef/MppEncRefs module MppEncRefs is for mpp encoder coded picture buffer (CPB) management. MppEncRefs will gnerate reference buffer status EncCpbStatus for each encoder to implement its own process. Change-Id: I5f85a0f46dc6bc40954b0393ba52ad4008565643 Signed-off-by: Herman Chen --- inc/mpp_rc_defs.h | 155 ++++--- mpp/base/CMakeLists.txt | 2 + mpp/base/inc/mpp_enc_ref.h | 69 +++ mpp/base/inc/mpp_enc_refs.h | 78 ++++ mpp/base/mpp_enc_ref.cpp | 427 ++++++++++++++++++ mpp/base/mpp_enc_refs.cpp | 752 +++++++++++++++++++++++++++++++ mpp/base/test/CMakeLists.txt | 3 + mpp/base/test/mpp_enc_ref_test.c | 176 ++++++++ mpp/inc/mpp_enc_cfg.h | 2 + 9 files changed, 1598 insertions(+), 66 deletions(-) create mode 100644 mpp/base/inc/mpp_enc_ref.h create mode 100644 mpp/base/inc/mpp_enc_refs.h create mode 100644 mpp/base/mpp_enc_ref.cpp create mode 100644 mpp/base/mpp_enc_refs.cpp create mode 100644 mpp/base/test/mpp_enc_ref_test.c diff --git a/inc/mpp_rc_defs.h b/inc/mpp_rc_defs.h index 7ea6be0d..fc81df2a 100644 --- a/inc/mpp_rc_defs.h +++ b/inc/mpp_rc_defs.h @@ -17,7 +17,9 @@ #ifndef __MPP_RC_DEFS_H__ #define __MPP_RC_DEFS_H__ -#include "rk_type.h" +#include "rk_venc_ref.h" + +#define MAX_CPB_REFS (8) /* * EncFrmStatus controls record the encoding frame status and also control @@ -29,86 +31,106 @@ * 16 ~ 31 reference frame status * bit 32 ~ 63 encoding flow control */ -typedef struct EncFrmStatus_t { - /* - * bit 0 ~ 31 frame status - */ - /* - * 0 - inter frame - * 1 - intra frame - */ - RK_U32 is_intra : 1; +typedef union EncFrmStatus_u { + struct { + /* + * bit 0 ~ 31 frame status + */ + /* status flag */ + RK_U32 valid : 1; + RK_U32 reserved0 : 3; - /* - * Valid when is_intra is true - * 0 - normal intra frame - * 1 - IDR frame - */ - RK_U32 is_idr : 1; + /* reference status flag */ + /* + * 0 - inter frame + * 1 - intra frame + */ + RK_U32 is_intra : 1; - /* - * 0 - mark as reference frame - * 1 - mark as non-refernce frame - */ - RK_U32 is_non_ref : 1; + /* + * Valid when is_intra is true + * 0 - normal intra frame + * 1 - IDR frame + */ + RK_U32 is_idr : 1; - /* - * Valid when is_non_ref is false - * 0 - mark as short-term reference frame - * 1 - mark as long-term refernce frame - */ - RK_U32 is_lt_ref : 1; + /* + * 0 - mark as reference frame + * 1 - mark as non-refernce frame + */ + RK_U32 is_non_ref : 1; - RK_U32 reserved0 : 4; + /* + * Valid when is_non_ref is false + * 0 - mark as short-term reference frame + * 1 - mark as long-term refernce frame + */ + RK_U32 is_lt_ref : 1; - /* bit 8 - 15 */ - RK_U32 lt_idx : 4; - RK_U32 temporal_id : 4; + /* bit 8 - 15 */ + RK_U32 lt_idx : 4; + RK_U32 temporal_id : 4; - /* distance between current frame and reference frame */ - RK_S32 ref_dist : 16; + /* distance between current frame and reference frame */ + MppEncRefMode ref_mode : 6; + RK_S32 ref_arg : 8; + RK_S32 ref_dist : 2; - /* - * bit 32 ~ 63 encoder flow control flags - */ - /* - * 0 - normal frame encoding - * 1 - current frame will be dropped - */ - RK_U32 drop : 1; + /* + * bit 32 ~ 63 encoder flow control flags + */ + /* + * 0 - normal frame encoding + * 1 - current frame will be dropped + */ + RK_U32 drop : 1; - /* - * 0 - rate control module does not change frame type parameter - * 1 - rate control module changes frame type parameter reencode is needed - * to reprocess the dpb process. Also this means dpb module will follow - * the frame status parameter provided by rate control module. - */ - RK_U32 re_dpb_proc : 1; + /* + * 0 - rate control module does not change frame type parameter + * 1 - rate control module changes frame type parameter reencode is needed + * to reprocess the dpb process. Also this means dpb module will follow + * the frame status parameter provided by rate control module. + */ + RK_U32 re_dpb_proc : 1; - /* - * 0 - current frame encoding is in normal flow - * 1 - current frame encoding is in reencode flow - */ - RK_U32 reencode : 1; + /* + * 0 - current frame encoding is in normal flow + * 1 - current frame encoding is in reencode flow + */ + RK_U32 reencode : 1; - /* - * When true current frame size is super large then the frame should be reencoded. - */ - RK_U32 super_frame : 1; + /* + * When true current frame size is super large then the frame should be reencoded. + */ + RK_U32 super_frame : 1; - /* - * When true currnet frame is force to encoded as software skip frame - */ - RK_U32 force_pskip : 1; - RK_U32 reserved1 : 3; + /* + * When true currnet frame is force to encoded as software skip frame + */ + RK_U32 force_pskip : 1; + RK_U32 reserved1 : 3; - /* reencode times */ - RK_U32 reencode_times : 8; + /* reencode times */ + RK_U32 reencode_times : 8; - /* sequential index for each frame */ - RK_U32 seq_idx : 16; + /* sequential index for each frame */ + RK_U32 seq_idx : 16; + }; + RK_U64 val; } EncFrmStatus; +typedef struct EncCpbStatus_t { + RK_S32 seq_idx; + + EncFrmStatus curr; + EncFrmStatus refr; + + /* initial cpb status for current frame encoding */ + EncFrmStatus init[MAX_CPB_REFS]; + /* final cpb status after current frame encoding */ + EncFrmStatus final[MAX_CPB_REFS]; +} EncCpbStatus; + /* * communication channel between rc / hal / hardware * @@ -136,6 +158,7 @@ typedef struct EncRcCommonInfo_t { } EncRcTaskInfo; typedef struct EncRcTask_s { + EncCpbStatus cpb; EncFrmStatus frm; EncRcTaskInfo info; MppFrame frame; diff --git a/mpp/base/CMakeLists.txt b/mpp/base/CMakeLists.txt index fbad8358..1b0079f9 100644 --- a/mpp/base/CMakeLists.txt +++ b/mpp/base/CMakeLists.txt @@ -4,6 +4,8 @@ # add mpp basic components # ---------------------------------------------------------------------------- add_library(mpp_base STATIC + mpp_enc_refs.cpp + mpp_enc_ref.cpp mpp_enc_cfg.cpp mpp_buf_slot.cpp mpp_buffer_impl.cpp diff --git a/mpp/base/inc/mpp_enc_ref.h b/mpp/base/inc/mpp_enc_ref.h new file mode 100644 index 00000000..5e02b8e2 --- /dev/null +++ b/mpp/base/inc/mpp_enc_ref.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef __MPP_ENC_REF_H__ +#define __MPP_ENC_REF_H__ + +#include "rk_venc_ref.h" + +#define REF_MODE_IS_GLOBAL(mode) ((mode >= REF_MODE_GLOBAL) && (mode < REF_MODE_GLOBAL_BUTT)) +#define REF_MODE_IS_LT_MODE(mode) ((mode > REF_MODE_LT) && (mode < REF_MODE_LT_BUTT)) +#define REF_MODE_IS_ST_MODE(mode) ((mode > REF_MODE_ST) && (mode < REF_MODE_ST_BUTT)) + +typedef struct MppEncCpbInfo_t { + RK_S32 dpb_size; + RK_S32 max_lt_cnt; + RK_S32 max_st_cnt; + RK_S32 max_lt_idx; + RK_S32 max_st_tid; + /* loop length of st/lt config */ + RK_S32 lt_gop; + RK_S32 st_gop; +} MppEncCpbInfo; + +typedef struct MppEncRefCfgImpl_t { + const char *name; + RK_S32 ready; + RK_U32 debug; + + /* config from user */ + RK_S32 max_lt_cfg; + RK_S32 max_st_cfg; + RK_S32 lt_cfg_cnt; + RK_S32 st_cfg_cnt; + MppEncRefLtFrmCfg *lt_cfg; + MppEncRefStFrmCfg *st_cfg; + + /* generated parameter for MppEncRefs */ + MppEncCpbInfo cpb_info; +} MppEncRefCfgImpl; + +#ifdef __cplusplus +extern "C" { +#endif + +MppEncRefCfg mpp_enc_ref_default(void); +MPP_RET mpp_enc_ref_cfg_copy(MppEncRefCfg dst, MppEncRefCfg src); +MppEncCpbInfo *mpp_enc_ref_cfg_get_cpb_info(MppEncRefCfg ref); + +#define check_is_mpp_enc_ref_cfg(ref) _check_is_mpp_enc_ref_cfg(__FUNCTION__, ref) +MPP_RET _check_is_mpp_enc_ref_cfg(const char *func, void *ref); + +#ifdef __cplusplus +} +#endif + +#endif /*__MPP_ENC_REF_H__*/ diff --git a/mpp/base/inc/mpp_enc_refs.h b/mpp/base/inc/mpp_enc_refs.h new file mode 100644 index 00000000..58c069c9 --- /dev/null +++ b/mpp/base/inc/mpp_enc_refs.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef __MPP_ENC_REFS_H__ +#define __MPP_ENC_REFS_H__ + +#include "mpp_enc_cfg.h" +#include "mpp_enc_ref.h" + +/* + * MppEncRefs has two runtime mode: + * + * 1. dryrun mode + * This mode is for estimating the dpb size and configure check. + * + * 2. runtime mode + * Thie mode is for real dpb loop in real encoder workflow. + * + * When encoder is running user can change the frame property by MppEncRefFrmUsrCfg. + */ +#define ENC_FORCE_IDR (0x00000001) +#define ENC_FORCE_LT_REF_IDX (0x00000002) +#define ENC_FORCE_TEMPORAL_ID (0x00000004) +#define ENC_FORCE_REF_MODE (0x00000008) + +typedef struct MppEncRefFrmForceCfg_t { + RK_U32 force_flag; + RK_S32 force_idr; + RK_S32 force_lt_idx; + RK_S32 force_temporal_id; + MppEncRefMode force_ref_mode; + RK_S32 force_ref_arg; +} MppEncRefFrmUsrCfg; + +typedef void* MppEncRefs; + +#ifdef __cplusplus +extern "C" { +#endif + +MPP_RET mpp_enc_refs_init(MppEncRefs *refs, MppEncCfgSet *cfg); +MPP_RET mpp_enc_refs_deinit(MppEncRefs *refs); + +MPP_RET mpp_enc_refs_set_cfg(MppEncRefs refs, MppEncRefCfg ref_cfg); +MPP_RET mpp_enc_refs_set_usr_cfg(MppEncRefs refs, MppEncRefFrmUsrCfg *force); + +/* get dpb size */ +MPP_RET mpp_enc_refs_get_cpb_info(MppEncRefs refs, MppEncCpbInfo *info); +/* get status for next frame */ +MPP_RET mpp_enc_refs_get_cpb(MppEncRefs refs, EncCpbStatus *status); +/* dryrun and check all configure */ +MPP_RET mpp_enc_refs_dryrun(MppEncRefs refs); + +MPP_RET mpp_enc_refs_stash(MppEncRefs refs); +MPP_RET mpp_enc_refs_rollback(MppEncRefs refs); + +#define dump_frm(frm) _dump_frm(frm, __FUNCTION__, __LINE__) + +void _dump_frm(EncFrmStatus *frm, const char *func, RK_S32 line); + +#ifdef __cplusplus +} +#endif + +#endif /*__MPP_ENC_REFS_H__*/ diff --git a/mpp/base/mpp_enc_ref.cpp b/mpp/base/mpp_enc_ref.cpp new file mode 100644 index 00000000..867ae5e6 --- /dev/null +++ b/mpp/base/mpp_enc_ref.cpp @@ -0,0 +1,427 @@ +/* + * 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. + */ + +#define MODULE_TAG "mpp_enc_ref" + +#include + +#include "mpp_env.h" +#include "mpp_log.h" +#include "mpp_mem.h" +#include "mpp_time.h" + +#include "mpp_rc_defs.h" +#include "mpp_enc_ref.h" +#include "mpp_enc_refs.h" + +#define setup_mpp_enc_ref_cfg(ref) \ + ((MppEncRefCfgImpl*)ref)->name = module_name; + +static const char *module_name = MODULE_TAG; + +MPP_RET _check_is_mpp_enc_ref_cfg(const char *func, void *ref) +{ + if (NULL == ref) { + mpp_err("%s input ref check NULL failed\n", func); + return MPP_NOK; + } + + if (((MppEncRefCfgImpl*)(ref))->name != module_name) { + mpp_err("%s input ref check %p %p failed\n", func, ((MppEncRefCfgImpl*)(ref))->name); + return MPP_NOK; + } + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_init(MppEncRefCfg *ref) +{ + if (NULL == ref) { + mpp_err_f("invalid NULL input ref_cfg\n"); + return MPP_ERR_NULL_PTR; + } + + MppEncRefCfgImpl *p = mpp_calloc(MppEncRefCfgImpl, 1); + *ref = p; + if (NULL == p) { + mpp_err_f("malloc failed\n"); + return MPP_ERR_NULL_PTR; + } + + mpp_env_get_u32("enc_ref_cfg_debug", &p->debug, 0); + + setup_mpp_enc_ref_cfg(p); + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_deinit(MppEncRefCfg *ref) +{ + if (!ref || check_is_mpp_enc_ref_cfg(*ref)) { + mpp_err_f("input %p check failed\n", ref); + return MPP_ERR_VALUE; + } + + MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)(*ref); + MPP_FREE(p->lt_cfg); + MPP_FREE(p->st_cfg); + MPP_FREE(p); + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_reset(MppEncRefCfg ref) +{ + if (check_is_mpp_enc_ref_cfg(ref)) + return MPP_ERR_VALUE; + + MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)ref; + MPP_FREE(p->lt_cfg); + MPP_FREE(p->st_cfg); + memset(p, 0, sizeof(*p)); + setup_mpp_enc_ref_cfg(p); + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_set_cfg_cnt(MppEncRefCfg ref, RK_S32 lt_cnt, RK_S32 st_cnt) +{ + if (check_is_mpp_enc_ref_cfg(ref)) + return MPP_ERR_NULL_PTR; + + MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)ref; + MppEncRefLtFrmCfg *lt_cfg = p->lt_cfg; + MppEncRefStFrmCfg *st_cfg = p->st_cfg;; + + if (lt_cfg || st_cfg) { + mpp_err_f("do call reset before setup new cnout\n"); + MPP_FREE(lt_cfg); + MPP_FREE(st_cfg); + } + + if (lt_cnt) { + lt_cfg = mpp_calloc(MppEncRefLtFrmCfg, lt_cnt); + if (NULL == lt_cfg) + mpp_log_f("failed to create %d lt ref cfg\n", lt_cnt); + } + + if (st_cnt) { + st_cfg = mpp_calloc(MppEncRefStFrmCfg, st_cnt); + if (NULL == st_cfg) + mpp_log_f("failed to create %d st ref cfg\n", lt_cnt); + } + + p->max_lt_cfg = lt_cnt; + p->max_st_cfg = st_cnt; + p->lt_cfg_cnt = 0; + p->st_cfg_cnt = 0; + p->lt_cfg = lt_cfg; + p->st_cfg = st_cfg; + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_add_lt_cfg(MppEncRefCfg ref, RK_S32 cnt, MppEncRefLtFrmCfg *frm) +{ + if (check_is_mpp_enc_ref_cfg(ref)) + return MPP_ERR_VALUE; + + MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)ref; + + if (p->debug) + mpp_log("ref %p add lt %d cfg idx %d tid %d gap %d delay %d ref mode %x\n", + ref, p->lt_cfg_cnt, frm->lt_idx, frm->temporal_id, + frm->lt_gap, frm->lt_delay, frm->ref_mode); + + memcpy(&p->lt_cfg[p->lt_cfg_cnt], frm, sizeof(*frm) * cnt); + p->lt_cfg_cnt += cnt; + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_add_st_cfg(MppEncRefCfg ref, RK_S32 cnt, MppEncRefStFrmCfg *frm) +{ + if (check_is_mpp_enc_ref_cfg(ref)) { + mpp_err_f("input %p check failed\n", ref); + return MPP_ERR_VALUE; + } + + MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)ref; + + if (p->debug) + mpp_log("ref %p add lt %d cfg non %d tid %d gap repeat %d ref mode %x arg %d\n", + ref, p->st_cfg_cnt, frm->is_non_ref, frm->temporal_id, + frm->repeat, frm->ref_mode, frm->ref_arg); + + memcpy(&p->st_cfg[p->st_cfg_cnt], frm, sizeof(*frm) * cnt); + p->st_cfg_cnt += cnt; + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_check(MppEncRefCfg ref) +{ + if (check_is_mpp_enc_ref_cfg(ref)) + return MPP_ERR_VALUE; + + MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)ref; + RK_S32 lt_cfg_cnt = p->lt_cfg_cnt; + RK_S32 st_cfg_cnt = p->st_cfg_cnt; + RK_S32 max_lt_ref_cnt = 0; + RK_S32 max_lt_ref_idx = 0; + RK_S32 lt_idx_used_mask = 0; + RK_S32 lt_dryrun_length = 0; + RK_S32 max_st_ref_cnt = 0; + RK_S32 max_st_tid = 0; + RK_S32 st_tid_used_mask = 0; + RK_S32 st_dryrun_length = 0; + RK_S32 ready = 1; + + /* parse and check gop config for encoder */ + if (lt_cfg_cnt) { + RK_S32 pos = 0; + MppEncRefLtFrmCfg *cfg = p->lt_cfg; + + for (pos = 0; pos < lt_cfg_cnt; pos++, cfg++) { + MppEncRefMode ref_mode = cfg->ref_mode; + RK_S32 temporal_id = cfg->temporal_id; + RK_S32 lt_idx = cfg->lt_idx; + RK_U32 lt_idx_mask = 1 << lt_idx; + + /* check lt idx */ + if (lt_idx >= MPP_ENC_MAX_LT_REF_NUM) { + mpp_err_f("ref cfg %p lt cfg %d with invalid lt_idx %d larger than MPP_ENC_MAX_LT_REF_NUM\n", + ref, pos, lt_idx); + ready = 0; + } + + if (lt_idx_used_mask & lt_idx_mask) { + mpp_err_f("ref cfg %p lt cfg %d with redefined lt_idx %d config\n", + ref, pos, lt_idx); + ready = 0; + } + + if (!(lt_idx_used_mask & lt_idx_mask)) { + lt_idx_used_mask |= lt_idx_mask; + max_lt_ref_cnt++; + } + + if (lt_idx > max_lt_ref_idx) + max_lt_ref_idx = lt_idx; + + /* check temporal id */ + if (temporal_id != 0) { + mpp_err_f("ref cfg %p lt cfg %d with invalid temporal_id %d is non-zero\n", + ref, pos, temporal_id); + ready = 0; + } + + /* check gop mode */ + if (!REF_MODE_IS_GLOBAL(ref_mode) && !REF_MODE_IS_LT_MODE(ref_mode)) { + mpp_err_f("ref cfg %p lt cfg %d with invalid ref mode %x\n", + ref, pos, ref_mode); + ready = 0; + } + + /* if check failed just quit */ + if (!ready) + break; + + if (cfg->lt_gap && (cfg->lt_gap + cfg->lt_delay > lt_dryrun_length)) + lt_dryrun_length = cfg->lt_gap + cfg->lt_delay; + } + } + + /* check st-ref config */ + if (ready && st_cfg_cnt) { + RK_S32 pos = 0; + MppEncRefStFrmCfg *cfg = p->st_cfg; + + for (pos = 0; pos < st_cfg_cnt; pos++, cfg++) { + MppEncRefMode ref_mode = cfg->ref_mode; + RK_S32 temporal_id = cfg->temporal_id; + RK_U32 tid_mask = 1 << temporal_id; + + /* check temporal_id */ + if (temporal_id > MPP_ENC_MAX_TEMPORAL_LAYER_NUM - 1) { + mpp_err_f("ref cfg %p st cfg %d with invalid temporal_id %d larger than MPP_ENC_MAX_TEMPORAL_LAYER_NUM\n", + ref, pos, temporal_id); + ready = 0; + } + + /* check gop mode */ + if (!REF_MODE_IS_GLOBAL(ref_mode) && !REF_MODE_IS_ST_MODE(ref_mode)) { + mpp_err_f("ref cfg %p st cfg %d with invalid ref mode %x\n", + ref, pos, ref_mode); + ready = 0; + } + + /* constrain on head and tail frame */ + if (pos == 0 || (pos == lt_cfg_cnt - 1)) { + if (cfg->is_non_ref) { + mpp_err_f("ref cfg %p st cfg %d with invalid non-ref frame on head/tail frame\n", + ref, pos); + ready = 0; + } + + if (temporal_id > 0) { + mpp_err_f("ref cfg %p st cfg %d with invalid non-zero temporal id %d on head/tail frame\n", + ref, pos, temporal_id); + ready = 0; + } + } + + if (!ready) + break; + + if (!cfg->is_non_ref && !(st_tid_used_mask & tid_mask)) { + max_st_ref_cnt++; + st_tid_used_mask |= tid_mask; + } + + if (temporal_id > max_st_tid) + max_st_tid = temporal_id; + + st_dryrun_length++; + st_dryrun_length += cfg->repeat; + } + } + + if (ready) { + MppEncCpbInfo *cpb_info = &p->cpb_info; + MppEncRefs refs = NULL; + MPP_RET ret = MPP_OK; + + cpb_info->dpb_size = 0; + cpb_info->max_lt_cnt = max_lt_ref_cnt; + cpb_info->max_st_cnt = max_st_ref_cnt; + cpb_info->max_lt_idx = max_lt_ref_idx; + cpb_info->max_st_tid = max_st_tid; + cpb_info->lt_gop = lt_dryrun_length; + cpb_info->st_gop = st_dryrun_length - 1; + + ret = mpp_enc_refs_init(&refs, NULL); + ready = (ret) ? 0 : (ready); + ret = mpp_enc_refs_set_cfg(refs, ref); + ready = (ret) ? 0 : (ready); + ret = mpp_enc_refs_dryrun(refs); + ready = (ret) ? 0 : (ready); + + /* update dpb size */ + ret = mpp_enc_refs_get_cpb_info(refs, cpb_info); + ready = (ret) ? 0 : (ready); + + ret = mpp_enc_refs_deinit(&refs); + ready = (ret) ? 0 : (ready); + } else { + mpp_err_f("check ref cfg %p failed\n", ref); + } + p->ready = ready; + + return ready ? MPP_OK : MPP_NOK; +} + +MPP_RET mpp_enc_ref_cfg_show(MppEncRefCfg ref) +{ + if (check_is_mpp_enc_ref_cfg(ref)) + return MPP_ERR_VALUE; + + return MPP_OK; +} + +MPP_RET mpp_enc_ref_cfg_copy(MppEncRefCfg dst, MppEncRefCfg src) +{ + if (check_is_mpp_enc_ref_cfg(dst) || check_is_mpp_enc_ref_cfg(src)) + return MPP_ERR_VALUE; + + MPP_RET ret = MPP_OK; + MppEncRefCfgImpl *d = (MppEncRefCfgImpl *)dst; + MppEncRefCfgImpl *s = (MppEncRefCfgImpl *)src; + RK_S32 max_lt_cfg = s->max_lt_cfg; + RK_S32 max_st_cfg = s->max_st_cfg; + + /* step 1 - free cfg in dst */ + MPP_FREE(d->lt_cfg); + MPP_FREE(d->st_cfg); + + /* step 2 - copy contex from src */ + memcpy(d, s, sizeof(*d)); + + /* step 3 - create new storage and copy lt/st cfg */ + if (max_lt_cfg) { + MppEncRefLtFrmCfg *lt_cfg = mpp_calloc(MppEncRefLtFrmCfg, max_lt_cfg); + + if (NULL == lt_cfg) { + mpp_log_f("failed to create %d lt ref cfg\n", max_lt_cfg); + ret = MPP_NOK; + } else + memcpy(lt_cfg, s->lt_cfg, sizeof(lt_cfg[0]) * s->max_lt_cfg); + + d->lt_cfg = lt_cfg; + } + + if (max_st_cfg) { + MppEncRefStFrmCfg *st_cfg = mpp_calloc(MppEncRefStFrmCfg, max_st_cfg); + + if (NULL == st_cfg) { + mpp_log_f("failed to create %d st ref cfg\n", max_st_cfg); + ret = MPP_NOK; + } else + memcpy(st_cfg, s->st_cfg, sizeof(st_cfg[0]) * s->max_st_cfg); + + d->st_cfg = st_cfg; + } + + if (ret) + mpp_enc_ref_cfg_reset(dst); + + return ret; +} + +MppEncCpbInfo *mpp_enc_ref_cfg_get_cpb_info(MppEncRefCfg ref) +{ + if (check_is_mpp_enc_ref_cfg(ref)) + return NULL; + + MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)ref; + return &p->cpb_info; +} + +static MppEncRefStFrmCfg default_st_ref_cfg = { + .is_non_ref = 0, + .temporal_id = 0, + .ref_mode = REF_TO_PREV_REF_FRM, + .ref_arg = 0, + .repeat = 0, +}; + +static const MppEncRefCfgImpl default_ref_cfg = { + .name = module_name, + .ready = 1, + .debug = 0, + .max_lt_cfg = 0, + .max_st_cfg = 1, + .lt_cfg_cnt = 0, + .st_cfg_cnt = 1, + .lt_cfg = NULL, + .st_cfg = &default_st_ref_cfg, + .cpb_info = { 1, 0, 1, 0, 0, 0, 0 }, +}; + +MppEncRefCfg mpp_enc_ref_default(void) +{ + return (MppEncRefCfg)&default_ref_cfg; +} diff --git a/mpp/base/mpp_enc_refs.cpp b/mpp/base/mpp_enc_refs.cpp new file mode 100644 index 00000000..a5161fb9 --- /dev/null +++ b/mpp/base/mpp_enc_refs.cpp @@ -0,0 +1,752 @@ +/* + * 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. + */ + +#define MODULE_TAG "mpp_enc_refs" + +#include + +#include "mpp_env.h" +#include "mpp_log.h" +#include "mpp_mem.h" +#include "mpp_common.h" + +#include "mpp_enc_ref.h" +#include "mpp_enc_refs.h" + +#define MAX_CPB_ST_FRM 16 +#define MAX_CPB_LT_FRM 16 +#define MAX_CPB_TID_FRM 16 +#define MAX_CPB_LT_IDX 16 +#define MAX_CPB_FRM ((MAX_CPB_ST_FRM) + (MAX_CPB_LT_FRM)) + +#define MPP_ENC_REFS_DBG_FUNC (0x00000001) +#define MPP_ENC_REFS_DBG_FLOW (0x00000002) +#define MPP_ENC_REFS_DBG_FRM (0x00000004) +#define MPP_ENC_REFS_DBG_SIZE (0x00000008) + +#define enc_refs_dbg_func(fmt, ...) _mpp_dbg_f(enc_refs_debug, MPP_ENC_REFS_DBG_FUNC, fmt, ## __VA_ARGS__) +#define enc_refs_dbg_flow(fmt, ...) _mpp_dbg_f(enc_refs_debug, MPP_ENC_REFS_DBG_FLOW, fmt, ## __VA_ARGS__) +#define enc_refs_dbg_frm(fmt, ...) _mpp_dbg(enc_refs_debug, MPP_ENC_REFS_DBG_FRM, fmt, ## __VA_ARGS__) +#define enc_refs_dbg_size(fmt, ...) _mpp_dbg(enc_refs_debug, MPP_ENC_REFS_DBG_SIZE, fmt, ## __VA_ARGS__) + +typedef struct RefsCnt_t { + RK_S32 delay; + RK_S32 delay_cnt; + RK_S32 len; + RK_S32 cnt; + RK_S32 idx; + + RK_S32 lt_idx; + RK_S32 tid; + MppEncRefMode ref_mode; + RK_S32 ref_arg; +} RefsCnt; + +typedef struct EncVirtualCpb_t { + MppEncCpbInfo info; + + /* + * max 32 reference slot for frame searching + * (max 16 lt + max 16 st) + * st_slot 0 ~ 15 + * lt_slot 16 ~ 31 + */ + EncFrmStatus cpb_refs[MAX_CPB_FRM]; + + /* max 32 reference mode */ + EncFrmStatus mode_refs[MAX_CPB_FRM]; + + /* + * reference mode storage with args + * st + tid - max 16 + * lt + idx - max 16 + */ + EncFrmStatus st_tid_refs[MAX_CPB_TID_FRM]; + EncFrmStatus lt_idx_refs[MAX_CPB_LT_IDX]; + + /* long-term counter */ + /* lt ref will have multiple counters */ + RefsCnt lt_cnter[MAX_CPB_LT_IDX]; + + /* max two list and each list as two frames */ + EncFrmStatus list0[2]; + EncFrmStatus list1[2]; + /* frame kept in cpb */ + EncFrmStatus cpb_st[2]; + + /* runtime record */ + RK_S32 frm_idx; /* overall frame index */ + RK_S32 seq_idx; /* sequence index in one gop */ + RK_S32 seq_cnt; + RK_S32 st_cfg_pos; + RK_S32 st_cfg_repeat_pos; +} EncVirtualCpb; + +typedef struct MppEncRefsImpl_t { + MppEncCfgSet *cfg_set; + MppEncRefCfgImpl *ref_cfg; + + RK_S32 igop; + RK_S32 usr_cfg_updated; + MppEncRefFrmUsrCfg usr_cfg; + + EncVirtualCpb cpb; + EncVirtualCpb cpb_stash; +} MppEncRefsImpl; + +RK_U32 enc_refs_debug = 0; + +void _dump_frm(EncFrmStatus *frm, const char *func, RK_S32 line) +{ + if (frm->is_non_ref) { + enc_refs_dbg_frm("%s:%d frm %d %s tid %d non-ref [%x:%d]\n", + func, line, frm->seq_idx, + frm->is_intra ? "intra" : "inter", + frm->temporal_id, frm->ref_mode, frm->ref_arg); + } else if (frm->is_lt_ref) { + enc_refs_dbg_frm("%s:%d frm %d %s tid %d lt-ref [%x:%d] lt_idx %d\n", + func, line, frm->seq_idx, + frm->is_intra ? "intra" : "inter", + frm->temporal_id, frm->ref_mode, frm->ref_arg, + frm->lt_idx); + } else { + enc_refs_dbg_frm("%s:%d frm %d %s tid %d st-ref [%x:%d]\n", + func, line, frm->seq_idx, + frm->is_intra ? "intra" : "inter", + frm->temporal_id, frm->ref_mode, frm->ref_arg); + } +} + +MPP_RET mpp_enc_refs_init(MppEncRefs *refs, MppEncCfgSet *cfg) +{ + if (NULL == refs) { + mpp_err_f("invalid NULL input refs\n"); + return MPP_ERR_NULL_PTR; + } + + enc_refs_dbg_func("enter %p cfg %p\n", refs, cfg); + + MppEncRefsImpl *p = mpp_calloc(MppEncRefsImpl, 1); + *refs = p; + if (NULL == p) { + mpp_err_f("create refs_impl failed\n"); + return MPP_ERR_NULL_PTR; + } + + p->cfg_set = cfg; + + mpp_env_get_u32("enc_refs_debug", &enc_refs_debug, 0); + + enc_refs_dbg_func("leave %p cfg %p\n", p, cfg); + return MPP_OK; +} + +MPP_RET mpp_enc_refs_deinit(MppEncRefs *refs) +{ + if (NULL == refs) { + mpp_err_f("invalid NULL input refs\n"); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p\n", refs); + + MppEncRefsImpl *p = (MppEncRefsImpl *)(*refs); + MPP_FREE(p); + + enc_refs_dbg_func("leave %p\n", refs); + return MPP_OK; +} + +MPP_RET mpp_enc_refs_set_cfg(MppEncRefs refs, MppEncRefCfg ref_cfg) +{ + if (NULL == refs || (ref_cfg && check_is_mpp_enc_ref_cfg(ref_cfg))) { + mpp_err_f("invalid input refs %p ref_cfg %p\n", refs, ref_cfg); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p cfg %p\n", refs, ref_cfg); + + MppEncRefsImpl *p = (MppEncRefsImpl *)refs; + EncVirtualCpb *cpb = &p->cpb; + MppEncRefCfgImpl *cfg = NULL; + + if (NULL == ref_cfg) { + if (p->cfg_set) + ref_cfg = p->cfg_set->ref_cfg; + } + + if (NULL == ref_cfg) + ref_cfg = mpp_enc_ref_default(); + + cfg = (MppEncRefCfgImpl *)ref_cfg; + + p->ref_cfg = cfg; + + if (cfg->lt_cfg_cnt) { + RK_S32 i; + + mpp_assert(cfg->lt_cfg_cnt < MAX_CPB_LT_FRM); + + for (i = 0; i < cfg->lt_cfg_cnt; i++) { + RefsCnt *lt_cnt = &cpb->lt_cnter[i]; + MppEncRefLtFrmCfg *lt_cfg = &cfg->lt_cfg[i]; + + lt_cnt->delay = lt_cfg->lt_delay; + lt_cnt->delay_cnt = lt_cfg->lt_delay; + lt_cnt->len = lt_cfg->lt_gap; + lt_cnt->lt_idx = lt_cfg->lt_idx; + lt_cnt->tid = lt_cfg->temporal_id; + lt_cnt->ref_mode = lt_cfg->ref_mode; + lt_cnt->ref_arg = lt_cfg->ref_arg; + } + } + + MppEncCpbInfo *info = &cpb->info; + memcpy(info, &cfg->cpb_info, sizeof(cfg->cpb_info)); + + enc_refs_dbg_flow("ref_cfg cpb size: lt %d st %d max lt_idx %d tid %d\n", + info->max_lt_cnt, info->max_st_cnt, + info->max_lt_idx, info->max_st_tid); + + enc_refs_dbg_func("leave %p cfg %p\n", refs, ref_cfg); + return MPP_OK; +} + +static void cleanup_cpb_refs(EncVirtualCpb *cpb) +{ + RK_U32 i; + + memset(cpb->cpb_refs, 0, sizeof(cpb->cpb_refs)); + memset(cpb->mode_refs, 0, sizeof(cpb->mode_refs)); + memset(cpb->st_tid_refs, 0, sizeof(cpb->st_tid_refs)); + memset(cpb->lt_idx_refs, 0, sizeof(cpb->lt_idx_refs)); + memset(cpb->list0, 0, sizeof(cpb->list0)); + memset(cpb->list1, 0, sizeof(cpb->list1)); + memset(cpb->cpb_st, 0, sizeof(cpb->cpb_st)); + + cpb->seq_idx = 0; + cpb->seq_cnt++; + cpb->st_cfg_pos = 0; + cpb->st_cfg_repeat_pos = 0; + + for (i = 0; i < MPP_ARRAY_ELEMS(cpb->lt_cnter); i++) { + RefsCnt *lt_cnt = &cpb->lt_cnter[i]; + + lt_cnt->delay_cnt = lt_cnt->delay; + lt_cnt->cnt = 0; + lt_cnt->idx = 0; + } +} + +static void set_st_cfg_to_frm(EncFrmStatus *frm, RK_S32 seq_idx, + MppEncRefStFrmCfg *st_cfg) +{ + memset(frm, 0, sizeof(*frm)); + + frm->seq_idx = seq_idx; + frm->valid = 1; + frm->is_idr = (seq_idx == 0); + frm->is_intra = frm->is_idr; + frm->is_non_ref = st_cfg->is_non_ref; + frm->is_lt_ref = 0; + frm->temporal_id = st_cfg->temporal_id; + frm->ref_mode = st_cfg->ref_mode; + frm->ref_arg = st_cfg->ref_arg; + dump_frm(frm); +} + +static void set_lt_cfg_to_frm(EncFrmStatus *frm, RefsCnt *lt_cfg) +{ + frm->is_non_ref = 0; + frm->is_lt_ref = 1; + frm->temporal_id = lt_cfg->tid; + frm->lt_idx = lt_cfg->lt_idx; + + if (lt_cfg->ref_mode != REF_TO_ST_REF_SETUP) { + frm->ref_mode = lt_cfg->ref_mode; + frm->ref_arg = lt_cfg->ref_arg; + } + dump_frm(frm); +} + +static EncFrmStatus *get_ref_from_cpb(EncVirtualCpb *cpb, EncFrmStatus *frm) +{ + MppEncRefMode ref_mode = frm->ref_mode; + RK_S32 ref_arg = frm->ref_arg; + + if (frm->is_intra) + return NULL; + + EncFrmStatus *ref = NULL; + + /* step 3.1 find seq_idx by mode and arg */ + switch (ref_mode) { + case REF_TO_PREV_REF_FRM : + case REF_TO_PREV_ST_REF : + case REF_TO_PREV_LT_REF : + case REF_TO_PREV_INTRA : { + ref = &cpb->mode_refs[ref_mode]; + } break; + case REF_TO_TEMPORAL_LAYER : { + ref = &cpb->st_tid_refs[ref_arg]; + } break; + case REF_TO_LT_REF_IDX : { + ref = &cpb->lt_idx_refs[ref_arg]; + } break; + case REF_TO_ST_PREV_N_REF : { + ref = &cpb->cpb_refs[ref_arg]; + } break; + case REF_TO_ST_REF_SETUP : + default : { + mpp_err_f("frm %d not supported ref mode 0x%x\n", frm->seq_idx, ref_mode); + } break; + } + + if (ref) { + if (ref->valid) + enc_refs_dbg_flow("frm %d ref mode %d arg %d -> seq %d %s idx %d\n", + frm->seq_idx, ref_mode, ref_arg, ref->seq_idx, + ref->is_lt_ref ? "lt" : "st", + ref->is_lt_ref ? ref->lt_idx : 0); + else + mpp_err_f("frm %d found ref %d but it is invalid\n", + frm->seq_idx, ref->seq_idx); + } else { + ref = NULL; + } + + return ref; +} + +static RK_S32 check_ref_cpb_pos(EncVirtualCpb *cpb, EncFrmStatus *frm) +{ + RK_S32 seq_idx = frm->seq_idx; + RK_S32 found = 0; + RK_S32 pos = -1; + + if (!frm->valid || frm->is_non_ref) { + enc_refs_dbg_flow("frm %d is not valid ref frm\n", seq_idx); + return pos; + } + + if (frm->is_lt_ref) { + /* find same lt_idx */ + for (pos = 0; pos < MAX_CPB_LT_FRM; pos++) { + RK_S32 cpb_idx = pos + MAX_CPB_ST_FRM; + EncFrmStatus *cpb_ref = &cpb->cpb_refs[cpb_idx]; + + if (cpb_ref->valid && cpb_ref->lt_idx == frm->lt_idx) { + pos = cpb_idx; + found = 1; + break; + } + } + } else { + /* search seq_idx in cpb to check the st cpb size */ + for (pos = 0; pos < MAX_CPB_ST_FRM; pos++) { + EncFrmStatus *cpb_ref = &cpb->cpb_refs[pos]; + + enc_refs_dbg_flow("matching ref %d at pos %d %d\n", + seq_idx, pos, cpb_ref->seq_idx); + + if (cpb_ref->valid && cpb_ref->seq_idx == seq_idx) { + enc_refs_dbg_flow("found ref %d at pos %d\n", seq_idx, pos); + found = 1; + break; + } + } + } + + if (!found) { + mpp_err_f("frm %d can NOT be found in st refs!!\n", seq_idx); + pos = -1; + } + + return pos; +} + +static void save_cpb_status(EncVirtualCpb *cpb, EncFrmStatus *refs) +{ + EncFrmStatus *ref = &cpb->cpb_refs[MAX_CPB_ST_FRM]; + MppEncCpbInfo *info = &cpb->info; + RK_S32 lt_ref_cnt = 0; + RK_S32 st_ref_cnt = 0; + RK_S32 ref_cnt = 0; + RK_S32 i; + + /* save lt ref */ + for (i = 0; i < info->max_lt_cnt; i++, ref++) { + if (!ref->valid || ref->is_non_ref || !ref->is_lt_ref) + continue; + + mpp_assert(!ref->is_non_ref); + mpp_assert(ref->is_lt_ref); + mpp_assert(ref->lt_idx >= 0); + + enc_refs_dbg_flow("save lt ref %d to slot %d\n", ref->seq_idx, ref_cnt); + refs[ref_cnt++].val = ref->val; + lt_ref_cnt++; + } + + ref = &cpb->cpb_refs[0]; + /* save st ref */ + for (i = 0; i < info->max_st_cnt; i++, ref++) { + if (!ref->valid || ref->is_non_ref || ref->is_lt_ref) + continue; + + mpp_assert(!ref->is_non_ref); + mpp_assert(!ref->is_lt_ref); + mpp_assert(ref->temporal_id >= 0); + + enc_refs_dbg_flow("save st ref %d to slot %d\n", ref->seq_idx, ref_cnt); + refs[ref_cnt++].val = ref->val; + st_ref_cnt++; + } + + enc_refs_dbg_flow("save ref total %d lt %d st %d\n", ref_cnt, lt_ref_cnt, st_ref_cnt); + for (i = 0; i < ref_cnt; i++) + dump_frm(&refs[i]); +} + +static void store_ref_to_cpb(EncVirtualCpb *cpb, EncFrmStatus *frm) +{ + RK_S32 seq_idx = frm->seq_idx; + RK_S32 lt_idx = frm->lt_idx; + RK_S32 tid = frm->temporal_id; + RK_S32 i; + + mpp_assert(frm->valid); + mpp_assert(lt_idx < MAX_CPB_LT_IDX); + mpp_assert(tid < MAX_CPB_LT_FRM); + + /* non-ref do not save to cpb */ + if (frm->is_non_ref) + return ; + + if (frm->is_intra) + cpb->mode_refs[REF_TO_PREV_INTRA].val = frm->val; + + if (frm->is_lt_ref) { + cpb->lt_idx_refs[lt_idx].val = frm->val; + cpb->mode_refs[REF_TO_PREV_REF_FRM].val = frm->val; + cpb->mode_refs[REF_TO_PREV_LT_REF].val = frm->val; + + RK_S32 found = 0; + EncFrmStatus *cpb_ref = NULL; + + /* find same lt_idx and replace */ + for (i = 0; i < MAX_CPB_LT_FRM; i++) { + RK_S32 cpb_idx = i + MAX_CPB_ST_FRM; + cpb_ref = &cpb->cpb_refs[cpb_idx]; + + if (!cpb_ref->valid) { + found = 1; + break; + } else { + if (cpb_ref->lt_idx == lt_idx) { + found = 2; + break; + } + } + } + + if (found) { + cpb_ref->val = frm->val; + enc_refs_dbg_flow("frm %d with lt idx %d %s to pos %d\n", + seq_idx, lt_idx, (found == 1) ? "add" : "replace", i); + } else { + mpp_err_f("frm %d with lt idx %d found no place to add or relace\n", + seq_idx, lt_idx); + } + } else { + /* do normal st sliding window */ + cpb->st_tid_refs[tid].val = frm->val; + cpb->mode_refs[REF_TO_PREV_REF_FRM].val = frm->val; + cpb->mode_refs[REF_TO_PREV_ST_REF].val = frm->val; + + + for (i = MAX_CPB_ST_FRM - 1; i > 0; i--) + cpb->cpb_refs[i - 1].val = cpb->cpb_refs[i].val; + + cpb->cpb_refs[0].val = frm->val; + + // TODO: Add prev intra valid check? + } + + enc_refs_dbg_flow("dumping cpb refs status start\n"); + for (i = 0; i < MAX_CPB_FRM; i++) { + if (cpb->cpb_refs[i].valid) + dump_frm(&cpb->cpb_refs[i]); + } + enc_refs_dbg_flow("dumping cpb refs status done\n"); +} + +MPP_RET mpp_enc_refs_dryrun(MppEncRefs refs) +{ + if (NULL == refs) { + mpp_err_f("invalid NULL input refs\n"); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p\n", refs); + + MppEncRefsImpl *p = (MppEncRefsImpl *)refs; + MppEncRefCfgImpl *cfg = p->ref_cfg; + MppEncRefStFrmCfg *st_cfg = cfg->st_cfg; + EncVirtualCpb *cpb = &p->cpb; + MppEncCpbInfo *info = &cpb->info; + RK_S32 lt_cfg_cnt = cfg->lt_cfg_cnt; + RK_S32 st_cfg_cnt = cfg->st_cfg_cnt; + RK_S32 cpb_st_used_size = 0; + RK_S32 seq_idx = 0; + RK_S32 st_idx; + + if (cfg->ready) + goto DONE; + + cleanup_cpb_refs(cpb); + + enc_refs_dbg_flow("dryrun start: lt_cfg %d st_cfg %d\n", + lt_cfg_cnt, st_cfg_cnt); + + for (st_idx = 0; st_idx < st_cfg_cnt; st_idx++, st_cfg++) { + EncFrmStatus frm; + RK_S32 repeat = (st_cfg->repeat) ? st_cfg->repeat : 1; + + while (repeat-- > 0) { + /* step 1. updated by st_cfg */ + set_st_cfg_to_frm(&frm, seq_idx++, st_cfg); + + /* step 2. updated by lt_cfg */ + RefsCnt *lt_cfg = &cpb->lt_cnter[0]; + RK_S32 set_to_lt = 0; + RK_S32 i; + + for (i = 0; i < lt_cfg_cnt; i++, lt_cfg++) { + if (lt_cfg->delay_cnt) + lt_cfg->delay_cnt--; + + if (lt_cfg->delay_cnt) + continue; + + if (!set_to_lt) { + if (!lt_cfg->cnt) { + set_lt_cfg_to_frm(&frm, lt_cfg); + set_to_lt = 1; + } + } + + lt_cfg->cnt++; + if (lt_cfg->cnt >= lt_cfg->len) { + lt_cfg->cnt = 0; + lt_cfg->idx++; + } + } + + /* step 3. try find ref by the ref_mode and update used cpb size */ + EncFrmStatus *ref = get_ref_from_cpb(cpb, &frm); + + if (ref) { + RK_S32 cpb_pos = check_ref_cpb_pos(cpb, ref); + + if (cpb_pos < MAX_CPB_ST_FRM) { + if (cpb_st_used_size < cpb_pos + 1) { + cpb_st_used_size = cpb_pos + 1; + enc_refs_dbg_flow("cpb_st_used_size update to %d\n", cpb_st_used_size); + } + } + } + + /* step 4. store frame according to status */ + store_ref_to_cpb(cpb, &frm); + } + } + + cleanup_cpb_refs(cpb); + info->max_st_cnt = cpb_st_used_size; + +DONE: + info->dpb_size = info->max_lt_cnt + info->max_st_cnt; + + enc_refs_dbg_size("dryrun success: cpb size %d\n", info->dpb_size); + + enc_refs_dbg_func("leave %p\n", refs); + return MPP_OK; +} + +MPP_RET mpp_enc_refs_set_usr_cfg(MppEncRefs refs, MppEncRefFrmUsrCfg *cfg) +{ + if (NULL == refs) { + mpp_err_f("invalid NULL input refs\n"); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p\n", refs); + + MppEncRefsImpl *p = (MppEncRefsImpl *)refs; + memcpy(&p->usr_cfg, cfg, sizeof(p->usr_cfg)); + p->usr_cfg_updated = 1; + + enc_refs_dbg_func("leave %p\n", refs); + return MPP_OK; +} + +MPP_RET mpp_enc_refs_get_cpb_info(MppEncRefs refs, MppEncCpbInfo *info) +{ + if (NULL == refs || NULL == info) { + mpp_err_f("invalid input refs %p info %p\n", refs, info); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p\n", refs); + + MppEncRefsImpl *p = (MppEncRefsImpl *)refs; + memcpy(info, &p->cpb.info, sizeof(*info)); + + enc_refs_dbg_func("leave %p\n", refs); + return MPP_OK; +} + +MPP_RET mpp_enc_refs_get_cpb(MppEncRefs refs, EncCpbStatus *status) +{ + if (NULL == refs) { + mpp_err_f("invalid NULL input refs\n"); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p\n", refs); + + MppEncRefsImpl *p = (MppEncRefsImpl *)refs; + MppEncCfgSet *cfg_set = p->cfg_set; + MppEncRefCfgImpl *cfg = p->ref_cfg; + EncVirtualCpb *cpb = &p->cpb; + MppEncRefStFrmCfg *st_cfg = &cfg->st_cfg[cpb->st_cfg_pos]; + EncFrmStatus *frm = &status->curr; + EncFrmStatus *ref = &status->refr; + RefsCnt *lt_cfg = cpb->lt_cnter; + RK_S32 set_to_lt = 0; + RK_S32 i; + + /* step 1. check igop from cfg_set */ + if (cfg_set->rc.gop != p->igop) { + p->igop = cfg_set->rc.gop; + cleanup_cpb_refs(cpb); + } if (p->igop && cpb->seq_idx >= p->igop) { + /* update seq_idx for igop loop */ + cleanup_cpb_refs(cpb); + } + + cpb->frm_idx++; + /* step 2. updated by st_cfg */ + set_st_cfg_to_frm(frm, cpb->seq_idx++, st_cfg); + + /* update st_cfg for st_cfg loop */ + cpb->st_cfg_repeat_pos++; + if (cpb->st_cfg_repeat_pos > st_cfg->repeat) { + cpb->st_cfg_repeat_pos = 0; + cpb->st_cfg_pos++; + + /* NOTE: second loop will start from 1 */ + if (cpb->st_cfg_pos >= cfg->st_cfg_cnt) { + if (cfg->st_cfg_cnt > 1) + cpb->st_cfg_pos = 1; + else + cpb->st_cfg_pos = 0; + } + } + + lt_cfg = p->cpb.lt_cnter; + + /* step 3. updated by lt_cfg */ + for (i = 0; i < cfg->lt_cfg_cnt; i++, lt_cfg++) { + if (lt_cfg->delay_cnt) + lt_cfg->delay_cnt--; + + if (lt_cfg->delay_cnt) + continue; + + if (!set_to_lt) { + if (!lt_cfg->cnt) { + set_lt_cfg_to_frm(frm, lt_cfg); + set_to_lt = 1; + } + } + + lt_cfg->cnt++; + if (lt_cfg->cnt >= lt_cfg->len) { + lt_cfg->cnt = 0; + lt_cfg->idx++; + } + } + + /* step 4. try find ref by the ref_mode */ + EncFrmStatus *ref_found = get_ref_from_cpb(&p->cpb, frm); + if (ref_found) { + RK_S32 cpb_idx = check_ref_cpb_pos(&p->cpb, ref_found); + + mpp_assert(cpb_idx >= 0); + cpb->list0[0].val = ref->val; + ref->val = ref_found->val; + } else + ref->val = 0; + + /* step 5. generate cpb init */ + memset(status->init, 0, sizeof(status->init)); + save_cpb_status(&p->cpb, status->init); + // TODO: cpb_init must be the same to cpb_final + + /* step 6. store frame according to status */ + store_ref_to_cpb(&p->cpb, frm); + + /* step 7. generate cpb final */ + memset(status->final, 0, sizeof(status->final)); + save_cpb_status(&p->cpb, status->final); + + enc_refs_dbg_func("leave %p\n", refs); + return MPP_OK; +} + +MPP_RET mpp_enc_refs_stash(MppEncRefs refs) +{ + if (NULL == refs) { + mpp_err_f("invalid NULL input refs\n"); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p\n", refs); + + MppEncRefsImpl *p = (MppEncRefsImpl *)refs; + memcpy(&p->cpb_stash, &p->cpb, sizeof(p->cpb_stash)); + + enc_refs_dbg_func("leave %p\n", refs); + return MPP_OK; +} + +MPP_RET mpp_enc_refs_rollback(MppEncRefs refs) +{ + if (NULL == refs) { + mpp_err_f("invalid NULL input refs\n"); + return MPP_ERR_VALUE; + } + + enc_refs_dbg_func("enter %p\n", refs); + + MppEncRefsImpl *p = (MppEncRefsImpl *)refs; + memcpy(&p->cpb, &p->cpb_stash, sizeof(p->cpb)); + + enc_refs_dbg_func("leave %p\n", refs); + return MPP_OK; +} diff --git a/mpp/base/test/CMakeLists.txt b/mpp/base/test/CMakeLists.txt index 92cf96fe..be270ab4 100644 --- a/mpp/base/test/CMakeLists.txt +++ b/mpp/base/test/CMakeLists.txt @@ -36,3 +36,6 @@ add_mpp_base_test(mpp_trie) # mpp_enc_cfg unit test add_mpp_base_test(mpp_enc_cfg) + +# mpp_enc_ref unit test +add_mpp_base_test(mpp_enc_ref) diff --git a/mpp/base/test/mpp_enc_ref_test.c b/mpp/base/test/mpp_enc_ref_test.c new file mode 100644 index 00000000..087740c1 --- /dev/null +++ b/mpp/base/test/mpp_enc_ref_test.c @@ -0,0 +1,176 @@ +/* + * 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. + */ + +#define MODULE_TAG "mpp_enc_ref_test" + +#include + +#include "mpp_log.h" + +#include "rk_venc_ref.h" + +int main() +{ + MPP_RET ret = MPP_OK; + RK_S32 lt_cnt = 0; + RK_S32 st_cnt = 0; + MppEncRefCfg ref = NULL; + MppEncRefLtFrmCfg lt_ref[4]; + MppEncRefStFrmCfg st_ref[16]; + + memset(<_ref, 0, sizeof(lt_ref)); + memset(&st_ref, 0, sizeof(st_ref)); + + mpp_log("mpp_enc_ref_test start\n"); + + ret = mpp_enc_ref_cfg_init(&ref); + + mpp_log("mpp_enc_ref_test tsvc4 ref info generation start\n"); + + lt_cnt = 1; + st_cnt = 9; + + ret = mpp_enc_ref_cfg_set_cfg_cnt(ref, lt_cnt, st_cnt); + + /* set 8 frame lt-ref gap */ + lt_ref[0].lt_idx = 0; + lt_ref[0].temporal_id = 0; + lt_ref[0].ref_mode = REF_TO_PREV_LT_REF; + lt_ref[0].lt_gap = 8; + lt_ref[0].lt_delay = 0; + + ret = mpp_enc_ref_cfg_add_lt_cfg(ref, 1, lt_ref); + + /* set tsvc4 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_PREV_REF_FRM; + 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_TEMPORAL_LAYER; + st_ref[8].ref_arg = 0; + st_ref[8].repeat = 0; + + ret = mpp_enc_ref_cfg_add_st_cfg(ref, 9, st_ref); + + /* check and get dpb size */ + mpp_log("mpp_enc_ref_test tsvc4 ref info verification start\n"); + ret = mpp_enc_ref_cfg_check(ref); + mpp_log("mpp_enc_ref_test tsvc4 ref info verification ret %d\n", ret); + + ret = mpp_enc_ref_cfg_show(ref); + + /* reset for next config */ + ret = mpp_enc_ref_cfg_reset(ref); + +#if 0 + mpp_log("mpp_enc_ref_test smartp ref info generation start\n"); + + /* typical smartp config */ + lt_cnt = 1; + st_cnt = 3; + + ret = mpp_enc_ref_cfg_set_cfg_cnt(ref, lt_cnt, st_cnt); + + memset(<_ref, 0, sizeof(lt_ref)); + memset(&st_ref, 0, sizeof(st_ref)); + + /* set 300 frame lt-ref gap */ + lt_ref[0].lt_idx = 0; + lt_ref[0].temporal_id = 0; + lt_ref[0].ref_mode = REF_TO_PREV_INTRA; + lt_ref[0].lt_gap = 300; + lt_ref[0].lt_delay = 0; + + ret = mpp_enc_ref_cfg_add_lt_cfg(ref, 1, lt_ref); + + st_ref[0].is_non_ref = 0; + st_ref[0].temporal_id = 0; + st_ref[0].ref_mode = REF_TO_PREV_LT_REF; + st_ref[0].ref_arg = 0; + st_ref[0].repeat = 0; + + st_ref[1].is_non_ref = 0; + st_ref[1].temporal_id = 0; + st_ref[1].ref_mode = REF_TO_PREV_REF_FRM; + st_ref[1].ref_arg = 0; + st_ref[1].repeat = 299; + + st_ref[2].is_non_ref = 0; + st_ref[2].temporal_id = 0; + st_ref[2].ref_mode = REF_TO_PREV_LT_REF; + st_ref[2].ref_arg = 0; + st_ref[2].repeat = 0; + + ret = mpp_enc_ref_cfg_add_st_cfg(ref, 3, st_ref); + + mpp_log("mpp_enc_ref_test smartp ref info verification start\n"); + ret = mpp_enc_ref_cfg_check(ref); + mpp_log("mpp_enc_ref_test smartp ref info verification ret %d\n", ret); + + ret = mpp_enc_ref_cfg_deinit(&ref); +#endif + + mpp_log("mpp_enc_ref_test %s\n", ret ? "failed" : "success"); + + return ret; +} diff --git a/mpp/inc/mpp_enc_cfg.h b/mpp/inc/mpp_enc_cfg.h index 580c5803..6642109d 100644 --- a/mpp/inc/mpp_enc_cfg.h +++ b/mpp/inc/mpp_enc_cfg.h @@ -18,6 +18,7 @@ #define __MPP_ENC_CFG_H__ #include "rk_venc_cmd.h" +#include "rk_venc_ref.h" #include "rc_data.h" /* @@ -36,6 +37,7 @@ typedef struct MppEncCfgSet_t { MppEncCodecCfg codec; MppEncSliceSplit split; + MppEncRefCfg ref_cfg; MppEncROICfg roi; MppEncOSDPltCfg plt_cfg; MppEncOSDPlt plt_data;