/*
|
* Copyright 2016 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 "rc_model_v2_smt"
|
|
#include <string.h>
|
#include <math.h>
|
|
#include "mpp_env.h"
|
#include "mpp_mem.h"
|
#include "mpp_common.h"
|
|
#include "rc_base.h"
|
#include "rc_debug.h"
|
#include "rc_model_v2_smt.h"
|
#include "mpp_rc.h"
|
|
#define MAD_THDI 20
|
#define LIMIT_QP_MORE_MOVE 30
|
#define ENOUGH_QP 33
|
#define LOW_QP 34
|
#define LOW_QP_level0 34
|
#define LOW_QP_level1 34
|
#define LOW_LOW_QP 38
|
#define LOW_PRE_DIFF_BIT_USE -20000
|
|
extern RK_S32 tab_lnx[64];
|
|
typedef struct RcModelV2SmtCtx_t {
|
RcCfg usr_cfg;
|
EncRcTaskInfo hal_cfg;
|
RK_U32 frame_type;
|
RK_U32 last_frame_type;
|
RK_S64 gop_total_bits;
|
RK_U32 bit_per_frame;
|
MppDataV2 *i_bit;
|
RK_U32 i_sumbits;
|
RK_U32 i_scale;
|
MppDataV2 *idr_bit;
|
RK_U32 idr_sumbits;
|
RK_U32 idr_scale;
|
MppDataV2 *p_bit;
|
RK_U32 p_sumbits;
|
RK_U32 p_scale;
|
MppDataV2 *pre_p_bit;
|
RK_S32 target_bps;
|
RK_S32 pre_target_bits;
|
RK_S32 pre_real_bits;
|
RK_S32 frm_bits_thr;
|
RK_S32 ins_bps;
|
RK_S32 last_inst_bps;
|
RK_U32 water_level_thr;
|
MppDataV2 *stat_bits;
|
MppDataV2 *stat_rate;
|
RK_S32 stat_watl_thrd;
|
RK_S32 stat_watl;
|
RK_S32 stat_last_watl;
|
RK_S32 next_i_ratio; // scale 64
|
RK_S32 next_ratio; // scale 64
|
RK_S32 pre_i_qp;
|
RK_S32 pre_p_qp;
|
RK_S32 scale_qp; // scale 64
|
MppDataV2 *means_qp;
|
RK_S64 frm_num;
|
RK_S32 reenc_cnt;
|
RK_S32 codec_type; // 264: 0 ; 265: 1
|
RK_S32 qp_min;
|
RK_S32 qp_max;
|
RK_S32 qp_step;
|
MppEncGopMode gop_mode;
|
RK_S32 window_len;
|
RK_S32 intra_to_inter_rate;
|
RK_S32 acc_intra_bits_in_fps;
|
RK_S32 acc_inter_bits_in_fps;
|
RK_S32 acc_total_bits;
|
RK_S64 acc_total_count;
|
RK_S64 acc_intra_count;
|
RK_S64 acc_inter_count;
|
RK_S32 last_fps_bits;
|
RK_S32 pre_gop_left_bit;
|
MppData *qp_p;
|
MppData *sse_p;
|
MppData *intra;
|
MppData *inter;
|
MppData *gop_bits;
|
MppData *intra_percent;
|
MppPIDCtx pid_fps;
|
RK_S32 bps_target_low_rate;
|
RK_S32 bps_target_high_rate;
|
RK_S32 bits_target_low_rate;
|
RK_S32 bits_target_high_rate;
|
RK_S32 bits_per_pic_low_rate;
|
RK_S32 bits_per_intra_low_rate;
|
RK_S32 bits_per_inter_low_rate;
|
RK_S32 bits_per_pic_high_rate;
|
RK_S32 bits_per_intra_high_rate;
|
RK_S32 bits_per_inter_high_rate;
|
RK_S32 pre_diff_bit_low_rate;
|
RK_S32 pre_diff_bit_high_rate;
|
RK_S32 gop_min;
|
MppPIDCtx pid_intra_low_rate;
|
MppPIDCtx pid_intra_high_rate;
|
MppPIDCtx pid_inter_low_rate;
|
MppPIDCtx pid_inter_high_rate;
|
RK_S32 bits_one_gop[1000];
|
RK_S32 bits_one_gop_use_flag;
|
RK_S32 bits_one_gop_sum;
|
RK_S32 delta_bits_per_frame;
|
RK_S32 frame_cnt_in_gop;
|
RK_S32 bits_target_use;
|
RK_S32 qp_out;
|
RK_S32 qp_prev_out;
|
RK_S32 qp_preavg;
|
RK_S32 intra_prerealbit;
|
RK_S32 intra_preqp;
|
RK_S32 intra_presse;
|
RK_S32 intra_premadi;
|
RK_U32 st_madi;
|
} RcModelV2SmtCtx;
|
|
typedef struct InfoList_t {
|
RK_U16 flag; // 1 - valid 0 - unvaild
|
RK_U16 up_left[2]; // 0 - y idx 1 - x idx
|
RK_U16 down_right[2]; // 0 - y idx 1 - x idx
|
} InfoList;
|
|
typedef struct RoiInfo_t {
|
RK_U16 flag; // 1 - valid 0 - unvaild
|
RK_U16 is_move; // 1 - is motion 0 - is motionless
|
RK_U16 up_left[2]; // 0 - y idx 1 - x idx
|
RK_U16 down_right[2]; // 0 - y idx 1 - x idx
|
} RoiInfo;
|
|
static RK_U32 cal_mv_info(InfoList* info)
|
{
|
RK_S32 k, i, j;
|
RK_S32 mb_sta_x, mb_sta_y;
|
RK_S32 mb_end_x, mb_end_y;
|
RK_U32 move_num = 0;
|
|
if (info == NULL)
|
return 0;
|
|
for (k = 0; k < 4096; k++) {
|
if (info[k].flag == 0)
|
break;
|
|
mb_sta_y = info[k].up_left[0] / 16;
|
mb_sta_x = info[k].up_left[1] / 16;
|
mb_end_y = info[k].down_right[0] / 16;
|
mb_end_x = info[k].down_right[1] / 16;
|
|
for (j = mb_sta_y; j <= mb_end_y; j++) {
|
for (i = mb_sta_x; i <= mb_end_x; i++) {
|
|
move_num++;
|
}
|
}
|
}
|
|
return move_num;
|
}
|
|
MPP_RET bits_model_smt_deinit(RcModelV2SmtCtx *ctx)
|
{
|
rc_dbg_func("enter %p\n", ctx);
|
|
if (ctx->qp_p) {
|
mpp_data_deinit(ctx->qp_p);
|
ctx->qp_p = NULL;
|
}
|
|
if (ctx->sse_p) {
|
mpp_data_deinit(ctx->sse_p);
|
ctx->sse_p = NULL;
|
}
|
|
if (ctx->intra) {
|
mpp_data_deinit(ctx->intra);
|
ctx->intra = NULL;
|
}
|
|
if (ctx->inter) {
|
mpp_data_deinit(ctx->inter);
|
ctx->inter = NULL;
|
}
|
|
if (ctx->gop_bits) {
|
mpp_data_deinit(ctx->gop_bits);
|
ctx->gop_bits = NULL;
|
}
|
|
if (ctx->intra_percent) {
|
mpp_data_deinit(ctx->intra_percent);
|
ctx->intra_percent = NULL;
|
}
|
|
|
if (ctx->i_bit != NULL) {
|
mpp_data_deinit_v2(ctx->i_bit);
|
ctx->i_bit = NULL;
|
}
|
|
if (ctx->p_bit != NULL) {
|
mpp_data_deinit_v2(ctx->p_bit);
|
ctx->p_bit = NULL;
|
}
|
|
if (ctx->pre_p_bit != NULL) {
|
mpp_data_deinit_v2(ctx->pre_p_bit);
|
ctx->pre_p_bit = NULL;
|
}
|
|
if (ctx->stat_rate != NULL) {
|
mpp_data_deinit_v2(ctx->stat_rate);
|
ctx->stat_rate = NULL;
|
}
|
|
if (ctx->stat_bits != NULL) {
|
mpp_data_deinit_v2(ctx->stat_bits);
|
ctx->stat_bits = NULL;
|
}
|
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET bits_model_smt_init(RcModelV2SmtCtx *ctx)
|
{
|
RK_S32 gop_len = ctx->usr_cfg.igop;
|
RcFpsCfg *fps = &ctx->usr_cfg.fps;
|
|
rc_dbg_func("enter %p\n", ctx);
|
ctx->frm_num = 0;
|
|
// smt
|
ctx->frame_cnt_in_gop = 0;
|
ctx->bits_one_gop_use_flag = 0;
|
ctx->gop_min = gop_len;
|
|
ctx->qp_min = 18;
|
ctx->qp_max = 51;
|
ctx->qp_step = 4;
|
|
if (gop_len < fps->fps_out_num)
|
ctx->window_len = fps->fps_out_num;
|
else
|
ctx->window_len = gop_len;
|
|
if (ctx->window_len < 10)
|
ctx->window_len = 10;
|
|
if (ctx->window_len > fps->fps_out_num)
|
ctx->window_len = fps->fps_out_num;
|
|
if (ctx->intra)
|
mpp_data_deinit(ctx->intra);
|
mpp_data_init(&ctx->intra, gop_len);
|
|
if (ctx->inter)
|
mpp_data_deinit(ctx->inter);
|
mpp_data_init(&ctx->inter, fps->fps_out_num); /* need test */
|
|
if (ctx->gop_bits)
|
mpp_data_deinit(ctx->gop_bits);
|
mpp_data_init(&ctx->gop_bits, gop_len);
|
|
if (ctx->intra_percent)
|
mpp_data_deinit(ctx->intra_percent);
|
mpp_data_init(&ctx->intra_percent, gop_len);
|
|
mpp_pid_reset(&ctx->pid_fps);
|
mpp_pid_reset(&ctx->pid_intra_low_rate);
|
mpp_pid_reset(&ctx->pid_intra_high_rate);
|
mpp_pid_reset(&ctx->pid_inter_low_rate);
|
mpp_pid_reset(&ctx->pid_inter_high_rate);
|
|
mpp_pid_set_param(&ctx->pid_fps, 4, 6, 0, 100, ctx->window_len);
|
mpp_pid_set_param(&ctx->pid_intra_low_rate, 4, 6, 0, 100, ctx->window_len);
|
mpp_pid_set_param(&ctx->pid_intra_high_rate, 4, 6, 0, 100, ctx->window_len);
|
mpp_pid_set_param(&ctx->pid_inter_low_rate, 4, 6, 0, 100, ctx->window_len);
|
mpp_pid_set_param(&ctx->pid_inter_high_rate, 4, 6, 0, 100, ctx->window_len);
|
|
ctx->bps_target_low_rate = ctx->usr_cfg.bps_min;
|
ctx->bps_target_high_rate = ctx->usr_cfg.bps_max;
|
ctx->bits_per_pic_low_rate = axb_div_c(ctx->bps_target_low_rate, fps->fps_out_denorm, fps->fps_out_num);
|
ctx->bits_per_pic_high_rate = axb_div_c(ctx->bps_target_high_rate, fps->fps_out_denorm, fps->fps_out_num);
|
|
ctx->acc_intra_bits_in_fps = 0;
|
ctx->acc_inter_bits_in_fps = 0;
|
ctx->acc_total_bits = 0;
|
ctx->acc_intra_count = 0;
|
ctx->acc_inter_count = 0;
|
ctx->last_fps_bits = 0;
|
|
RK_S32 avg_low_rate = ctx->bits_per_pic_low_rate;
|
RK_S32 avg_high_rate = ctx->bits_per_pic_high_rate;
|
|
if (gop_len == 0) {
|
ctx->gop_mode = MPP_GOP_ALL_INTER;
|
ctx->bits_per_inter_low_rate = avg_low_rate;
|
ctx->bits_per_intra_low_rate = avg_low_rate * 10;
|
ctx->bits_per_inter_high_rate = avg_high_rate;
|
ctx->bits_per_intra_high_rate = avg_high_rate * 10;
|
} else if (gop_len == 1) {
|
ctx->gop_mode = MPP_GOP_ALL_INTRA;
|
ctx->bits_per_inter_low_rate = 0;
|
ctx->bits_per_intra_low_rate = avg_low_rate;
|
ctx->bits_per_inter_high_rate = 0;
|
ctx->bits_per_intra_high_rate = avg_high_rate;
|
ctx->intra_to_inter_rate = 0;
|
} else if (gop_len < ctx->window_len) {
|
ctx->gop_mode = MPP_GOP_SMALL;
|
ctx->intra_to_inter_rate = gop_len + 1;
|
|
ctx->bits_per_inter_low_rate = avg_low_rate / 2;
|
ctx->bits_per_intra_low_rate = ctx->bits_per_inter_low_rate * ctx->intra_to_inter_rate;
|
ctx->bits_per_inter_high_rate = avg_high_rate / 2;
|
ctx->bits_per_intra_high_rate = ctx->bits_per_inter_high_rate * ctx->intra_to_inter_rate;
|
} else {
|
ctx->gop_mode = MPP_GOP_LARGE;
|
ctx->intra_to_inter_rate = gop_len + 1;
|
|
ctx->bits_per_intra_low_rate = ctx->bits_per_pic_low_rate * (2.0 * log((float)gop_len));
|
ctx->bits_per_inter_low_rate = ctx->bits_per_pic_low_rate;
|
ctx->bits_per_inter_low_rate -= ctx->bits_per_intra_low_rate / (fps->fps_out_num - 1);
|
|
ctx->bits_per_intra_high_rate = ctx->bits_per_pic_high_rate * (2.0 * log((float)gop_len));
|
ctx->bits_per_inter_high_rate = ctx->bits_per_pic_high_rate;
|
ctx->bits_per_inter_high_rate -= ctx->bits_per_intra_high_rate / (fps->fps_out_num - 1);
|
}
|
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET bits_model_update_smt(RcModelV2SmtCtx *ctx, RK_S32 real_bit)
|
{
|
rc_dbg_func("enter %p\n", ctx);
|
// smt
|
RK_S32 gop_len = ctx->usr_cfg.igop;
|
RcFpsCfg *fps = &ctx->usr_cfg.fps;
|
|
ctx->pre_diff_bit_low_rate = ctx->bits_target_low_rate - real_bit;
|
ctx->pre_diff_bit_high_rate = ctx->bits_target_high_rate - real_bit;
|
ctx->bits_one_gop[ctx->frame_cnt_in_gop % 1000] = real_bit;
|
ctx->frame_cnt_in_gop++;
|
|
if (ctx->frame_cnt_in_gop == gop_len) {
|
ctx->frame_cnt_in_gop = 0;
|
ctx->bits_one_gop_use_flag = 1;
|
ctx->bits_one_gop_sum = 0;
|
RK_S32 i = 0;
|
RK_S32 gop_len_save = gop_len;
|
if (gop_len > 1000) {
|
gop_len_save = 1000;
|
}
|
for (i = 0; i < gop_len_save; i++)
|
ctx->bits_one_gop_sum += ctx->bits_one_gop[i];
|
|
ctx->delta_bits_per_frame = ctx->bps_target_high_rate / (fps->fps_out_num) - ctx->bits_one_gop_sum / gop_len_save;
|
}
|
|
if (ctx->frame_type == INTRA_FRAME) {
|
ctx->acc_intra_count++;
|
ctx->acc_intra_bits_in_fps += real_bit;
|
mpp_data_update(ctx->intra, real_bit);
|
mpp_data_update(ctx->gop_bits, real_bit);
|
mpp_pid_update(&ctx->pid_intra_low_rate, real_bit - ctx->bits_target_low_rate);
|
mpp_pid_update(&ctx->pid_intra_high_rate, real_bit - ctx->bits_target_high_rate);
|
} else {
|
ctx->acc_inter_count++;
|
ctx->acc_inter_bits_in_fps += real_bit;
|
mpp_data_update(ctx->inter, real_bit);
|
mpp_data_update(ctx->gop_bits, real_bit);
|
mpp_pid_update(&ctx->pid_inter_low_rate, real_bit - ctx->bits_target_low_rate);
|
mpp_pid_update(&ctx->pid_inter_high_rate, real_bit - ctx->bits_target_high_rate);
|
}
|
|
ctx->acc_total_count++;
|
ctx->last_fps_bits += real_bit;
|
/* new fps start */
|
if ((ctx->acc_intra_count + ctx->acc_inter_count) % fps->fps_out_num == 0) {
|
RK_S32 bps_target_temp = (ctx->bps_target_low_rate + ctx->bps_target_high_rate) / 2;
|
if (bps_target_temp > (ctx->last_fps_bits * 2 / 3))
|
mpp_pid_update(&ctx->pid_fps, bps_target_temp - ctx->last_fps_bits);
|
else {
|
bps_target_temp = ctx->bps_target_low_rate * 0.4 + ctx->bps_target_high_rate * 0.6;
|
mpp_pid_update(&ctx->pid_fps, bps_target_temp - ctx->last_fps_bits);
|
}
|
ctx->acc_intra_bits_in_fps = 0;
|
ctx->acc_inter_bits_in_fps = 0;
|
ctx->last_fps_bits = 0;
|
}
|
|
/* new frame start */
|
ctx->qp_prev_out = ctx->qp_out;
|
|
rc_dbg_func("leave %p\n", ctx);
|
|
return MPP_OK;
|
}
|
|
MPP_RET reenc_calc_cbr_ratio_smt(RcModelV2SmtCtx *ctx, EncRcTaskInfo *cfg)
|
{
|
RK_S32 stat_time = ctx->usr_cfg.stats_time;
|
RK_S32 last_ins_bps = mpp_data_sum_v2(ctx->stat_bits) / stat_time;
|
RK_S32 ins_bps = (last_ins_bps * stat_time - mpp_data_get_pre_val_v2(ctx->stat_bits, -1) + cfg->bit_real) / stat_time;
|
RK_S32 real_bit = cfg->bit_real;
|
RK_S32 target_bit = cfg->bit_target;
|
RK_S32 target_bps = ctx->target_bps;
|
RK_S32 water_level = 0;
|
RK_S32 idx1, idx2;
|
RK_S32 i_flag = 0;
|
RK_S32 bit_diff_ratio, ins_ratio, bps_ratio, wl_ratio;
|
|
rc_dbg_func("enter %p\n", ctx);
|
|
i_flag = (ctx->frame_type == INTRA_FRAME);
|
|
if (real_bit + ctx->stat_watl > ctx->stat_watl_thrd)
|
water_level = ctx->stat_watl_thrd - ctx->bit_per_frame;
|
else
|
water_level = real_bit + ctx->stat_watl_thrd - ctx->bit_per_frame;
|
|
if (water_level < ctx->stat_last_watl) {
|
water_level = ctx->stat_last_watl;
|
}
|
|
if (target_bit > real_bit)
|
bit_diff_ratio = 32 * (real_bit - target_bit) / target_bit;
|
else
|
bit_diff_ratio = 32 * (real_bit - target_bit) / real_bit;
|
|
idx1 = ins_bps / (target_bps >> 5);
|
idx2 = last_ins_bps / (target_bps >> 5);
|
|
idx1 = mpp_clip(idx1, 0, 63);
|
idx2 = mpp_clip(idx2, 0, 63);
|
ins_ratio = tab_lnx[idx1] - tab_lnx[idx2];
|
|
bps_ratio = 96 * (ins_bps - target_bps) / target_bps;
|
wl_ratio = 32 * (water_level - (ctx->water_level_thr >> 3)) / (ctx->water_level_thr >> 3);
|
if (last_ins_bps < ins_bps && target_bps != last_ins_bps) {
|
ins_ratio = 6 * ins_ratio;
|
ins_ratio = mpp_clip(ins_ratio, -192, 256);
|
} else {
|
if (i_flag) {
|
ins_ratio = 3 * ins_ratio;
|
ins_ratio = mpp_clip(ins_ratio, -192, 256);
|
} else {
|
ins_ratio = 0;
|
}
|
}
|
if (bit_diff_ratio >= 256)
|
bit_diff_ratio = 256;
|
|
if (bps_ratio >= 32)
|
bps_ratio = 32;
|
|
if (wl_ratio >= 32)
|
wl_ratio = 32;
|
|
if (bit_diff_ratio < -128)
|
ins_ratio = ins_ratio - 128;
|
else
|
ins_ratio = ins_ratio + bit_diff_ratio;
|
|
if (bps_ratio < -32)
|
ins_ratio = ins_ratio - 32;
|
else
|
ins_ratio = ins_ratio + bps_ratio;
|
|
if (wl_ratio < -32)
|
wl_ratio = ins_ratio - 32;
|
else
|
wl_ratio = ins_ratio + wl_ratio;
|
|
ctx->next_ratio = wl_ratio;
|
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET reenc_calc_vbr_ratio_smt(RcModelV2SmtCtx *ctx, EncRcTaskInfo *cfg)
|
{
|
RK_S32 stat_time = ctx->usr_cfg.stats_time;
|
RK_S32 last_ins_bps = mpp_data_sum_v2(ctx->stat_bits) / stat_time;
|
RK_S32 ins_bps = (last_ins_bps * stat_time - mpp_data_get_pre_val_v2(ctx->stat_bits, -1)
|
+ cfg->bit_real) / stat_time;
|
RK_S32 bps_change = ctx->target_bps;
|
RK_S32 max_bps_target = ctx->usr_cfg.bps_max;
|
RK_S32 real_bit = cfg->bit_real;
|
RK_S32 target_bit = cfg->bit_target;
|
RK_S32 idx1, idx2;
|
RK_S32 bit_diff_ratio, ins_ratio, bps_ratio;
|
|
rc_dbg_func("enter %p\n", ctx);
|
|
if (target_bit <= real_bit)
|
bit_diff_ratio = 32 * (real_bit - target_bit) / target_bit;
|
else
|
bit_diff_ratio = 32 * (real_bit - target_bit) / real_bit;
|
|
idx1 = ins_bps / (max_bps_target >> 5);
|
idx2 = last_ins_bps / (max_bps_target >> 5);
|
idx1 = mpp_clip(idx1, 0, 63);
|
idx2 = mpp_clip(idx2, 0, 63);
|
if (last_ins_bps < ins_bps && bps_change < ins_bps) {
|
ins_ratio = 6 * (tab_lnx[idx1] - tab_lnx[idx2]);
|
ins_ratio = mpp_clip(ins_ratio, -192, 256);
|
} else {
|
ins_ratio = 0;
|
}
|
|
bps_ratio = 96 * (ins_bps - bps_change) / bps_change;
|
|
if (bit_diff_ratio >= 256)
|
bit_diff_ratio = 256;
|
if (bps_ratio >= 32)
|
bps_ratio = 32;
|
|
if (bit_diff_ratio < -128)
|
ins_ratio = ins_ratio - 128;
|
else
|
ins_ratio = bit_diff_ratio + ins_ratio;
|
|
if (bps_ratio < -32)
|
ins_ratio = ins_ratio - 32;
|
else
|
ins_ratio = ins_ratio + bps_ratio;
|
|
ctx->next_ratio = ins_ratio;
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET check_re_enc_smt(RcModelV2SmtCtx *ctx, EncRcTaskInfo *cfg)
|
{
|
return MPP_OK;
|
|
RK_S32 frame_type = ctx->frame_type;
|
RK_S32 i_flag = 0;
|
RK_S32 big_flag = 0;
|
RK_S32 stat_time = ctx->usr_cfg.stats_time;
|
RK_S32 last_ins_bps = mpp_data_sum_v2(ctx->stat_bits) / stat_time;
|
RK_S32 ins_bps = (last_ins_bps * stat_time - mpp_data_get_pre_val_v2(ctx->stat_bits, -1)
|
+ cfg->bit_real) / stat_time;
|
RK_S32 target_bps;
|
RK_S32 flag1 = 0;
|
RK_S32 flag2 = 0;
|
|
rc_dbg_func("enter %p\n", ctx);
|
|
if (ctx->usr_cfg.mode == RC_CBR)
|
target_bps = ctx->usr_cfg.bps_target;
|
else
|
target_bps = ctx->usr_cfg.bps_max;
|
|
if (ctx->reenc_cnt >= ctx->usr_cfg.max_reencode_times) {
|
return MPP_OK;
|
}
|
|
i_flag = (frame_type == INTRA_FRAME);
|
if (!i_flag && cfg->bit_real > 3 * cfg->bit_target) {
|
big_flag = 1;
|
}
|
|
if (i_flag && cfg->bit_real > 3 * cfg->bit_target / 2) {
|
big_flag = 1;
|
}
|
|
if (ctx->usr_cfg.mode == RC_CBR) {
|
flag1 = target_bps / 20 < ins_bps - last_ins_bps;
|
if (target_bps + target_bps / 10 < ins_bps ||
|
target_bps - target_bps / 10 > ins_bps) {
|
flag2 = 1;
|
}
|
} else {
|
flag1 = target_bps - (target_bps >> 3) < ins_bps;
|
flag2 = target_bps / 20 < ins_bps - last_ins_bps;
|
}
|
|
if (!(big_flag && flag1 && flag2)) {
|
return MPP_OK;
|
}
|
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
|
MPP_RET rc_model_v2_smt_h265_init(void *ctx, RcCfg *cfg)
|
{
|
RcModelV2SmtCtx *p = (RcModelV2SmtCtx*)ctx;
|
|
rc_dbg_func("enter %p\n", ctx);
|
|
p->codec_type = 1;
|
memcpy(&p->usr_cfg, cfg, sizeof(RcCfg));
|
bits_model_smt_init(p);
|
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET rc_model_v2_smt_h264_init(void *ctx, RcCfg *cfg)
|
{
|
RcModelV2SmtCtx *p = (RcModelV2SmtCtx*)ctx;
|
|
rc_dbg_func("enter %p\n", ctx);
|
|
p->codec_type = 0;
|
memcpy(&p->usr_cfg, cfg, sizeof(RcCfg));
|
bits_model_smt_init(p);
|
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
|
MPP_RET rc_model_v2_smt_deinit(void *ctx)
|
{
|
RcModelV2SmtCtx *p = (RcModelV2SmtCtx *)ctx;
|
rc_dbg_func("enter %p\n", ctx);
|
bits_model_smt_deinit(p);
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET rc_model_v2_smt_start(void *ctx, EncRcTask *task)
|
{
|
RcModelV2SmtCtx *p = (RcModelV2SmtCtx*)ctx;
|
EncFrmStatus *frm = &task->frm;
|
EncRcTaskInfo *info = &task->info;
|
RcFpsCfg *fps = &p->usr_cfg.fps;
|
RK_U32 md_ctu_cnt = 0;
|
void *ptr = NULL;
|
|
if (frm->reencode)
|
return MPP_OK;
|
|
if (mpp_frame_has_meta(task->frame)) {
|
MppMeta meta = mpp_frame_get_meta(task->frame);
|
|
mpp_meta_get_ptr(meta, KEY_MV_LIST, &ptr);
|
}
|
|
md_ctu_cnt = cal_mv_info(ptr);
|
|
if (frm->is_intra) {
|
p->frame_type = INTRA_FRAME;
|
p->acc_total_count = 0;
|
p->acc_intra_bits_in_fps = 0;
|
} else {
|
p->frame_type = INTER_P_FRAME;
|
}
|
|
switch (p->gop_mode) {
|
case MPP_GOP_ALL_INTER : {
|
if (p->frame_type == INTRA_FRAME) {
|
p->bits_target_low_rate = p->bits_per_intra_low_rate;
|
p->bits_target_high_rate = p->bits_per_intra_high_rate;
|
} else {
|
p->bits_target_low_rate = p->bits_per_inter_low_rate
|
- mpp_pid_calc(&p->pid_inter_low_rate);
|
p->bits_target_high_rate = p->bits_per_inter_high_rate
|
- mpp_pid_calc(&p->pid_inter_high_rate);
|
}
|
} break;
|
case MPP_GOP_ALL_INTRA : {
|
p->bits_target_low_rate = p->bits_per_intra_low_rate
|
- mpp_pid_calc(&p->pid_intra_low_rate);
|
p->bits_target_high_rate = p->bits_per_intra_high_rate
|
- mpp_pid_calc(&p->pid_intra_high_rate);
|
} break;
|
default : {
|
if (p->frame_type == INTRA_FRAME) {
|
//float intra_percent = 0.0;
|
RK_S32 diff_bit = mpp_pid_calc(&p->pid_fps);
|
/* only affected by last gop */
|
p->pre_gop_left_bit = p->pid_fps.i - diff_bit;
|
|
mpp_pid_reset(&p->pid_fps);
|
|
if (p->acc_intra_count) {
|
p->bits_target_low_rate = (p->bits_per_intra_low_rate + diff_bit);
|
p->bits_target_high_rate = (p->bits_per_intra_high_rate + diff_bit);
|
} else {
|
p->bits_target_low_rate = p->bits_per_intra_low_rate
|
- mpp_pid_calc(&p->pid_intra_low_rate);
|
p->bits_target_high_rate = p->bits_per_intra_high_rate
|
- mpp_pid_calc(&p->pid_intra_high_rate);
|
}
|
} else {
|
if (p->last_frame_type == INTRA_FRAME) {
|
RK_S32 diff_bit = mpp_pid_calc(&p->pid_fps);
|
/*
|
* case - inter frame after intra frame
|
* update inter target bits with compensation of previous intra frame
|
*/
|
RK_S32 bits_prev_intra = mpp_data_avg(p->intra, 1, 1, 1);
|
|
p->bits_per_inter_low_rate = (p->bps_target_low_rate *
|
(p->gop_min * 1.0 / fps->fps_out_num) -
|
bits_prev_intra + diff_bit + p->pre_gop_left_bit) /
|
(p->gop_min - 1);
|
p->bits_target_low_rate = p->bits_per_inter_low_rate;
|
p->bits_per_inter_high_rate = (p->bps_target_high_rate *
|
(p->gop_min * 1.0 / fps->fps_out_num) -
|
bits_prev_intra + diff_bit + p->pre_gop_left_bit) /
|
(p->gop_min - 1);
|
p->bits_target_high_rate = p->bits_per_inter_high_rate;
|
} else {
|
RK_S32 diff_bit_low_rate = mpp_pid_calc(&p->pid_inter_low_rate);
|
p->bits_target_low_rate = p->bits_per_inter_low_rate - diff_bit_low_rate;
|
if (p->bits_target_low_rate > p->bits_per_pic_low_rate * 2) {
|
p->bits_target_low_rate = 2 * p->bits_per_pic_low_rate;
|
p->pid_inter_low_rate.i = p->pid_inter_low_rate.i / 2;
|
}
|
RK_S32 diff_bit_high_rate = mpp_pid_calc(&p->pid_inter_high_rate);
|
p->bits_target_high_rate = p->bits_per_inter_high_rate - diff_bit_high_rate;
|
|
if (p->bits_target_high_rate > p->bits_per_pic_high_rate * 2) {
|
p->bits_target_high_rate = 2 * p->bits_per_pic_high_rate;
|
p->pid_inter_high_rate.i = p->pid_inter_high_rate.i / 2;
|
}
|
}
|
|
}
|
} break;
|
|
}
|
|
if (NULL == p->qp_p)
|
mpp_data_init(&p->qp_p, MPP_MIN(p->gop_min, 15));
|
if (NULL == p->sse_p)
|
mpp_data_init(&p->sse_p, MPP_MIN(p->gop_min, 15));
|
|
RK_S32 madi = p->hal_cfg.madi;
|
|
if (p->frm_num == 0) {
|
RK_S32 bits_target_use = 0;
|
bits_target_use = (p->bits_target_high_rate - p->bits_target_low_rate) * 1 * 0.5
|
+ p->bits_target_low_rate;
|
p->bits_target_use = bits_target_use;
|
bits_target_use = bits_target_use * ((1920 * 1080 * 1000)
|
/ (p->usr_cfg.width * p->usr_cfg.height)) / 1000;
|
p->qp_out = 95 - 10.0 * log((float)(bits_target_use / 1000));
|
|
if (p->qp_out < 27) {
|
p->qp_out = 27;
|
}
|
p->qp_out = mpp_clip(p->qp_out, p->qp_min, p->qp_max);
|
p->qp_preavg = 0;
|
}
|
|
if (p->frame_type == INTRA_FRAME) {
|
if (p->frm_num > 0) {
|
p->qp_out = mpp_data_avg(p->qp_p, -1, 1, 1) - 3;
|
RK_S32 sse = mpp_data_avg(p->sse_p, 1, 1, 1) + 1;
|
RK_S32 bit_target_use = (p->bits_target_low_rate + p->bits_target_high_rate) / 2;
|
RK_S32 qp_add = 0;
|
|
if (bit_target_use < 100)
|
bit_target_use = 100;
|
|
if (sse < p->intra_presse) {
|
if (bit_target_use <= p->intra_prerealbit) {
|
p->qp_out = p->intra_preqp;
|
} else {
|
if (madi >= MAD_THDI) {
|
p->qp_out = p->intra_preqp - 1;
|
} else {
|
if (p->intra_preqp - p->qp_out >= 4) {
|
p->qp_out = p->intra_preqp - (madi <= 12 ? 4 : 3);
|
}
|
}
|
}
|
} else if (sse > p->intra_presse && p->qp_out < p->intra_preqp) {
|
if (p->intra_preqp - p->qp_out >= 3)
|
p->qp_out = p->intra_preqp - 2;
|
} else {
|
RK_S32 ratio = (RK_S32)((float)sse / (float)p->intra_presse + 0.5);
|
if (ratio <= 2) {
|
p->qp_out = p->intra_preqp;
|
} else if (ratio <= 4) {
|
p->qp_out = mpp_clip(p->qp_out, p->intra_preqp - 1, p->intra_preqp + 1);
|
} else {
|
p->qp_out = mpp_clip(p->qp_out, p->intra_preqp - 2, p->intra_preqp + 2);
|
}
|
}
|
if (p->qp_prev_out < 25) {
|
qp_add = 4;
|
} else if (p->qp_prev_out < 27) {
|
qp_add = 2;
|
} else if (p->qp_prev_out < 29) {
|
qp_add = 1;
|
}
|
p->qp_out = mpp_clip(p->qp_out, p->qp_prev_out - 4, p->qp_prev_out + qp_add);
|
|
p->bits_target_use = (p->bits_target_low_rate + p->bits_target_high_rate) / 2;
|
if (p->bits_target_use < 100)
|
p->bits_target_use = 100;
|
}
|
} else {
|
p->bits_target_use = (p->bits_target_low_rate + p->bits_target_high_rate) / 2;
|
p->qp_out = p->qp_preavg;
|
|
if (p->last_frame_type == INTRA_FRAME) {
|
RK_S32 qp_add = 0;
|
if (p->qp_prev_out < 25) {
|
qp_add = 2;
|
} else if (p->qp_prev_out < 29) {
|
qp_add = 1;
|
}
|
p->qp_out = mpp_clip(p->qp_out, p->qp_prev_out + qp_add, p->qp_prev_out + 4 + qp_add);
|
p->bits_target_use = (p->bits_target_low_rate + p->bits_target_high_rate) / 2;
|
if (p->bits_target_use < 100)
|
p->bits_target_use = 100;
|
} else {
|
MppFrame frame = task->frame;
|
RK_S32 width = mpp_frame_get_width(frame);
|
RK_S32 height = mpp_frame_get_height(frame);
|
float coef = (float)md_ctu_cnt / (MPP_ALIGN(width, 16) / 16 * MPP_ALIGN(height, 16) / 16);
|
float coef2 = 0.5;
|
|
if (coef >= 0.7)
|
coef2 = 1.0;
|
else if (coef >= 0.3) // 0.7~0.3 --> 1.0~0.5
|
coef2 = 0.5 + (coef - 0.3) * (1.0 - 0.5) / (0.7 - 0.3);
|
else // 0.3~0.0 --> 0.5~0.0
|
coef2 = 0.0 + coef * (0.5 - 0.0) / (0.3 - 0.0);
|
|
RK_S32 bits_target_use = p->bits_target_low_rate; // bits_target_high_rate
|
RK_S32 pre_diff_bit_use = p->pre_diff_bit_low_rate; // pre_diff_bit_high_rate
|
|
bits_target_use = (p->bits_target_high_rate - p->bits_target_low_rate) * 1 * coef2
|
+ p->bits_target_low_rate;
|
pre_diff_bit_use = (p->pre_diff_bit_high_rate - p->pre_diff_bit_low_rate) * 1 * coef2
|
+ p->pre_diff_bit_low_rate;
|
|
if (bits_target_use < 100)
|
bits_target_use = 100;
|
|
p->bits_target_use = bits_target_use;
|
if (abs(pre_diff_bit_use) <= bits_target_use * 3 / 100) {
|
p->qp_out = p->qp_prev_out - 1;
|
} else if (pre_diff_bit_use > bits_target_use * 3 / 100) {
|
if (pre_diff_bit_use >= bits_target_use) {
|
p->qp_out = (p->qp_out >= 30 && madi < MAD_THDI) ? p->qp_prev_out - 4 : p->qp_prev_out - 3;
|
} else if (pre_diff_bit_use >= bits_target_use * 1 / 4) {
|
p->qp_out = (p->qp_out >= 30 && madi < MAD_THDI) ? p->qp_prev_out - 3 : p->qp_prev_out - 2;
|
} else if (pre_diff_bit_use > bits_target_use * 1 / 10) {
|
p->qp_out = (madi < MAD_THDI) ? p->qp_prev_out - 2 : p->qp_prev_out - 1;
|
} else {
|
p->qp_out = p->qp_prev_out - 1;
|
}
|
} else {
|
pre_diff_bit_use = abs(pre_diff_bit_use);
|
RK_S32 weight = (madi < MAD_THDI ? 1 : 3);
|
if (pre_diff_bit_use >= 2 * weight * bits_target_use) {
|
p->qp_out = p->qp_prev_out + 3 ;
|
} else if (pre_diff_bit_use >= bits_target_use * 2 * weight / 3) {
|
p->qp_out = p->qp_prev_out + 2 ;
|
} else if (pre_diff_bit_use > bits_target_use * weight / 5) {
|
p->qp_out = p->qp_prev_out + 1;
|
} else {
|
p->qp_out = p->qp_prev_out;
|
}
|
}
|
|
p->qp_out = mpp_clip(p->qp_out, p->qp_min, p->qp_max);
|
|
pre_diff_bit_use = (p->pre_diff_bit_high_rate - p->pre_diff_bit_low_rate) * 1 * coef2 + p->pre_diff_bit_low_rate;
|
RK_S32 low_pre_diff_bit_use = LOW_PRE_DIFF_BIT_USE;
|
low_pre_diff_bit_use = (p->bps_target_low_rate + p->bps_target_high_rate) / 2 / fps->fps_out_num;
|
low_pre_diff_bit_use = -low_pre_diff_bit_use / 5;
|
|
RK_S32 frame_low_qp;
|
|
if (p->codec_type == 1) {
|
if (madi >= MAD_THDI)
|
frame_low_qp = LOW_QP_level1;
|
else
|
frame_low_qp = LOW_QP_level0;
|
} else {
|
frame_low_qp = LOW_QP;
|
}
|
|
if (p->qp_out > frame_low_qp) {
|
if (pre_diff_bit_use <= 2 * low_pre_diff_bit_use)
|
coef2 += 0.5;
|
else if (pre_diff_bit_use <= low_pre_diff_bit_use)
|
coef2 += 0.2;
|
else
|
coef2 += 0.1;
|
|
if (coef2 >= 1.0)
|
coef2 = 1.0;
|
|
if (p->qp_out > LOW_LOW_QP)
|
coef2 = 1.0;
|
|
if (coef2 == 1.0 && p->qp_prev_out > frame_low_qp) {
|
float delta_coef = 0;
|
if (p->bits_one_gop_use_flag == 1 && p->delta_bits_per_frame > 0) {
|
delta_coef = ((float)p->delta_bits_per_frame) / ((float)p->bits_per_inter_high_rate - p->bits_per_inter_low_rate);
|
|
if (delta_coef >= 0.5)
|
delta_coef = 0.5;
|
else if (delta_coef >= 0.3)
|
delta_coef = 0.3;
|
|
if (p->qp_prev_out > (frame_low_qp + 1))
|
delta_coef += 0.1;
|
|
coef2 += delta_coef;
|
}
|
}
|
|
bits_target_use = (p->bits_target_high_rate - p->bits_target_low_rate) * 1 * coef2 + p->bits_target_low_rate;
|
pre_diff_bit_use = (p->pre_diff_bit_high_rate - p->pre_diff_bit_low_rate) * 1 * coef2 + p->pre_diff_bit_low_rate;
|
if (bits_target_use < 100)
|
bits_target_use = 100;
|
|
p->bits_target_use = bits_target_use;
|
if (abs(pre_diff_bit_use) <= bits_target_use * 3 / 100) {
|
p->qp_out = p->qp_prev_out ;
|
} else if (pre_diff_bit_use > bits_target_use * 3 / 100) {
|
if (pre_diff_bit_use >= bits_target_use) {
|
p->qp_out = (p->qp_out >= 30 && madi < MAD_THDI) ? p->qp_prev_out - 3 : p->qp_prev_out - 2;
|
} else if (pre_diff_bit_use >= bits_target_use * 1 / 4) {
|
p->qp_out = (p->qp_out >= 30 && madi < MAD_THDI) ? p->qp_prev_out - 2 : p->qp_prev_out - 1;
|
} else if (pre_diff_bit_use > bits_target_use * 1 / 10) {
|
p->qp_out = (madi < MAD_THDI) ? p->qp_prev_out - 1 : p->qp_prev_out;
|
} else {
|
p->qp_out = p->qp_prev_out;
|
}
|
} else {
|
pre_diff_bit_use = abs(pre_diff_bit_use);
|
RK_S32 weight = (madi < MAD_THDI ? 0 : 2);
|
if (pre_diff_bit_use >= 2 * weight * bits_target_use) {
|
p->qp_out = p->qp_prev_out + 1 ;
|
} else if (pre_diff_bit_use >= bits_target_use * 2 * weight / 3) {
|
p->qp_out = p->qp_prev_out + 1 ;
|
} else if (pre_diff_bit_use > bits_target_use * weight / 5) {
|
p->qp_out = p->qp_prev_out;
|
} else {
|
p->qp_out = p->qp_prev_out;
|
}
|
}
|
|
p->qp_out = mpp_clip(p->qp_out, p->qp_min, p->qp_max);
|
}
|
|
if (p->qp_out < LIMIT_QP_MORE_MOVE && coef >= 0.2)
|
p->qp_out = LIMIT_QP_MORE_MOVE;
|
}
|
}
|
|
if (p->frm_num >= 1) {
|
p->qp_out = mpp_clip(p->qp_out, p->qp_prev_out - 3, p->qp_prev_out + p->qp_step);
|
}
|
|
info->bit_target = p->bits_target_use;
|
info->quality_target = p->qp_out;
|
info->quality_max = p->usr_cfg.max_quality;
|
info->quality_min = p->usr_cfg.min_quality;
|
p->frm_num++;
|
p->reenc_cnt = 0;
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET rc_model_v2_smt_check_reenc(void *ctx, EncRcTask *task)
|
{
|
RcModelV2SmtCtx *p = (RcModelV2SmtCtx *)ctx;
|
EncRcTaskInfo *cfg = (EncRcTaskInfo *)&task->info;
|
EncFrmStatus *frm = &task->frm;
|
RcCfg *usr_cfg = &p->usr_cfg;
|
|
rc_dbg_func("enter ctx %p cfg %p\n", ctx, cfg);
|
|
frm->reencode = 0;
|
|
if ((usr_cfg->mode == RC_FIXQP) ||
|
(task->force.force_flag & ENC_RC_FORCE_QP))
|
return MPP_OK;
|
|
if (check_re_enc_smt(p, cfg)) {
|
if (p->usr_cfg.mode == RC_CBR) {
|
reenc_calc_cbr_ratio_smt(p, cfg);
|
} else {
|
reenc_calc_vbr_ratio_smt(p, cfg);
|
}
|
if (p->next_ratio != 0 && cfg->quality_target < cfg->quality_max) {
|
p->reenc_cnt++;
|
frm->reencode = 1;
|
}
|
}
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
MPP_RET rc_model_v2_smt_end(void *ctx, EncRcTask *task)
|
{
|
RcModelV2SmtCtx *p = (RcModelV2SmtCtx *)ctx;
|
EncRcTaskInfo *cfg = (EncRcTaskInfo *)&task->info;
|
MppFrame frame = task->frame;
|
RK_S32 width = mpp_frame_get_width(frame);
|
RK_S32 height = mpp_frame_get_height(frame);
|
RK_S32 bit_real = cfg->bit_real;
|
RK_S32 madi = cfg->madi;
|
RK_S32 cu64_num = (MPP_ALIGN(width, 64) / 64 * MPP_ALIGN(height, 64) / 64) ;
|
RK_U64 sse_dat = cfg->madp * cu64_num;
|
RK_U32 qp_sum;
|
double avg_qp = 0.0;
|
RK_S32 avg_sse = 1;
|
|
rc_dbg_func("enter ctx %p cfg %p\n", ctx, cfg);
|
|
if (p->codec_type == 1)
|
qp_sum = cfg->quality_real / 64; // 265
|
else
|
qp_sum = cfg->quality_real / 16; // 264
|
|
avg_qp = qp_sum;
|
avg_sse = (RK_S32)sqrt((double)(sse_dat));
|
p->qp_preavg = (RK_S32)(avg_qp + 0.5);
|
if (p->frame_type == INTER_P_FRAME) {
|
if (madi >= MAD_THDI) {
|
avg_qp = p->qp_out;
|
}
|
}
|
|
if (p->frame_type == INTER_P_FRAME || p->gop_mode == MPP_GOP_ALL_INTRA) {
|
mpp_data_update(p->qp_p, avg_qp);
|
mpp_data_update(p->sse_p, avg_sse);
|
} else {
|
p->intra_preqp = p->qp_out;
|
p->intra_presse = avg_sse;
|
p->intra_premadi = madi;
|
p->intra_prerealbit = bit_real;
|
}
|
|
p->st_madi = cfg->madi;
|
rc_dbg_rc("bits_mode_update real_bit %d", bit_real);
|
bits_model_update_smt(p, bit_real);
|
p->pre_target_bits = cfg->bit_target;
|
p->pre_real_bits = bit_real;
|
p->qp_prev_out = p->qp_out;
|
p->last_inst_bps = p->ins_bps;
|
p->last_frame_type = p->frame_type;
|
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
|
MPP_RET rc_model_v2_smt_hal_start(void *ctx, EncRcTask *task)
|
{
|
rc_dbg_func("smt_hal_start enter ctx %p task %p\n", ctx, task);
|
return MPP_OK;
|
}
|
|
MPP_RET rc_model_v2_smt_hal_end(void *ctx, EncRcTask *task)
|
{
|
rc_dbg_func("smt_hal_end enter ctx %p task %p\n", ctx, task);
|
rc_dbg_func("leave %p\n", ctx);
|
return MPP_OK;
|
}
|
|
|
const RcImplApi smt_h264e = {
|
"smart",
|
MPP_VIDEO_CodingAVC,
|
sizeof(RcModelV2SmtCtx),
|
rc_model_v2_smt_h264_init,
|
rc_model_v2_smt_deinit,
|
NULL,
|
rc_model_v2_smt_check_reenc,
|
rc_model_v2_smt_start,
|
rc_model_v2_smt_end,
|
rc_model_v2_smt_hal_start,
|
rc_model_v2_smt_hal_end,
|
};
|
|
const RcImplApi smt_h265e = {
|
"smart",
|
MPP_VIDEO_CodingHEVC,
|
sizeof(RcModelV2SmtCtx),
|
rc_model_v2_smt_h265_init,
|
rc_model_v2_smt_deinit,
|
NULL,
|
rc_model_v2_smt_check_reenc,
|
rc_model_v2_smt_start,
|
rc_model_v2_smt_end,
|
rc_model_v2_smt_hal_start,
|
rc_model_v2_smt_hal_end,
|
};
|