/* * Copyright 2015 - 2017 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 "hal_h264e_vepu_v2" #include #include "mpp_mem.h" #include "mpp_common.h" #include "mpp_buffer.h" #include "mpp_enc_ref.h" #include "vepu_common.h" #include "h264e_slice.h" #include "hal_h264e_debug.h" #include "hal_h264e_vepu_v2.h" #include "hal_h264e_vpu_tbl_v2.h" typedef struct HalH264eVepuMbRcImpl_t { RK_S32 width; RK_S32 height; RK_S32 mb_w; RK_S32 mb_h; RK_S32 pels; RK_S32 mbs; RK_S32 bits_per_pic; RK_S32 mb_bit_rc_enable; /* frame rate control */ RK_S32 fps_in_num; RK_S32 fps_in_denorm; RK_S32 fps_out_num; RK_S32 fps_out_denorm; RK_S32 fps_count; RK_S32 fps_step; RK_S32 fps_threshold; /* * MAD based QP adjustment * mad_qp_change [-8..7] * mad_threshold MAD threshold div256 */ RK_S32 mad_qp_change; RK_S32 mad_threshold; /* * check point parameter */ RK_S32 check_point_count; RK_S32 check_point_distance; /* estimated first I frame qp */ RK_S32 qp_init_est; RK_S32 frame_type; RK_S32 pre_frame_type; } HalH264eVepuMbRcImpl; RK_S32 exp_golomb_signed(RK_S32 val) { RK_S32 tmp = 0; if (val > 0) val = 2 * val; else val = -2 * val + 1; while (val >> ++tmp) ; return tmp * 2 - 1; } static void vepu_swap_endian(RK_U32 *buf, RK_S32 size_bytes) { RK_U32 i = 0; RK_S32 words = size_bytes / 4; RK_U32 val, val2, tmp, tmp2; mpp_assert((size_bytes % 8) == 0); while (words > 0) { val = buf[i]; tmp = 0; tmp |= (val & 0xFF) << 24; tmp |= (val & 0xFF00) << 8; tmp |= (val & 0xFF0000) >> 8; tmp |= (val & 0xFF000000) >> 24; { val2 = buf[i + 1]; tmp2 = 0; tmp2 |= (val2 & 0xFF) << 24; tmp2 |= (val2 & 0xFF00) << 8; tmp2 |= (val2 & 0xFF0000) >> 8; tmp2 |= (val2 & 0xFF000000) >> 24; buf[i] = tmp2; words--; i++; } buf[i] = tmp; words--; i++; } } static void vepu_write_cabac_table(MppBuffer buf, RK_S32 cabac_init_idc) { const RK_S32(*context)[460][2]; RK_S32 i, j, qp; RK_U8 table[H264E_CABAC_TABLE_BUF_SIZE] = {0}; for (qp = 0; qp < 52; qp++) { /* All QP values */ for (j = 0; j < 2; j++) { /* Intra/Inter */ if (j == 0) /*lint -e(545) */ context = &h264_context_init_intra; else /*lint -e(545) */ context = &h264_context_init[cabac_init_idc]; for (i = 0; i < 460; i++) { RK_S32 m = (RK_S32)(*context)[i][0]; RK_S32 n = (RK_S32)(*context)[i][1]; RK_S32 pre_ctx_state = MPP_CLIP3(1, 126, ((m * (RK_S32)qp) >> 4) + n); if (pre_ctx_state <= 63) table[qp * 464 * 2 + j * 464 + i] = (RK_U8)((63 - pre_ctx_state) << 1); else table[qp * 464 * 2 + j * 464 + i] = (RK_U8)(((pre_ctx_state - 64) << 1) | 1); } } } vepu_swap_endian((RK_U32 *)table, H264E_CABAC_TABLE_BUF_SIZE); mpp_buffer_write(buf, 0, table, H264E_CABAC_TABLE_BUF_SIZE); } MPP_RET h264e_vepu_buf_init(HalH264eVepuBufs *bufs) { MPP_RET ret = MPP_OK; hal_h264e_dbg_buffer("enter %p\n", bufs); memset(bufs, 0, sizeof(*bufs)); // do not create buffer on cavlc case bufs->cabac_init_idc = -1; ret = mpp_buffer_group_get_internal(&bufs->group, MPP_BUFFER_TYPE_ION); if (ret) mpp_err_f("get buffer group failed ret %d\n", ret); hal_h264e_dbg_buffer("leave %p\n", bufs); return ret; } MPP_RET h264e_vepu_buf_deinit(HalH264eVepuBufs *bufs) { RK_S32 i; hal_h264e_dbg_buffer("enter %p\n", bufs); if (bufs->cabac_table) mpp_buffer_put(bufs->cabac_table); if (bufs->nal_size_table) mpp_buffer_put(bufs->nal_size_table); for (i = 0; i < (RK_S32)MPP_ARRAY_ELEMS(bufs->frm_buf); i++) { if (bufs->frm_buf[i]) mpp_buffer_put(bufs->frm_buf[i]); } if (bufs->group) mpp_buffer_group_put(bufs->group); memset(bufs, 0, sizeof(*bufs)); bufs->cabac_init_idc = -1; hal_h264e_dbg_buffer("leave %p\n", bufs); return MPP_OK; } MPP_RET h264e_vepu_buf_set_cabac_idc(HalH264eVepuBufs *bufs, RK_S32 idc) { hal_h264e_dbg_buffer("enter %p\n", bufs); if (idc >= 0 && !bufs->cabac_table) mpp_buffer_get(bufs->group, &bufs->cabac_table, H264E_CABAC_TABLE_BUF_SIZE); if (bufs->cabac_table && idc != bufs->cabac_init_idc && idc >= 0) vepu_write_cabac_table(bufs->cabac_table, idc); bufs->cabac_init_idc = idc; hal_h264e_dbg_buffer("leave %p\n", bufs); return MPP_OK; } MPP_RET h264e_vepu_buf_set_frame_size(HalH264eVepuBufs *bufs, RK_S32 w, RK_S32 h) { RK_S32 aligned_w = MPP_ALIGN(w, 16); RK_S32 aligned_h = MPP_ALIGN(h, 16); size_t yuv_size = aligned_w * aligned_h; size_t frm_size = yuv_size * 3 / 2; RK_S32 i; RK_S32 cnt = (RK_S32)MPP_ARRAY_ELEMS(bufs->frm_buf); hal_h264e_dbg_buffer("enter %p\n", bufs); mpp_assert(frm_size); if (frm_size != bufs->frm_size) { if (bufs->frm_size) { /* reallocate only on larger frame size to save time */ mpp_log("new frame size [%d:%d] require buffer %d not equal to %d\n", w, h, frm_size, bufs->frm_size); } for (i = 0; i < cnt; i++) { if (bufs->frm_buf[i]) { mpp_buffer_put(bufs->frm_buf[i]); bufs->frm_buf[i] = NULL; bufs->frm_cnt--; } } } bufs->mb_h = aligned_h >> 4; if (bufs->mb_h) bufs->nal_tab_size = MPP_ALIGN((bufs->mb_h + 1) * sizeof(RK_U32), 8); else bufs->nal_tab_size = 0; bufs->yuv_size = yuv_size; bufs->frm_size = frm_size; hal_h264e_dbg_buffer("leave %p\n", bufs); return MPP_OK; } MppBuffer h264e_vepu_buf_get_nal_size_table(HalH264eVepuBufs *bufs) { MppBuffer buf = bufs->nal_size_table; hal_h264e_dbg_buffer("enter %p\n", bufs); if (NULL == buf) { mpp_buffer_get(bufs->group, &buf, bufs->nal_tab_size); mpp_assert(buf); bufs->nal_size_table = buf; } hal_h264e_dbg_buffer("leave %p\n", bufs); return buf; } MppBuffer h264e_vepu_buf_get_frame_buffer(HalH264eVepuBufs *bufs, RK_S32 index) { MppBuffer buf = bufs->frm_buf[index]; hal_h264e_dbg_buffer("enter\n", bufs); if (NULL == buf) { mpp_buffer_get(bufs->group, &buf, bufs->frm_size); mpp_assert(buf); bufs->frm_buf[index] = buf; bufs->frm_cnt++; } hal_h264e_dbg_buffer("leave %p\n", bufs); return buf; } MPP_RET h264e_vepu_prep_setup(HalH264eVepuPrep *prep, MppEncPrepCfg *cfg) { MPP_RET ret = MPP_OK; MppFrameFormat format = cfg->format; VepuFormatCfg fmt_cfg; hal_h264e_dbg_buffer("enter\n"); prep->src_fmt = format; prep->src_w = cfg->width; prep->src_h = cfg->height; if (!get_vepu_fmt(&fmt_cfg, format)) { prep->r_mask_msb = fmt_cfg.r_mask; prep->g_mask_msb = fmt_cfg.g_mask; prep->b_mask_msb = fmt_cfg.b_mask; prep->swap_8_in = fmt_cfg.swap_8_in; prep->swap_16_in = fmt_cfg.swap_16_in; prep->swap_32_in = fmt_cfg.swap_32_in; prep->src_fmt = fmt_cfg.format; } else { prep->src_fmt = VEPU_FMT_BUTT; } if (format < MPP_FRAME_FMT_RGB) { // YUV case if (prep->src_fmt == VEPU_FMT_BUTT) { mpp_err("vepu do not support input frame format %d\n", format); ret = MPP_NOK; } prep->color_conversion_coeff_a = 0; prep->color_conversion_coeff_b = 0; prep->color_conversion_coeff_c = 0; prep->color_conversion_coeff_e = 0; prep->color_conversion_coeff_f = 0; } else { if (prep->src_fmt == VEPU_FMT_BUTT) { mpp_err("vepu do not support input frame format %d\n", format); ret = MPP_NOK; } switch (cfg->color) { case MPP_FRAME_SPC_RGB : { /* BT.601 */ /* Y = 0.2989 R + 0.5866 G + 0.1145 B * Cb = 0.5647 (B - Y) + 128 * Cr = 0.7132 (R - Y) + 128 */ prep->color_conversion_coeff_a = 19589; prep->color_conversion_coeff_b = 38443; prep->color_conversion_coeff_c = 7504; prep->color_conversion_coeff_e = 37008; prep->color_conversion_coeff_f = 46740; } break; case MPP_FRAME_SPC_BT709 : { /* BT.709 */ /* Y = 0.2126 R + 0.7152 G + 0.0722 B * Cb = 0.5389 (B - Y) + 128 * Cr = 0.6350 (R - Y) + 128 */ prep->color_conversion_coeff_a = 13933; prep->color_conversion_coeff_b = 46871; prep->color_conversion_coeff_c = 4732; prep->color_conversion_coeff_e = 35317; prep->color_conversion_coeff_f = 41615; } break; default : { prep->color_conversion_coeff_a = 19589; prep->color_conversion_coeff_b = 38443; prep->color_conversion_coeff_c = 7504; prep->color_conversion_coeff_e = 37008; prep->color_conversion_coeff_f = 46740; } break; } } /* NOTE: vepu only support 8bit encoding and stride must match with width align to 16 */ RK_S32 hor_stride = cfg->hor_stride; RK_S32 ver_stride = cfg->ver_stride; VepuStrideCfg *stride_cfg = &prep->stride_cfg; prep->offset_cb = 0; prep->offset_cr = 0; get_vepu_pixel_stride(stride_cfg, prep->src_w, hor_stride, format); prep->pixel_stride = stride_cfg->pixel_stride; hor_stride = stride_cfg->pixel_stride * stride_cfg->pixel_size; switch (format & MPP_FRAME_FMT_MASK) { case MPP_FMT_YUV420SP : { prep->offset_cb = hor_stride * ver_stride; prep->size_y = hor_stride * MPP_ALIGN(prep->src_h, 16); prep->size_c = hor_stride / 2 * MPP_ALIGN(prep->src_h / 2, 8); } break; case MPP_FMT_YUV420P : { prep->offset_cb = hor_stride * ver_stride; prep->offset_cr = prep->offset_cb + ((hor_stride * ver_stride) / 4); prep->size_y = hor_stride * MPP_ALIGN(prep->src_h, 16); prep->size_c = hor_stride / 2 * MPP_ALIGN(prep->src_h / 2, 8); } break; case MPP_FMT_YUV422_YUYV : case MPP_FMT_YUV422_UYVY : { prep->size_y = hor_stride * MPP_ALIGN(prep->src_h, 16); prep->size_c = 0; } break; case MPP_FMT_RGB565 : case MPP_FMT_BGR565 : case MPP_FMT_RGB555 : case MPP_FMT_BGR555 : case MPP_FMT_RGB444 : case MPP_FMT_BGR444 : { prep->size_y = hor_stride * MPP_ALIGN(prep->src_h, 16); prep->size_c = 0; } break; case MPP_FMT_ARGB8888 : case MPP_FMT_ABGR8888 : case MPP_FMT_RGBA8888 : case MPP_FMT_BGRA8888 : case MPP_FMT_RGB101010 : case MPP_FMT_BGR101010 : { prep->size_y = hor_stride * MPP_ALIGN(prep->src_h, 16); prep->size_c = 0; } break; default: { mpp_err_f("invalid format %d", format); ret = MPP_NOK; } } hal_h264e_dbg_buffer("leave\n"); return ret; } MPP_RET h264e_vepu_prep_get_addr(HalH264eVepuPrep *prep, MppBuffer buffer, RK_U32 (*addr)[3]) { RK_U32 fd = (RK_U32)mpp_buffer_get_fd(buffer); size_t size = mpp_buffer_get_size(buffer); hal_h264e_dbg_buffer("enter\n"); (*addr)[0] = fd; (*addr)[1] = fd; (*addr)[2] = fd; if (size < prep->size_y) mpp_err("warnning: input buffer size 0x%x is smaller than required size 0x%x", size, prep->size_y); if (prep->size_c && (prep->offset_cb || prep->offset_cr)) { if (prep->offset_cb && (size < prep->offset_cb + prep->size_c)) mpp_err("warnning: input buffer size 0x%x is smaller than cb requirement 0x%x + 0x%x", size, prep->offset_cb, prep->size_c); if (prep->offset_cr && (size < prep->offset_cr + prep->size_c)) mpp_err("warnning: input buffer size 0x%x is smaller than cb requirement 0x%x + 0x%x", size, prep->offset_cr, prep->size_c); } hal_h264e_dbg_buffer("leave\n"); return MPP_OK; } MPP_RET h264e_vepu_mbrc_init(HalH264eVepuMbRcCtx *ctx, HalH264eVepuMbRc *mbrc) { MPP_RET ret = MPP_OK; HalH264eVepuMbRcImpl *p = mpp_calloc(HalH264eVepuMbRcImpl, 1); if (!p) { mpp_err_f("failed to alloc rate control context\n"); ret = MPP_ERR_NOMEM; } memset(mbrc, 0, sizeof(*mbrc)); mbrc->qp_init = -1; mbrc->qp_max = 48; mbrc->qp_min = 16; *ctx = p; return ret; } MPP_RET h264e_vepu_mbrc_deinit(HalH264eVepuMbRcCtx ctx) { MPP_FREE(ctx); return MPP_OK; } MPP_RET h264e_vepu_mbrc_setup(HalH264eVepuMbRcCtx ctx, MppEncCfgSet*cfg) { HalH264eVepuMbRcImpl *p = (HalH264eVepuMbRcImpl *)ctx; MppEncPrepCfg *prep = &cfg->prep; MppEncRcCfg *rc = &cfg->rc; MppEncHwCfg* hw_cfg = &cfg->hw; hal_h264e_dbg_func("enter\n"); // get necessary parameter from config p->width = prep->width; p->height = prep->height; p->mb_w = MPP_ALIGN(prep->width, 16) / 16; p->mb_h = MPP_ALIGN(prep->height, 16) / 16; p->pels = p->width * p->height; p->mbs = p->mb_w * p->mb_h; p->bits_per_pic = axb_div_c(rc->bps_target, rc->fps_out_denorm, rc->fps_out_num); mpp_assert(p->pels); // frame rate control mpp_assert(rc->fps_out_num / rc->fps_out_denorm <= rc->fps_in_num / rc->fps_in_denorm); p->fps_in_num = rc->fps_in_num; p->fps_in_denorm = rc->fps_in_denorm; p->fps_out_num = rc->fps_out_num; p->fps_out_denorm = rc->fps_out_denorm; p->fps_step = rc->fps_in_denorm * rc->fps_out_num; p->fps_threshold = rc->fps_in_num * rc->fps_out_denorm; p->fps_count = p->fps_threshold; // if not constant p->mb_bit_rc_enable = !hw_cfg->mb_rc_disable && (rc->rc_mode != MPP_ENC_RC_MODE_FIXQP); hal_h264e_dbg_rc("estimated init qp %d\n", p->qp_init_est); // init first frame mad parameter p->mad_qp_change = 2; p->mad_threshold = 256 * 6; // init check point position if (p->mb_bit_rc_enable) { p->check_point_count = MPP_MIN(p->mb_h - 1, VEPU_CHECK_POINTS_MAX); p->check_point_distance = p->mbs / (p->check_point_count + 1); } else { p->check_point_count = 0; p->check_point_distance = 0; } p->frame_type = INTRA_FRAME; p->pre_frame_type = INTRA_FRAME; hal_h264e_dbg_func("leave\n"); return MPP_OK; } #define WORD_CNT_MAX 65535 MPP_RET h264e_vepu_mbrc_prepare(HalH264eVepuMbRcCtx ctx, HalH264eVepuMbRc *mbrc, EncRcTask *rc_task) { HalH264eVepuMbRcImpl *p = (HalH264eVepuMbRcImpl *)ctx; EncFrmStatus *frm = &rc_task->frm; EncRcTaskInfo *info = &rc_task->info; RK_S32 i; const RK_S32 sscale = 256; RK_S32 scaler, srcPrm; RK_S32 tmp, nonZeroTarget; RK_S32 coeffCntMax = p->mbs * 24 * 16; mbrc->qp_init = info->quality_target; mbrc->qp_min = info->quality_min; mbrc->qp_max = info->quality_max; mbrc->mad_qp_change = 0; mbrc->mad_threshold = 0; mbrc->cp_distance_mbs = 0; if (!p->mb_bit_rc_enable) return MPP_OK; p->pre_frame_type = p->frame_type; p->frame_type = (frm->is_intra) ? INTRA_FRAME : INTER_P_FRAME; if (mbrc->rlc_count == 0) { mbrc->rlc_count = 1; } srcPrm = axb_div_c(mbrc->out_strm_size * 8, 256, mbrc->rlc_count); /* Disable Mb Rc for Intra Slices, because coeffTarget will be wrong */ if (frm->is_intra || srcPrm == 0) return 0; /* Required zero cnt */ nonZeroTarget = axb_div_c(info->bit_target, 256, srcPrm); nonZeroTarget = MPP_MIN(coeffCntMax, MPP_MAX(0, nonZeroTarget)); nonZeroTarget = MPP_MIN(0x7FFFFFFFU / 1024U, (RK_U32)nonZeroTarget); if (nonZeroTarget > 0) { scaler = axb_div_c(nonZeroTarget, sscale, (RK_S32) p->mbs); } else { return 0; } if ((p->frame_type != p->pre_frame_type) || (mbrc->rlc_count == 0)) { for (i = 0; i < VEPU_CHECK_POINTS_MAX; i++) { tmp = (scaler * (p->check_point_distance * (i + 1) + 1)) / sscale; tmp = MPP_MIN(WORD_CNT_MAX, tmp / 32 + 1); if (tmp < 0) tmp = WORD_CNT_MAX; /* Detect overflow */ mbrc->cp_target[i] = tmp; /* div32 for regs */ } tmp = axb_div_c(p->bits_per_pic, 256, srcPrm); } else { for (i = 0; i < VEPU_CHECK_POINTS_MAX; i++) { tmp = (RK_S32) (mbrc->cp_usage[i] * scaler) / sscale; tmp = MPP_MIN(WORD_CNT_MAX, tmp / 32 + 1); if (tmp < 0) tmp = WORD_CNT_MAX; /* Detect overflow */ mbrc->cp_target[i] = tmp; /* div32 for regs */ } tmp = axb_div_c(p->bits_per_pic, 256, srcPrm); } mbrc->cp_error[0] = -tmp * 3; mbrc->cp_delta_qp[0] = 3; mbrc->cp_error[1] = -tmp * 2; mbrc->cp_delta_qp[1] = 2; mbrc->cp_error[2] = -tmp * 1; mbrc->cp_delta_qp[2] = 1; mbrc->cp_error[3] = tmp * 1; mbrc->cp_delta_qp[3] = 0; mbrc->cp_error[4] = tmp * 2; mbrc->cp_delta_qp[4] = -1; mbrc->cp_error[5] = tmp * 3; mbrc->cp_delta_qp[5] = -2; mbrc->cp_error[6] = tmp * 4; mbrc->cp_delta_qp[6] = -3; for (i = 0; i < VEPU_CTRL_LEVELS; i++) { tmp = mbrc->cp_error[i]; tmp = mpp_clip(tmp / 4, -32768, 32767); mbrc->cp_error[i] = tmp; } mbrc->cp_distance_mbs = p->check_point_distance; return MPP_OK; } MPP_RET h264e_vepu_slice_split_cfg(H264eSlice *slice, HalH264eVepuMbRc *mbrc, EncRcTask *rc_task, MppEncCfgSet *cfg) { MppEncSliceSplit *split = &cfg->split; EncRcTaskInfo *info = &rc_task->info; RK_U32 slice_mb_rows = 0; hal_h264e_dbg_func("enter\n"); switch (split->split_mode) { case MPP_ENC_SPLIT_NONE : { mbrc->slice_size_mb_rows = 0; } break; case MPP_ENC_SPLIT_BY_BYTE : { RK_U32 mb_per_col = (cfg->prep.height + 15) / 16; mpp_assert(split->split_arg > 0); RK_U32 slice_num = info->bit_target / (split->split_arg * 8); if (slice_num <= 0) slice_num = 4; slice_mb_rows = (mb_per_col + slice_num - 1) / slice_num; mbrc->slice_size_mb_rows = mpp_clip(slice_mb_rows, 2, 127); } break; case MPP_ENC_SPLIT_BY_CTU : { mpp_assert(split->split_arg > 0); RK_U32 mb_per_line = (cfg->prep.width + 15) / 16; slice_mb_rows = (split->split_arg + mb_per_line - 1) / mb_per_line; mbrc->slice_size_mb_rows = mpp_clip(slice_mb_rows, 2, 127); } break; default : { mpp_log_f("invalide slice split mode %d\n", split->split_mode); } break; } slice->is_multi_slice = (mbrc->slice_size_mb_rows > 0); split->change = 0; hal_h264e_dbg_func("leave\n"); return MPP_OK; } MPP_RET h264e_vepu_mbrc_update(HalH264eVepuMbRcCtx ctx, HalH264eVepuMbRc *mbrc) { HalH264eVepuMbRcImpl *p = (HalH264eVepuMbRcImpl *)ctx; (void) p; (void) mbrc; hal_h264e_dbg_func("enter\n"); hal_h264e_dbg_func("leave\n"); return MPP_OK; } #define START_CODE 0x000001 ///< start_code_prefix_one_3bytes static RK_S32 get_next_nal(RK_U8 *buf, RK_S32 *length) { RK_S32 i, consumed = 0; RK_S32 len = *length; RK_U8 *tmp_buf = buf; /* search start code */ while (len >= 4) { if (tmp_buf[2] == 0) { len--; tmp_buf++; continue; } if (tmp_buf[0] != 0 || tmp_buf[1] != 0 || tmp_buf[2] != 1) { RK_U32 state = (RK_U32) - 1; RK_S32 has_nal = 0; for (i = 0; i < (RK_S32)len; i++) { state = (state << 8) | tmp_buf[i]; if (((state >> 8) & 0xFFFFFF) == START_CODE) { has_nal = 1; i = i - 3; break; } } if (has_nal) { len -= i; tmp_buf += i; consumed = *length - len - 1; break; } consumed = *length; break; } tmp_buf += 3; len -= 3; } *length = *length - consumed; return consumed; } MPP_RET h264e_vepu_stream_amend_init(HalH264eVepuStreamAmend *ctx) { memset(ctx, 0, sizeof(*ctx)); ctx->buf_size = SZ_128K; return MPP_OK; } MPP_RET h264e_vepu_stream_amend_deinit(HalH264eVepuStreamAmend *ctx) { MPP_FREE(ctx->src_buf); MPP_FREE(ctx->dst_buf); return MPP_OK; } MPP_RET h264e_vepu_stream_amend_config(HalH264eVepuStreamAmend *ctx, MppPacket packet, MppEncCfgSet *cfg, H264eSlice *slice, H264ePrefixNal *prefix) { MppEncRefCfgImpl *ref = (MppEncRefCfgImpl *)cfg->ref_cfg; if (ref->lt_cfg_cnt || ref->st_cfg_cnt > 1) { ctx->enable = 1; ctx->slice_enabled = 0; if (NULL == ctx->dst_buf) ctx->dst_buf = mpp_calloc(RK_U8, ctx->buf_size); if (NULL == ctx->src_buf) ctx->src_buf = mpp_calloc(RK_U8, ctx->buf_size); } else { MPP_FREE(ctx->dst_buf); MPP_FREE(ctx->src_buf); memset(ctx, 0, sizeof(*ctx)); } ctx->slice = slice; ctx->prefix = prefix; ctx->packet = packet; ctx->buf_base = mpp_packet_get_length(packet); ctx->old_length = 0; ctx->new_length = 0; return MPP_OK; } MPP_RET h264e_vepu_stream_amend_proc(HalH264eVepuStreamAmend *ctx) { H264ePrefixNal *prefix = ctx->prefix; H264eSlice *slice = ctx->slice; MppPacket pkt = ctx->packet; RK_U8 *p = mpp_packet_get_pos(pkt); RK_S32 size = mpp_packet_get_size(pkt); RK_S32 base = ctx->buf_base; RK_S32 len = ctx->old_length; RK_S32 hw_len_bit = 0; RK_S32 sw_len_bit = 0; RK_S32 hw_len_byte = 0; RK_S32 sw_len_byte = 0; RK_S32 diff_size = 0; RK_S32 tail_0bit = 0; RK_U8 tail_byte = 0; RK_U8 tail_tmp = 0; RK_U8 *dst_buf = NULL; RK_S32 buf_size; RK_S32 final_len = 0; RK_S32 last_slice = 0; { RK_S32 more_buf = 0; while (len > ctx->buf_size - 16) { ctx->buf_size *= 2; more_buf = 1; } if (more_buf) { MPP_FREE(ctx->src_buf); MPP_FREE(ctx->dst_buf); ctx->src_buf = mpp_malloc(RK_U8, ctx->buf_size); ctx->dst_buf = mpp_malloc(RK_U8, ctx->buf_size); } } memset(ctx->dst_buf, 0, ctx->buf_size); memset(ctx->src_buf, 0, ctx->buf_size); dst_buf = ctx->dst_buf; buf_size = ctx->buf_size; p += base; do { RK_U32 nal_len = 0; tail_0bit = 0; // copy hw stream to stream buffer first if (slice->is_multi_slice) { nal_len = get_next_nal(p, &len); memcpy(ctx->src_buf, p, nal_len); p += nal_len; last_slice = (len == 0); } else { memcpy(ctx->src_buf, p, len); nal_len = len; last_slice = 1; } hal_h264e_dbg_amend("nal_len %d multi %d last %d prefix %p\n", nal_len, slice->is_multi_slice, last_slice, prefix); if (prefix) { /* add prefix for each slice */ RK_S32 prefix_bit = h264e_slice_write_prefix_nal_unit_svc(prefix, dst_buf, buf_size); prefix_bit = (prefix_bit + 7) / 8; dst_buf += prefix_bit; buf_size -= prefix_bit; final_len += prefix_bit; } H264eSlice slice_rd; memcpy(&slice_rd, slice, sizeof(slice_rd)); slice_rd.log2_max_frame_num = 16; slice_rd.pic_order_cnt_type = 2; hw_len_bit = h264e_slice_read(&slice_rd, ctx->src_buf, size); // write new header to header buffer slice->qp_delta = slice_rd.qp_delta; slice->first_mb_in_slice = slice_rd.first_mb_in_slice; sw_len_bit = h264e_slice_write(slice, dst_buf, buf_size); hw_len_byte = (hw_len_bit + 7) / 8; sw_len_byte = (sw_len_bit + 7) / 8; tail_byte = ctx->src_buf[nal_len - 1]; tail_tmp = tail_byte; while (!(tail_tmp & 1) && tail_0bit < 8) { tail_tmp >>= 1; tail_0bit++; } mpp_assert(tail_0bit < 8); // move the reset slice data from src buffer to dst buffer diff_size = h264e_slice_move(dst_buf, ctx->src_buf, sw_len_bit, hw_len_bit, nal_len); hal_h264e_dbg_amend("tail 0x%02x %d hw_hdr %d sw_hdr %d len %d hw_byte %d sw_byte %d diff %d\n", tail_byte, tail_0bit, hw_len_bit, sw_len_bit, nal_len, hw_len_byte, sw_len_byte, diff_size); if (slice->entropy_coding_mode) { memcpy(dst_buf + sw_len_byte, ctx->src_buf + hw_len_byte, nal_len - hw_len_byte); final_len += nal_len - hw_len_byte + sw_len_byte; nal_len = nal_len - hw_len_byte + sw_len_byte; } else { RK_S32 hdr_diff_bit = sw_len_bit - hw_len_bit; RK_S32 bit_len = nal_len * 8 - tail_0bit + hdr_diff_bit; RK_S32 new_len = (bit_len + diff_size * 8 + 7) / 8; hal_h264e_dbg_amend("frm %4d %c len %d bit hw %d sw %d byte hw %d sw %d diff %d -> %d\n", slice->frame_num, (slice->idr_flag ? 'I' : 'P'), nal_len, hw_len_bit, sw_len_bit, hw_len_byte, sw_len_byte, diff_size, new_len); hal_h264e_dbg_amend("%02x %02x %02x %02x -> %02x %02x %02x %02x\n", ctx->src_buf[nal_len - 4], ctx->src_buf[nal_len - 3], ctx->src_buf[nal_len - 2], ctx->src_buf[nal_len - 1], dst_buf[new_len - 4], dst_buf[new_len - 3], dst_buf[new_len - 2], dst_buf[new_len - 1]); nal_len = new_len; final_len += new_len; } if (last_slice) { p = mpp_packet_get_pos(pkt); p += base; memcpy(p, ctx->dst_buf, final_len); if (slice->entropy_coding_mode) { if (final_len < ctx->old_length) memset(p + final_len, 0, ctx->old_length - final_len); } else p[final_len] = 0; break; } dst_buf += nal_len; buf_size -= nal_len; } while (1); ctx->new_length = final_len; return MPP_OK; } MPP_RET h264e_vepu_stream_amend_sync_ref_idc(HalH264eVepuStreamAmend *ctx) { H264eSlice *slice = ctx->slice; MppPacket pkt = ctx->packet; RK_S32 base = ctx->buf_base; RK_S32 len = ctx->old_length; RK_U8 *p = mpp_packet_get_pos(pkt) + base; RK_U8 val = p[4]; RK_S32 hw_nal_ref_idc = (val >> 5) & 0x3; RK_S32 sw_nal_ref_idc = slice->nal_reference_idc; if (hw_nal_ref_idc == sw_nal_ref_idc) return MPP_OK; /* fix nal_ref_idc in all slice */ if (!slice->is_multi_slice) { /* single slice do NOT scan */ val = val & (~0x60); val |= (sw_nal_ref_idc << 5) & 0x60; p[4] = val; return MPP_OK; } /* multi-slice fix each nal_ref_idc */ do { RK_U32 nal_len = get_next_nal(p, &len); val = p[4]; val = val & (~0x60); val |= (sw_nal_ref_idc << 5) & 0x60; p[4] = val; if (len == 0) break; p += nal_len; } while (1); return MPP_OK; }