/*
|
* 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 <string.h>
|
|
#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;
|
}
|
|
if (cfg->repeat < 0) {
|
mpp_err_f("ref cfg %p st cfg %d with negative repeat %d set to zero\n",
|
ref, pos, cfg->repeat);
|
cfg->repeat = 0;
|
}
|
|
/* constrain on head and tail frame */
|
if (pos == 0 || (pos == st_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);
|
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_set_keep_cpb(MppEncRefCfg ref, RK_S32 keep)
|
{
|
if (check_is_mpp_enc_ref_cfg(ref))
|
return MPP_ERR_VALUE;
|
|
MppEncRefCfgImpl *p = (MppEncRefCfgImpl *)ref;
|
p->keep_cpb = keep;
|
|
return MPP_OK;
|
}
|
|
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,
|
.keep_cpb = 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;
|
}
|