Files
mpp/utils/mpi_enc_utils.c
Herman Chen a2c289bbce [mpp_enc_ref]: Add st_cfg repeat value check
1. When repeat in st_cfg is negative set it to zero.
2. Do not insert non-reference frame when smart oop vi_len less than 2.

Change-Id: Icdf2be21fbbe0a919f7c3006d9c1dde5dfc5ffcb
Signed-off-by: Herman Chen <herman.chen@rock-chips.com>
2020-09-11 15:29:39 +08:00

672 lines
22 KiB
C

/*
* 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 "mpi_enc_utils"
#include <string.h>
#include "mpp_mem.h"
#include "mpp_log.h"
#include "rk_mpi.h"
#include "utils.h"
#include "mpp_common.h"
#include "mpi_enc_utils.h"
#define MAX_FILE_NAME_LENGTH 256
RK_S32 mpi_enc_width_default_stride(RK_S32 width, MppFrameFormat fmt)
{
RK_S32 stride = 0;
switch (fmt & MPP_FRAME_FMT_MASK) {
case MPP_FMT_YUV420SP : {
stride = MPP_ALIGN(width, 8);
} break;
case MPP_FMT_YUV420P : {
/* NOTE: 420P need to align to 16 so chroma can align to 8 */
stride = MPP_ALIGN(width, 16);
} break;
case MPP_FMT_YUV422P:
case MPP_FMT_YUV422SP:
case MPP_FMT_RGB565:
case MPP_FMT_BGR565:
case MPP_FMT_RGB555:
case MPP_FMT_BGR555:
case MPP_FMT_RGB444:
case MPP_FMT_BGR444:
case MPP_FMT_YUV422_YUYV :
case MPP_FMT_YUV422_YVYU :
case MPP_FMT_YUV422_UYVY :
case MPP_FMT_YUV422_VYUY : {
/* NOTE: for vepu limitation */
stride = MPP_ALIGN(width, 8) * 2;
} break;
case MPP_FMT_RGB888 :
case MPP_FMT_BGR888 : {
/* NOTE: for vepu limitation */
stride = MPP_ALIGN(width, 8) * 3;
} break;
case MPP_FMT_ARGB8888 :
case MPP_FMT_ABGR8888:
case MPP_FMT_BGRA8888:
case MPP_FMT_RGBA8888: {
/* NOTE: for vepu limitation */
stride = MPP_ALIGN(width, 8) * 4;
} break;
default : {
mpp_err_f("do not support type %d\n", fmt);
} break;
}
return stride;
}
MpiEncTestArgs *mpi_enc_test_cmd_get(void)
{
MpiEncTestArgs *args = mpp_calloc(MpiEncTestArgs, 1);
return args;
}
MPP_RET mpi_enc_test_cmd_update_by_args(MpiEncTestArgs* cmd, int argc, char **argv)
{
const char *opt;
const char *next;
RK_S32 optindex = 1;
RK_S32 handleoptions = 1;
MPP_RET ret = MPP_NOK;
if ((argc < 2) || (cmd == NULL))
return ret;
/* parse options */
while (optindex < argc) {
opt = (const char*)argv[optindex++];
next = (const char*)argv[optindex];
if (handleoptions && opt[0] == '-' && opt[1] != '\0') {
if (opt[1] == '-') {
if (opt[2] != '\0') {
opt++;
} else {
handleoptions = 0;
continue;
}
}
opt++;
switch (*opt) {
case 'i' : {
if (next) {
size_t len = strnlen(next, MAX_FILE_NAME_LENGTH);
if (len) {
cmd->file_input = mpp_calloc(char, len + 1);
strcpy(cmd->file_input, next);
name_to_frame_format(cmd->file_input, &cmd->format);
}
} else {
mpp_err("input file is invalid\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'o' : {
if (next) {
size_t len = strnlen(next, MAX_FILE_NAME_LENGTH);
if (len) {
cmd->file_output = mpp_calloc(char, len + 1);
strcpy(cmd->file_output, next);
name_to_coding_type(cmd->file_output, &cmd->type);
}
} else {
mpp_log("output file is invalid\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'w' : {
if (next) {
cmd->width = atoi(next);
} else {
mpp_err("invalid input width\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'h' : {
if (!next)
goto PARSE_OPINIONS_OUT;
if ((*(opt + 1) != '\0') && !strncmp(opt, "help", 4)) {
goto PARSE_OPINIONS_OUT;
} else if (next) {
cmd->height = atoi(next);
}
} break;
case 'u' : {
if (next) {
cmd->hor_stride = atoi(next);
} else {
mpp_err("invalid input width\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'v' : {
if (next) {
cmd->ver_stride = atoi(next);
} else {
mpp_log("input height is invalid\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'f' : {
if (next) {
if (strstr(next, "x") || strstr(next, "X")) {
/* hex value with 0x prefix, use sscanf */
sscanf(next, "0x%x", &cmd->format);
} else if (strstr(next, "a") || strstr(next, "A") ||
strstr(next, "b") || strstr(next, "B") ||
strstr(next, "c") || strstr(next, "C") ||
strstr(next, "d") || strstr(next, "D") ||
strstr(next, "e") || strstr(next, "E") ||
strstr(next, "f") || strstr(next, "F")) {
/* hex value without 0x prefix, use sscanf */
sscanf(next, "%x", &cmd->format);
} else {
/* decimal value, use atoi */
cmd->format = (MppFrameFormat)atoi(next);
}
ret = (!MPP_FRAME_FMT_IS_LE(cmd->format)) && ((cmd->format >= MPP_FMT_YUV_BUTT && cmd->format < MPP_FRAME_FMT_RGB) ||
cmd->format >= MPP_FMT_RGB_BUTT);
}
if (!next || ret) {
mpp_err("invalid input format %d\n", cmd->format);
goto PARSE_OPINIONS_OUT;
}
} break;
case 't' : {
if (next) {
cmd->type = (MppCodingType)atoi(next);
ret = mpp_check_support_format(MPP_CTX_ENC, cmd->type);
}
if (!next || ret) {
mpp_err("invalid input coding type\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'n' : {
if (next) {
cmd->num_frames = atoi(next);
} else {
mpp_err("invalid input max number of frames\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'g' : {
RK_S32 cnt = 0;
if (next)
cnt = sscanf(next, "%d:%d:%d",
&cmd->gop_mode, &cmd->gop_len, &cmd->vi_len);
if (!cnt) {
mpp_err("invalid gop mode use -g gop_mode:gop_len:vi_len\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'b' : {
RK_S32 cnt = 0;
if (next)
cnt = sscanf(next, "%d:%d:%d:%d",
&cmd->bps_target, &cmd->bps_min, &cmd->bps_max,
&cmd->rc_mode);
if (!cnt) {
mpp_err("invalid bit rate usage -b bps_target:bps_min:bps_max:rc_mode\n");
mpp_err("rc_mode 0:vbr 1:cbr 2:avbr 3:cvbr 4:fixqp\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'r' : {
if (next) {
RK_S32 num = sscanf(next, "%d:%d:%d/%d:%d:%d",
&cmd->fps_in_num, &cmd->fps_in_den, &cmd->fps_in_flex,
&cmd->fps_out_num, &cmd->fps_out_den, &cmd->fps_out_flex);
switch (num) {
case 1 : {
cmd->fps_out_num = cmd->fps_in_num;
cmd->fps_out_den = cmd->fps_in_den = 1;
cmd->fps_out_flex = cmd->fps_in_flex = 0;
} break;
case 2 : {
cmd->fps_out_num = cmd->fps_in_num;
cmd->fps_out_den = cmd->fps_in_den;
cmd->fps_out_flex = cmd->fps_in_flex = 0;
} break;
case 3 : {
cmd->fps_out_num = cmd->fps_in_num;
cmd->fps_out_den = cmd->fps_in_den;
cmd->fps_out_flex = cmd->fps_in_flex;
} break;
case 4 : {
cmd->fps_out_den = 1;
cmd->fps_out_flex = 0;
} break;
case 5 : {
cmd->fps_out_flex = 0;
} break;
case 6 : {
} break;
default : {
mpp_err("invalid in/out frame rate,"
" use \"-r numerator:denominator:flex\""
" for set the input to the same fps as the output, such as 50:1:1\n"
" or \"-r numerator:denominator/flex-numerator:denominator:flex\""
" for set input and output separately, such as 40:1:1/30:1:0\n");
goto PARSE_OPINIONS_OUT;
} break;
}
} else {
mpp_err("invalid output frame rate\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'l' : {
if (next) {
cmd->loop_cnt = atoi(next);
} else {
mpp_err("invalid loop count\n");
goto PARSE_OPINIONS_OUT;
}
} break;
case 'x': {
if (next) {
size_t len = strnlen(next, MAX_FILE_NAME_LENGTH);
if (len) {
cmd->file_cfg = mpp_calloc(char, len + 1);
strncpy(cmd->file_cfg, next, len);
cmd->cfg_ini = iniparser_load(cmd->file_cfg);
}
} else {
mpp_err("input file is invalid\n");
goto PARSE_OPINIONS_OUT;
}
} break;
default : {
mpp_err("skip invalid opt %c\n", *opt);
} break;
}
optindex++;
}
}
ret = MPP_OK;
/* check essential parameter */
if (cmd->type <= MPP_VIDEO_CodingAutoDetect) {
mpp_err("invalid type %d\n", cmd->type);
ret = MPP_NOK;
}
if (!cmd->hor_stride)
cmd->hor_stride = mpi_enc_width_default_stride(cmd->width, cmd->format);
if (!cmd->ver_stride)
cmd->ver_stride = cmd->height;
if (cmd->width <= 0 || cmd->height <= 0 ||
cmd->hor_stride <= 0 || cmd->ver_stride <= 0) {
mpp_err("invalid w:h [%d:%d] stride [%d:%d]\n",
cmd->width, cmd->height, cmd->hor_stride, cmd->ver_stride);
ret = MPP_NOK;
}
PARSE_OPINIONS_OUT:
return ret;
}
MPP_RET mpi_enc_test_cmd_put(MpiEncTestArgs* cmd)
{
if (NULL == cmd)
return MPP_OK;
if (cmd->cfg_ini) {
iniparser_freedict(cmd->cfg_ini);
cmd->cfg_ini = NULL;
}
MPP_FREE(cmd->file_input);
MPP_FREE(cmd->file_output);
MPP_FREE(cmd->file_cfg);
MPP_FREE(cmd);
return MPP_OK;
}
MPP_RET mpi_enc_gen_ref_cfg(MppEncRefCfg ref, RK_S32 gop_mode)
{
MppEncRefLtFrmCfg lt_ref[4];
MppEncRefStFrmCfg st_ref[16];
RK_S32 lt_cnt = 0;
RK_S32 st_cnt = 0;
MPP_RET ret = MPP_OK;
memset(&lt_ref, 0, sizeof(lt_ref));
memset(&st_ref, 0, sizeof(st_ref));
switch (gop_mode) {
case 3 : {
// tsvc4
// /-> P1 /-> P3 /-> P5 /-> P7
// / / / /
// //--------> P2 //--------> P6
// // //
// ///---------------------> P4
// ///
// P0 ------------------------------------------------> P8
lt_cnt = 1;
/* 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;
st_cnt = 9;
/* 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_LT_REF;
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;
} break;
case 2 : {
// tsvc3
// /-> P1 /-> P3
// / /
// //--------> P2
// //
// P0/---------------------> P4
lt_cnt = 0;
st_cnt = 5;
/* 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 2 - non-ref */
st_ref[1].is_non_ref = 1;
st_ref[1].temporal_id = 2;
st_ref[1].ref_mode = REF_TO_PREV_REF_FRM;
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_PREV_REF_FRM;
st_ref[2].ref_arg = 0;
st_ref[2].repeat = 0;
/* st 3 layer 2 - non-ref */
st_ref[3].is_non_ref = 1;
st_ref[3].temporal_id = 2;
st_ref[3].ref_mode = REF_TO_PREV_REF_FRM;
st_ref[3].ref_arg = 0;
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;
} break;
case 1 : {
// tsvc2
// /-> P1
// /
// P0--------> P2
lt_cnt = 0;
st_cnt = 3;
/* 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 2 - 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 1 - 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;
} break;
default : {
mpp_err_f("unsupport gop mode %d\n", gop_mode);
} break;
}
if (lt_cnt || st_cnt) {
ret = mpp_enc_ref_cfg_set_cfg_cnt(ref, lt_cnt, st_cnt);
if (lt_cnt)
ret = mpp_enc_ref_cfg_add_lt_cfg(ref, lt_cnt, lt_ref);
if (st_cnt)
ret = mpp_enc_ref_cfg_add_st_cfg(ref, st_cnt, st_ref);
/* check and get dpb size */
ret = mpp_enc_ref_cfg_check(ref);
}
return ret;
}
MPP_RET mpi_enc_gen_smart_gop_ref_cfg(MppEncRefCfg ref, RK_S32 gop_len, RK_S32 vi_len)
{
MppEncRefLtFrmCfg lt_ref[4];
MppEncRefStFrmCfg st_ref[16];
RK_S32 lt_cnt = 1;
RK_S32 st_cnt = 8;
RK_S32 pos = 0;
MPP_RET ret = MPP_OK;
memset(&lt_ref, 0, sizeof(lt_ref));
memset(&st_ref, 0, sizeof(st_ref));
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 = gop_len;
lt_ref[0].lt_delay = 0;
ret = mpp_enc_ref_cfg_add_lt_cfg(ref, 1, lt_ref);
/* st 0 layer 0 - ref */
st_ref[pos].is_non_ref = 0;
st_ref[pos].temporal_id = 0;
st_ref[pos].ref_mode = REF_TO_PREV_INTRA;
st_ref[pos].ref_arg = 0;
st_ref[pos].repeat = 0;
pos++;
/* st 1 layer 1 - non-ref */
if (vi_len > 1) {
st_ref[pos].is_non_ref = 0;
st_ref[pos].temporal_id = 1;
st_ref[pos].ref_mode = REF_TO_PREV_REF_FRM;
st_ref[pos].ref_arg = 0;
st_ref[pos].repeat = vi_len - 2;
pos++;
}
st_ref[pos].is_non_ref = 0;
st_ref[pos].temporal_id = 0;
st_ref[pos].ref_mode = REF_TO_PREV_INTRA;
st_ref[pos].ref_arg = 0;
st_ref[pos].repeat = 0;
pos++;
ret = mpp_enc_ref_cfg_add_st_cfg(ref, pos, st_ref);
/* check and get dpb size */
ret = mpp_enc_ref_cfg_check(ref);
return ret;
}
MPP_RET mpi_enc_gen_osd_data(MppEncOSDData *osd_data, MppBuffer osd_buf, RK_U32 frame_cnt)
{
RK_U32 k = 0;
RK_U32 buf_size = 0;
RK_U32 buf_offset = 0;
RK_U8 *buf = mpp_buffer_get_ptr(osd_buf);
osd_data->num_region = 8;
osd_data->buf = osd_buf;
for (k = 0; k < osd_data->num_region; k++) {
MppEncOSDRegion *region = &osd_data->region[k];
RK_U8 idx = k;
region->enable = 1;
region->inverse = frame_cnt & 1;
region->start_mb_x = k * 3;
region->start_mb_y = k * 2;
region->num_mb_x = 2;
region->num_mb_y = 2;
buf_size = region->num_mb_x * region->num_mb_y * 256;
buf_offset = k * buf_size;
osd_data->region[k].buf_offset = buf_offset;
memset(buf + buf_offset, idx, buf_size);
}
return MPP_OK;
}
MPP_RET mpi_enc_gen_osd_plt(MppEncOSDPlt *osd_plt, RK_U32 *table)
{
RK_U32 k = 0;
if (osd_plt->data && table) {
for (k = 0; k < 256; k++)
osd_plt->data[k].val = table[k % 8];
}
return MPP_OK;
}
static OptionInfo mpi_enc_cmd[] = {
{"i", "input_file", "input bitstream file"},
{"o", "output_file", "output bitstream file, "},
{"w", "width", "the width of input picture"},
{"h", "height", "the height of input picture"},
{"f", "format", "the format of input picture"},
{"t", "type", "output stream coding type"},
{"n", "max frame number", "max encoding frame number"},
{"g", "gop_mode", "gop reference mode"},
{"d", "debug", "debug flag"},
{"b", "bps target:min:max", "set tareget bps"},
{"r", "in/output fps", "set input and output frame rate"},
{"l", "loop count", "loop encoding times for each frame"},
};
MPP_RET mpi_enc_test_cmd_show_opt(MpiEncTestArgs* cmd)
{
mpp_log("cmd parse result:\n");
mpp_log("input file name: %s\n", cmd->file_input);
mpp_log("output file name: %s\n", cmd->file_output);
mpp_log("width : %d\n", cmd->width);
mpp_log("height : %d\n", cmd->height);
mpp_log("format : %d\n", cmd->format);
mpp_log("type : %d\n", cmd->type);
return MPP_OK;
}
void mpi_enc_test_help(void)
{
mpp_log("usage: mpi_enc_test [options]\n");
show_options(mpi_enc_cmd);
mpp_show_support_format();
mpp_show_color_format();
}