/* * 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 #include #include "mpp_env.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_v2.h" #include "rc.h" #include "mpp_rc.h" #include "mpp_soc.h" #include "enc_impl_api.h" #include "mpp_enc_cfg.h" #include "mpp_enc_impl.h" RK_U32 h264e_debug = 0; typedef struct { /* config from mpp_enc */ MppClientType type; MppEncCfgSet *cfg; MppEncRefs refs; RK_U32 idr_request; RK_U32 pre_ref_idx; /* H.264 high level syntax */ H264eSps sps; H264ePps 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; /* header generation */ MppPacket hdr_pkt; void *hdr_buf; size_t hdr_size; size_t hdr_len; RK_S32 sps_offset; RK_S32 sps_len; RK_S32 pps_offset; RK_S32 pps_len; RK_S32 prefix_offset; RK_S32 prefix_len; H264ePrefixNal prefix; /* 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, MppClientType type) { MppEncRcCfg *rc_cfg = &cfg->rc; MppEncPrepCfg *prep = &cfg->prep; MppEncH264Cfg *h264 = &cfg->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->vui.vui_en = 1; cfg->tune.scene_mode = MPP_ENC_SCENE_MODE_DEFAULT; cfg->tune.deblur_en = 0; cfg->tune.vmaf_opt = 0; switch (type) { case VPU_CLIENT_VEPU1 : case VPU_CLIENT_VEPU2 : { h264->poc_type = 2; h264->log2_max_poc_lsb = 12; h264->log2_max_frame_num = 12; h264->hw_cfg.hw_poc_type = 2; h264->hw_cfg.hw_log2_max_frame_num_minus4 = 12; h264->hw_cfg.hw_split_out = 0; } break; case VPU_CLIENT_RKVENC : { h264->poc_type = 0; h264->log2_max_poc_lsb = 12; h264->log2_max_frame_num = 12; h264->chroma_cb_qp_offset = -6; h264->chroma_cr_qp_offset = -6; h264->hw_cfg.hw_poc_type = 0; h264->hw_cfg.hw_log2_max_frame_num_minus4 = 12; h264->hw_cfg.hw_split_out = 0; } break; default : { h264->poc_type = 0; h264->log2_max_poc_lsb = 12; h264->log2_max_frame_num = 12; h264->hw_cfg.hw_poc_type = 0; h264->hw_cfg.hw_log2_max_frame_num_minus4 = 12; h264->hw_cfg.hw_split_out = 0; } break; } /* * default prep: * 720p * YUV420SP */ 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->rotation_ext = MPP_ENC_ROT_0; prep->color = MPP_FRAME_SPC_UNSPECIFIED; prep->colorprim = MPP_FRAME_PRI_UNSPECIFIED; prep->colortrc = MPP_FRAME_TRC_UNSPECIFIED; prep->range = MPP_FRAME_RANGE_UNSPECIFIED; prep->mirroring = 0; prep->mirroring_ext = 0; prep->denoise = 0; prep->flip = 0; /* * default rc_cfg: * CBR * 2Mbps +-25% * 30fps * gop 60 */ 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_denom = 1; rc_cfg->fps_out_flex = 0; rc_cfg->fps_out_num = 30; rc_cfg->fps_out_denom = 1; rc_cfg->gop = 60; rc_cfg->max_reenc_times = 1; rc_cfg->max_i_prop = 30; rc_cfg->min_i_prop = 10; rc_cfg->init_ip_ratio = 160; rc_cfg->qp_init = 26; rc_cfg->qp_max = 48; rc_cfg->qp_min = 8; /* default max/min intra qp is not set */ rc_cfg->qp_max_i = 0; rc_cfg->qp_min_i = 0; rc_cfg->qp_delta_ip = 2; rc_cfg->fqp_min_i = INT_MAX; rc_cfg->fqp_min_p = INT_MAX; rc_cfg->fqp_max_i = INT_MAX; rc_cfg->fqp_max_p = INT_MAX; cfg->tune.lambda_idx_i = 6; cfg->tune.lambda_idx_p = 6; cfg->tune.atl_str = 1; cfg->tune.atr_str_i = 1; cfg->tune.atr_str_p = 1; cfg->tune.anti_flicker_str = 1; cfg->tune.deblur_str = 3; /* smart v3 parameters */ cfg->tune.bg_delta_qp_i = -10; cfg->tune.bg_delta_qp_p = -10; cfg->tune.fg_delta_qp_i = 3; cfg->tune.fg_delta_qp_p = 1; cfg->tune.bmap_qpmin_i = 30; cfg->tune.bmap_qpmin_p = 30; cfg->tune.bmap_qpmax_i = 45; cfg->tune.bmap_qpmax_p = 47; cfg->tune.min_bg_fqp = 30; cfg->tune.max_bg_fqp = 45; cfg->tune.min_fg_fqp = 25; cfg->tune.max_fg_fqp = 35; } 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->type = ctrl_cfg->type; 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, p->type); 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 void h264e_check_cfg(MppEncCfgSet *cfg) { MppEncRcCfg *rc = &cfg->rc; MppEncH264Cfg *h264 = &cfg->h264; if (rc->drop_mode == MPP_ENC_RC_DROP_FRM_PSKIP && rc->drop_gap == 0 && h264->poc_type == 2) { mpp_err("poc type 2 is conflict with successive non-reference pskip mode\n"); mpp_err("set drop gap to 1\n"); rc->drop_gap = 1; } } 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 : { h264e_check_cfg(cfg); } break; case MPP_ENC_SET_SEI_CFG : { } break; case MPP_ENC_SET_SPLIT : { } 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); h264e_pps_update(&p->pps, p->cfg); /* * NOTE: When sps/pps is update we need to update dpb and slice info */ if (!p->dpb.total_cnt) 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_offset, &p->sps_len, p->cfg); h264e_pps_to_packet(&p->pps, p->hdr_pkt, &p->pps_offset, &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); mpp_packet_add_segment_info(pkt, H264_NALU_TYPE_SPS, p->sps_offset, p->sps_len); mpp_packet_add_segment_info(pkt, H264_NALU_TYPE_PPS, p->pps_offset, p->pps_len); } h264e_dbg_func("leave\n"); return MPP_OK; } static MPP_RET h264e_start(void *ctx, HalEncTask *task) { h264e_dbg_func("enter\n"); if (mpp_frame_has_meta(task->frame)) { MppEncRefFrmUsrCfg *frm_cfg = task->frm_cfg; EncRcForceCfg *rc_force = &task->rc_task->force; MppMeta meta = mpp_frame_get_meta(task->frame); RK_S32 force_lt_idx = -1; RK_S32 force_use_lt_idx = -1; RK_S32 force_frame_qp = -1; RK_S32 base_layer_pid = -1; RK_S32 force_tid = -1; mpp_meta_get_s32(meta, KEY_ENC_MARK_LTR, &force_lt_idx); mpp_meta_get_s32(meta, KEY_ENC_USE_LTR, &force_use_lt_idx); mpp_meta_get_s32(meta, KEY_ENC_FRAME_QP, &force_frame_qp); mpp_meta_get_s32(meta, KEY_ENC_BASE_LAYER_PID, &base_layer_pid); mpp_meta_get_s32(meta, KEY_TEMPORAL_ID, &force_tid); if (force_lt_idx >= 0) { frm_cfg->force_flag |= ENC_FORCE_LT_REF_IDX; frm_cfg->force_lt_idx = force_lt_idx; } if (force_use_lt_idx >= 0) { frm_cfg->force_flag |= ENC_FORCE_REF_MODE; frm_cfg->force_ref_mode = REF_TO_LT_REF_IDX; frm_cfg->force_ref_arg = force_use_lt_idx; } if (force_tid >= 0) { frm_cfg->force_flag |= ENC_FORCE_TEMPORAL_ID; frm_cfg->force_temporal_id = force_tid; } if (force_frame_qp >= 0) { rc_force->force_flag = ENC_RC_FORCE_QP; rc_force->force_qp = force_frame_qp; } else { rc_force->force_flag &= (~ENC_RC_FORCE_QP); rc_force->force_qp = -1; } if (base_layer_pid >= 0) { H264eCtx *p = (H264eCtx *)ctx; MppEncH264Cfg *h264 = &p->cfg->h264; h264->base_layer_pid = base_layer_pid; } } h264e_dbg_func("leave\n"); return MPP_OK; } MPP_RET h264e_pskip_ref_check(H264eDpb *dpb, H264eFrmInfo *frms) { H264eDpbFrm *curr = NULL; H264eDpbFrm *refr = NULL; RK_U32 i; MPP_RET ret = MPP_OK; curr = dpb->curr; refr = dpb->refr; if (curr->status.force_pskip_is_ref) { H264eDpbFrm *temp_frm; for (i = 0; i < H264E_MAX_REFS_CNT; i++) { temp_frm = &dpb->frames[i]; if (temp_frm->slot_idx != frms->refr_idx) { temp_frm->as_pskip_ref = 0; } else { temp_frm->as_pskip_ref = 1; } } } if (!refr->status.force_pskip_is_ref && !curr->status.force_pskip_is_ref) { H264eDpbFrm *temp_frm; for (i = 0; i < H264E_MAX_REFS_CNT; i++) { temp_frm = &dpb->frames[i]; if (temp_frm) { temp_frm->as_pskip_ref = 0; } } } return ret; } 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, &p->pps, dpb->curr); // update frame usage frms->seq_idx = curr->seq_idx; frms->curr_idx = curr->slot_idx; if (refr) { if (refr->status.force_pskip_is_ref) frms->refr_idx = refr->prev_ref_idx; else frms->refr_idx = refr->slot_idx; } else { frms->refr_idx = curr->slot_idx; } // mark actual refs buf when force pskip is ref h264e_pskip_ref_check(dpb, frms); 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; h264e_dbg_func("leave\n"); return MPP_OK; } static MPP_RET h264e_proc_hal(void *ctx, HalEncTask *task) { H264eCtx *p = (H264eCtx *)ctx; MppEncH264Cfg *h264 = &p->cfg->h264; 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_DPB, &p->dpb); h264e_add_syntax(p, H264E_SYN_SLICE, &p->slice); h264e_add_syntax(p, H264E_SYN_FRAME, &p->frms); /* check max temporal layer id */ { MppEncCpbInfo *cpb_info = mpp_enc_ref_cfg_get_cpb_info(p->cfg->ref_cfg); RK_S32 cpb_max_tid = cpb_info->max_st_tid; RK_S32 cfg_max_tid = h264->max_tid; if (cpb_max_tid != cfg_max_tid) { mpp_log("max tid is update to match cpb %d -> %d\n", cfg_max_tid, cpb_max_tid); h264->max_tid = cpb_max_tid; } } /* NOTE: prefix nal is added after SEI packet and before hw_stream */ if (h264->prefix_mode || h264->max_tid) { H264ePrefixNal *prefix = &p->prefix; H264eSlice *slice = &p->slice; EncFrmStatus *frm = &task->rc_task->frm; MppPacket packet = task->packet; MppMeta meta = mpp_packet_get_meta(packet); prefix->idr_flag = slice->idr_flag; prefix->nal_ref_idc = slice->nal_reference_idc; prefix->priority_id = h264->base_layer_pid + frm->temporal_id; prefix->no_inter_layer_pred_flag = 1; prefix->dependency_id = 0; prefix->quality_id = 0; prefix->temporal_id = frm->temporal_id; prefix->use_ref_base_pic_flag = 0; prefix->discardable_flag = 0; prefix->output_flag = 1; h264e_add_syntax(p, H264E_SYN_PREFIX, &p->prefix); mpp_meta_set_s32(meta, KEY_TEMPORAL_ID, frm->temporal_id); if (!frm->is_non_ref && frm->is_lt_ref) mpp_meta_set_s32(meta, KEY_LONG_REF_IDX, frm->lt_idx); } else h264e_add_syntax(p, H264E_SYN_PREFIX, NULL); task->valid = 1; task->syntax.data = &p->syntax[0]; task->syntax.number = p->syn_num; h264e_dbg_func("leave\n"); return MPP_OK; } static MPP_RET h264e_sw_enc(void *ctx, HalEncTask *task) { H264eCtx *p = (H264eCtx *)ctx; MppEncH264Cfg *h264 = &p->cfg->h264; EncRcTaskInfo *rc_info = &task->rc_task->info; MppPacket packet = task->packet; void *pos = mpp_packet_get_pos(packet); void *data = mpp_packet_get_data(packet); size_t size = mpp_packet_get_size(packet); size_t length = mpp_packet_get_length(packet); void *base = pos + length; RK_S32 buf_size = (data + size) - (pos + length); RK_S32 slice_len = 0; RK_S32 final_len = 0; if (h264->prefix_mode || h264->max_tid) { /* add prefix first */ RK_S32 prefix_bit = h264e_slice_write_prefix_nal_unit_svc(&p->prefix, base, buf_size); prefix_bit = (prefix_bit + 7) / 8; base += prefix_bit; buf_size -= prefix_bit; final_len += prefix_bit; } /* write slice header */ slice_len = h264e_slice_write_pskip(&p->slice, base, buf_size); slice_len = (slice_len + 7) / 8; final_len += slice_len; task->length += final_len; rc_info->bit_real = task->length * 8; rc_info->quality_real = rc_info->quality_target; p->dpb.curr->prev_ref_idx = p->frms.refr_idx; mpp_packet_add_segment_info(packet, H264_NALU_TYPE_SLICE, length, final_len); mpp_buffer_sync_partial_end(mpp_packet_get_buffer(packet), length, final_len); return MPP_OK; } MPP_RET h264e_add_sei(MppPacket pkt, RK_S32 *length, RK_U8 uuid[16], const void *data, RK_S32 size) { if (uuid == uuid_refresh_cfg) { return h264e_sei_recovery_point_to_packet(pkt, length, ((RK_U32 *)data)[0] - 1); } else 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, h264e_sw_enc, };