Files
mpp/mpp/codec/enc/h264/h264e_api_v2.c
Herman Chen 33e7e4c52a [mpi]: Remove unused H.264 mpi cfg structure
Remove sei / ref cfg in MppEncH264Cfg.

Change-Id: Iaef6a8a845b691dc21d0a6a6eca62e27bc5cf5a4
Signed-off-by: Herman Chen <herman.chen@rock-chips.com>
2020-06-10 17:37:49 +08:00

618 lines
17 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 "h264e_api_v2"
#include <string.h>
#include "mpp_env.h"
#include "mpp_log.h"
#include "mpp_mem.h"
#include "mpp_common.h"
#include "mpp_packet_impl.h"
#include "mpp_enc_refs.h"
#include "h264e_debug.h"
#include "h264e_syntax.h"
#include "h264e_sps.h"
#include "h264e_pps.h"
#include "h264e_sei.h"
#include "h264e_dpb.h"
#include "h264e_slice.h"
#include "h264e_api.h"
#include "rc.h"
#include "enc_impl_api.h"
#include "mpp_enc_cfg_impl.h"
RK_U32 h264e_debug = 0;
typedef struct {
/* config from mpp_enc */
MppDeviceId dev_id;
MppEncCfgSet *cfg;
MppEncRefs refs;
RK_U32 idr_request;
/* H.264 high level syntax */
SynH264eSps sps;
SynH264ePps pps;
/*
* H.264 low level syntax
*
* NOTE: two dpb is for dpb roll-back and reencode
*/
H264eDpb dpb;
H264eDpb dpb_bak;
H264eSlice slice;
H264eReorderInfo reorder;
H264eMarkingInfo marking;
/* H.264 frame status syntax */
H264eFrmInfo frms;
RcSyntax rc_syn;
/* header generation */
MppPacket hdr_pkt;
void *hdr_buf;
size_t hdr_size;
size_t hdr_len;
RK_S32 sps_len;
RK_S32 pps_len;
RK_S32 sei_len;
/* rate control config */
RcCtx rc_ctx;
/* output to hal */
RK_S32 syn_num;
H264eSyntaxDesc syntax[H264E_SYN_BUTT];
} H264eCtx;
static void init_h264e_cfg_set(MppEncCfgSet *cfg)
{
MppEncRcCfg *rc_cfg = &cfg->rc;
MppEncPrepCfg *prep = &cfg->prep;
MppEncH264Cfg *h264 = &cfg->codec.h264;
/*
* default codec:
* High Profile
* frame mode
* all flag enabled
*/
memset(h264, 0, sizeof(*h264));
h264->profile = H264_PROFILE_BASELINE;
h264->level = H264_LEVEL_3_1;
h264->qp_init = 26;
h264->qp_max = 48;
h264->qp_min = 8;
h264->qp_max_step = 8;
/*
* default prep:
* 720p
* YUV420SP
*/
prep->change = 0;
prep->width = 1280;
prep->height = 720;
prep->hor_stride = 1280;
prep->ver_stride = 720;
prep->format = MPP_FMT_YUV420SP;
prep->rotation = MPP_ENC_ROT_0;
prep->mirroring = 0;
prep->denoise = 0;
/*
* default rc_cfg:
* CBR
* 2Mbps +-25%
* 30fps
* gop 60
*/
rc_cfg->change = 0;
rc_cfg->rc_mode = MPP_ENC_RC_MODE_CBR;
rc_cfg->quality = MPP_ENC_RC_QUALITY_MEDIUM;
rc_cfg->bps_target = 2000 * 1000;
rc_cfg->bps_max = rc_cfg->bps_target * 5 / 4;
rc_cfg->bps_min = rc_cfg->bps_target * 3 / 4;
rc_cfg->fps_in_flex = 0;
rc_cfg->fps_in_num = 30;
rc_cfg->fps_in_denorm = 1;
rc_cfg->fps_out_flex = 0;
rc_cfg->fps_out_num = 30;
rc_cfg->fps_out_denorm = 1;
rc_cfg->gop = 60;
rc_cfg->max_reenc_times = 1;
}
static void h264e_add_syntax(H264eCtx *ctx, H264eSyntaxType type, void *p)
{
ctx->syntax[ctx->syn_num].type = type;
ctx->syntax[ctx->syn_num].p = p;
ctx->syn_num++;
}
static MPP_RET h264e_init(void *ctx, EncImplCfg *ctrl_cfg)
{
MPP_RET ret = MPP_OK;
H264eCtx *p = (H264eCtx *)ctx;
mpp_env_get_u32("h264e_debug", &h264e_debug, 0);
h264e_dbg_func("enter\n");
p->dev_id = ctrl_cfg->dev_id;
p->hdr_size = SZ_1K;
p->hdr_buf = mpp_malloc_size(void, p->hdr_size);
mpp_assert(p->hdr_buf);
mpp_packet_init(&p->hdr_pkt, p->hdr_buf, p->hdr_size);
mpp_assert(p->hdr_pkt);
p->cfg = ctrl_cfg->cfg;
p->refs = ctrl_cfg->refs;
p->idr_request = 0;
h264e_reorder_init(&p->reorder);
h264e_marking_init(&p->marking);
h264e_dpb_init(&p->dpb, &p->reorder, &p->marking);
h264e_slice_init(&p->slice, &p->reorder, &p->marking);
init_h264e_cfg_set(p->cfg);
mpp_env_get_u32("h264e_debug", &h264e_debug, 0);
h264e_dbg_func("leave\n");
return ret;
}
static MPP_RET h264e_deinit(void *ctx)
{
H264eCtx *p = (H264eCtx *)ctx;
h264e_dbg_func("enter\n");
if (p->hdr_pkt)
mpp_packet_deinit(&p->hdr_pkt);
MPP_FREE(p->hdr_buf);
h264e_dbg_func("leave\n");
return MPP_OK;
}
static MPP_RET h264e_proc_prep_cfg(MppEncPrepCfg *dst, MppEncPrepCfg *src)
{
MPP_RET ret = MPP_OK;
RK_U32 change = src->change;
mpp_assert(change);
if (change) {
MppEncPrepCfg bak = *dst;
if (change & MPP_ENC_PREP_CFG_CHANGE_FORMAT) {
dst->format = src->format;
dst->color = src->color;
dst->range = src->range;
}
if (change & MPP_ENC_PREP_CFG_CHANGE_ROTATION)
dst->rotation = src->rotation;
if (change & MPP_ENC_PREP_CFG_CHANGE_MIRRORING)
dst->mirroring = src->mirroring;
if (change & MPP_ENC_PREP_CFG_CHANGE_DENOISE)
dst->denoise = src->denoise;
if (change & MPP_ENC_PREP_CFG_CHANGE_SHARPEN)
dst->sharpen = src->sharpen;
if (change & MPP_ENC_PREP_CFG_CHANGE_INPUT) {
if (dst->rotation == MPP_ENC_ROT_90 || dst->rotation == MPP_ENC_ROT_270) {
dst->width = src->height;
dst->height = src->width;
} else {
dst->width = src->width;
dst->height = src->height;
}
dst->hor_stride = src->hor_stride;
dst->ver_stride = src->ver_stride;
}
dst->change |= change;
// parameter checking
if (dst->rotation == MPP_ENC_ROT_90 || dst->rotation == MPP_ENC_ROT_270) {
if (dst->height > dst->hor_stride || dst->width > dst->ver_stride) {
mpp_err("invalid size w:h [%d:%d] stride [%d:%d]\n",
dst->width, dst->height, dst->hor_stride, dst->ver_stride);
ret = MPP_ERR_VALUE;
}
} else {
if (dst->width > dst->hor_stride || dst->height > dst->ver_stride) {
mpp_err("invalid size w:h [%d:%d] stride [%d:%d]\n",
dst->width, dst->height, dst->hor_stride, dst->ver_stride);
ret = MPP_ERR_VALUE;
}
}
if (ret) {
mpp_err_f("failed to accept new prep config\n");
*dst = bak;
} else {
mpp_log_f("MPP_ENC_SET_PREP_CFG w:h [%d:%d] stride [%d:%d]\n",
dst->width, dst->height,
dst->hor_stride, dst->ver_stride);
}
}
return ret;
}
static MPP_RET h264e_proc_rc_cfg(MppEncRcCfg *dst, MppEncRcCfg *src)
{
MPP_RET ret = MPP_OK;
RK_U32 change = src->change;
if (change) {
MppEncRcCfg bak = *dst;
if (change & MPP_ENC_RC_CFG_CHANGE_RC_MODE)
dst->rc_mode = src->rc_mode;
if (change & MPP_ENC_RC_CFG_CHANGE_QUALITY)
dst->quality = src->quality;
if (change & MPP_ENC_RC_CFG_CHANGE_BPS) {
dst->bps_target = src->bps_target;
dst->bps_max = src->bps_max;
dst->bps_min = src->bps_min;
}
if (change & MPP_ENC_RC_CFG_CHANGE_FPS_IN) {
dst->fps_in_flex = src->fps_in_flex;
dst->fps_in_num = src->fps_in_num;
dst->fps_in_denorm = src->fps_in_denorm;
}
if (change & MPP_ENC_RC_CFG_CHANGE_FPS_OUT) {
dst->fps_out_flex = src->fps_out_flex;
dst->fps_out_num = src->fps_out_num;
dst->fps_out_denorm = src->fps_out_denorm;
}
if (change & MPP_ENC_RC_CFG_CHANGE_GOP)
dst->gop = src->gop;
if (change & MPP_ENC_RC_CFG_CHANGE_MAX_REENC)
dst->max_reenc_times = src->max_reenc_times;
// parameter checking
if (dst->rc_mode >= MPP_ENC_RC_MODE_BUTT) {
mpp_err("invalid rc mode %d should be RC_MODE_VBR or RC_MODE_CBR\n",
src->rc_mode);
ret = MPP_ERR_VALUE;
}
if (dst->quality >= MPP_ENC_RC_QUALITY_BUTT) {
mpp_err("invalid quality %d should be from QUALITY_WORST to QUALITY_BEST\n",
dst->quality);
ret = MPP_ERR_VALUE;
}
if (dst->rc_mode != MPP_ENC_RC_MODE_FIXQP) {
if ((dst->bps_target >= 100 * SZ_1M || dst->bps_target <= 1 * SZ_1K) ||
(dst->bps_max >= 100 * SZ_1M || dst->bps_max <= 1 * SZ_1K) ||
(dst->bps_min >= 100 * SZ_1M || dst->bps_min <= 1 * SZ_1K)) {
mpp_err("invalid bit per second %d [%d:%d] out of range 1K~100M\n",
dst->bps_target, dst->bps_min, dst->bps_max);
ret = MPP_ERR_VALUE;
}
}
dst->change |= change;
if (ret) {
mpp_err_f("failed to accept new rc config\n");
*dst = bak;
} else {
mpp_log_f("MPP_ENC_SET_RC_CFG bps %d [%d : %d] fps [%d:%d] gop %d\n",
dst->bps_target, dst->bps_min, dst->bps_max,
dst->fps_in_num, dst->fps_out_num, dst->gop);
}
}
return ret;
}
static MPP_RET h264e_proc_h264_cfg(MppEncH264Cfg *dst, MppEncH264Cfg *src)
{
MPP_RET ret = MPP_OK;
RK_U32 change = src->change;
// TODO: do codec check first
if (change & MPP_ENC_H264_CFG_STREAM_TYPE)
dst->stream_type = src->stream_type;
if (change & MPP_ENC_H264_CFG_CHANGE_PROFILE) {
dst->profile = src->profile;
dst->level = src->level;
}
if (change & MPP_ENC_H264_CFG_CHANGE_ENTROPY) {
dst->entropy_coding_mode = src->entropy_coding_mode;
dst->cabac_init_idc = src->cabac_init_idc;
}
if (change & MPP_ENC_H264_CFG_CHANGE_TRANS_8x8)
dst->transform8x8_mode = src->transform8x8_mode;
if (change & MPP_ENC_H264_CFG_CHANGE_CONST_INTRA)
dst->constrained_intra_pred_mode = src->constrained_intra_pred_mode;
if (change & MPP_ENC_H264_CFG_CHANGE_CHROMA_QP) {
dst->chroma_cb_qp_offset = src->chroma_cb_qp_offset;
dst->chroma_cr_qp_offset = src->chroma_cr_qp_offset;
}
if (change & MPP_ENC_H264_CFG_CHANGE_DEBLOCKING) {
dst->deblock_disable = src->deblock_disable;
dst->deblock_offset_alpha = src->deblock_offset_alpha;
dst->deblock_offset_beta = src->deblock_offset_beta;
}
if (change & MPP_ENC_H264_CFG_CHANGE_LONG_TERM)
dst->use_longterm = src->use_longterm;
if (change & MPP_ENC_H264_CFG_CHANGE_SCALING_LIST)
dst->scaling_list_mode = src->scaling_list_mode;
if (change & MPP_ENC_H264_CFG_CHANGE_QP_LIMIT) {
dst->qp_init = src->qp_init;
dst->qp_max = src->qp_max;
dst->qp_min = src->qp_min;
dst->qp_max_step = src->qp_max_step;
}
if (change & MPP_ENC_H264_CFG_CHANGE_INTRA_REFRESH) {
dst->intra_refresh_mode = src->intra_refresh_mode;
dst->intra_refresh_arg = src->intra_refresh_arg;
}
if (change & MPP_ENC_H264_CFG_CHANGE_SLICE_MODE) {
dst->slice_mode = src->slice_mode;
dst->slice_arg = src->slice_arg;
}
if (change & MPP_ENC_H264_CFG_CHANGE_VUI) {
dst->vui = src->vui;
}
dst->change |= change;
return ret;
}
static MPP_RET h264e_proc_split_cfg(MppEncSliceSplit *dst, MppEncSliceSplit *src)
{
MPP_RET ret = MPP_OK;
RK_U32 change = src->change;
if (change & MPP_ENC_SPLIT_CFG_CHANGE_MODE) {
dst->split_mode = src->split_mode;
dst->split_arg = src->split_arg;
}
if (change & MPP_ENC_SPLIT_CFG_CHANGE_ARG)
dst->split_arg = src->split_arg;
dst->change |= change;
return ret;
}
static MPP_RET h264e_proc_cfg(void *ctx, MpiCmd cmd, void *param)
{
MPP_RET ret = MPP_OK;
H264eCtx *p = (H264eCtx *)ctx;
MppEncCfgSet *cfg = p->cfg;
h264e_dbg_func("enter ctx %p cmd %x param %p\n", ctx, cmd, param);
switch (cmd) {
case MPP_ENC_SET_CFG : {
MppEncCfgImpl *impl = (MppEncCfgImpl *)param;
MppEncCfgSet *src = &impl->cfg;
if (src->prep.change) {
ret |= h264e_proc_prep_cfg(&cfg->prep, &src->prep);
src->prep.change = 0;
}
if (src->rc.change) {
ret |= h264e_proc_rc_cfg(&cfg->rc, &src->rc);
src->rc.change = 0;
}
if (src->codec.h264.change) {
ret |= h264e_proc_h264_cfg(&cfg->codec.h264, &src->codec.h264);
src->codec.h264.change = 0;
}
if (src->split.change) {
ret |= h264e_proc_split_cfg(&cfg->split, &src->split);
src->split.change = 0;
}
} break;
case MPP_ENC_SET_PREP_CFG : {
ret = h264e_proc_prep_cfg(&cfg->prep, param);
} break;
case MPP_ENC_SET_RC_CFG : {
ret = h264e_proc_rc_cfg(&cfg->rc, param);
} break;
case MPP_ENC_SET_CODEC_CFG : {
MppEncCodecCfg *codec = (MppEncCodecCfg *)param;
ret = h264e_proc_h264_cfg(&cfg->codec.h264, &codec->h264);
} break;
case MPP_ENC_SET_SEI_CFG : {
} break;
case MPP_ENC_SET_SPLIT : {
ret = h264e_proc_split_cfg(&cfg->split, param);
} break;
default:
mpp_err("No correspond cmd found, and can not config!");
ret = MPP_NOK;
break;
}
h264e_dbg_func("leave ret %d\n", ret);
return ret;
}
static MPP_RET h264e_gen_hdr(void *ctx, MppPacket pkt)
{
H264eCtx *p = (H264eCtx *)ctx;
h264e_dbg_func("enter\n");
h264e_sps_update(&p->sps, p->cfg, p->dev_id);
h264e_pps_update(&p->pps, p->cfg);
/*
* NOTE: When sps/pps is update we need to update dpb and slice info
*/
h264e_dpb_setup(&p->dpb, p->cfg, &p->sps);
mpp_packet_reset(p->hdr_pkt);
h264e_sps_to_packet(&p->sps, p->hdr_pkt, &p->sps_len);
h264e_pps_to_packet(&p->pps, p->hdr_pkt, &p->pps_len);
p->hdr_len = mpp_packet_get_length(p->hdr_pkt);
if (pkt) {
mpp_packet_write(pkt, 0, p->hdr_buf, p->hdr_len);
mpp_packet_set_length(pkt, p->hdr_len);
}
h264e_dbg_func("leave\n");
return MPP_OK;
}
static MPP_RET h264e_start(void *ctx, HalEncTask *task)
{
H264eCtx *p = (H264eCtx *)ctx;
h264e_dbg_func("enter\n");
h264e_dbg_func("leave\n");
(void) task;
(void) p;
return MPP_OK;
}
static MPP_RET h264e_proc_dpb(void *ctx, HalEncTask *task)
{
H264eCtx *p = (H264eCtx *)ctx;
H264eDpb *dpb = &p->dpb;
H264eFrmInfo *frms = &p->frms;
EncCpbStatus *cpb = &task->rc_task->cpb;
EncFrmStatus *frm = &task->rc_task->frm;
H264eDpbFrm *curr = NULL;
H264eDpbFrm *refr = NULL;
RK_S32 i;
h264e_dbg_func("enter\n");
// update dpb
h264e_dpb_proc(dpb, cpb);
curr = dpb->curr;
refr = dpb->refr;
// update slice info
h264e_slice_update(&p->slice, p->cfg, &p->sps, dpb->curr);
// update frame usage
frms->seq_idx = curr->seq_idx;
frms->curr_idx = curr->slot_idx;
frms->refr_idx = (refr) ? refr->slot_idx : curr->slot_idx;
for (i = 0; i < (RK_S32)MPP_ARRAY_ELEMS(frms->usage); i++)
frms->usage[i] = dpb->frames[i].on_used;
// update dpb to after encoding status
h264e_dpb_check(dpb, cpb);
frm->val = curr->status.val;
/*
* Step 5: Wait previous frame bit/quality result
*
* On normal case encoder will wait previous encoding done and get feedback
* from hardware then start the new frame encoding.
* But for asynchronous process rate control module should be able to
* handle the case that previous encoding is not done.
*/
h264e_dbg_func("leave\n");
return MPP_OK;
}
static MPP_RET h264e_proc_hal(void *ctx, HalEncTask *task)
{
H264eCtx *p = (H264eCtx *)ctx;
h264e_dbg_func("enter\n");
p->syn_num = 0;
h264e_add_syntax(p, H264E_SYN_CFG, p->cfg);
h264e_add_syntax(p, H264E_SYN_SPS, &p->sps);
h264e_add_syntax(p, H264E_SYN_PPS, &p->pps);
h264e_add_syntax(p, H264E_SYN_SLICE, &p->slice);
h264e_add_syntax(p, H264E_SYN_FRAME, &p->frms);
h264e_add_syntax(p, H264E_SYN_RC, &p->rc_syn);
task->valid = 1;
task->syntax.data = &p->syntax[0];
task->syntax.number = p->syn_num;
task->is_intra = p->slice.idr_flag;
h264e_dbg_func("leave\n");
return MPP_OK;
}
MPP_RET h264e_add_sei(MppPacket pkt, RK_S32 *length, RK_U8 uuid[16],
const void *data, RK_S32 size)
{
return h264e_sei_to_packet(pkt, length, H264_SEI_USER_DATA_UNREGISTERED,
uuid, data, size);
}
/*!
***********************************************************************
* \brief
* api struct interface
***********************************************************************
*/
const EncImplApi api_h264e = {
"h264e_control",
MPP_VIDEO_CodingAVC,
sizeof(H264eCtx),
0,
h264e_init,
h264e_deinit,
h264e_proc_cfg,
h264e_gen_hdr,
h264e_start,
h264e_proc_dpb,
h264e_proc_hal,
h264e_add_sei,
NULL,
NULL,
NULL,
};