/* * 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. */ #if defined(_WIN32) #include "vld.h" #endif #define MODULE_TAG "mpi_rc_test" #include #include #include "rk_mpi.h" #include "mpp_log.h" #include "mpp_env.h" #include "mpp_time.h" #include "mpp_common.h" #include "mpp_mem.h" #include "utils.h" #include "vpu_api.h" #include "mpp_parse_cfg.h" #include "mpp_event_trigger.h" #define MPI_RC_FILE_NAME_LEN 256 #define MPI_DEC_STREAM_SIZE (SZ_1K) #define MPI_BIT_DEPTH 8 #define MPI_PIXEL_MAX ((1 << MPI_BIT_DEPTH) - 1) typedef RK_U8 pixel; typedef struct { double psnr_y; double ssim_y; RK_U32 avg_bitrate; // Every sequence, byte per second RK_U32 ins_bitrate; // Every second, byte per second RK_U32 frame_size; // Every frame, byte } MpiRcStat; typedef struct { FILE *fp_input; FILE *fp_enc_out; FILE *fp_stat; } MpiRcFile; typedef struct { char file_input[MPI_RC_FILE_NAME_LEN]; char file_enc_out[MPI_RC_FILE_NAME_LEN]; char file_stat[MPI_RC_FILE_NAME_LEN]; char file_config[MPI_RC_FILE_NAME_LEN]; MppCodingType type; RK_U32 debug; RK_U32 num_frames; RK_U32 psnr_en; RK_U32 ssim_en; RK_U32 have_input; RK_U32 have_enc_out; RK_U32 have_stat_out; RK_U32 have_config_file; struct rc_test_config cfg; struct event_ctx *ectx; struct event_packet e; } MpiRcTestCmd; typedef struct { MpiRcTestCmd cmd; MpiRcStat stat; MpiRcFile file; RK_U8 *com_buf; MppCtx enc_ctx; MppCtx dec_ctx_post; MppCtx dec_ctx_pre; MppApi *enc_mpi; MppApi *dec_mpi_post; MppApi *dec_mpi_pre; MppEncRcCfg rc_cfg; MppEncPrepCfg prep_cfg; MppEncCodecCfg codec_cfg; MppBufferGroup pkt_grp; MppBuffer enc_pkt_buf; MppPacket enc_pkt; MppPacket dec_pkt_post; MppPacket dec_pkt_pre; MppTask enc_in_task; MppTask enc_out_task; RK_U8 *dec_in_buf_post; RK_U8 *dec_in_buf_pre; RK_U32 dec_in_buf_pre_size; RK_U32 pkt_eos; RK_S32 frm_eos; RK_U32 frm_idx; RK_U32 calc_base_idx; RK_U32 stream_size_1s; } MpiRc2TestCtx; static OptionInfo mpi_rc_cmd[] = { {"i", "input_file", "input bitstream file"}, {"o", "output_file", "output bitstream file, "}, {"t", "type", "output stream coding type"}, {"n", "max frame number", "max encoding frame number"}, {"d", "debug", "debug flag"}, {"s", "stat_file", "stat output file name"}, {"c", "rc test item", "rc test item flags, one bit each item: roi|force_intra|gop|fps|bps" }, {"g", "config file", "read config from a file"}, {"p", "enable psnr", "enable psnr calculate"}, {"m", "enable ssim", "enable ssim calculate"}, }; /* a callback to notify test that a event occur */ static void event_occur(void *parent, void *event) { struct rc_event *e = (struct rc_event *)event; int ret; MpiRc2TestCtx *ctx = (MpiRc2TestCtx *)parent; MppCtx *enc_ctx = &ctx->enc_ctx; MppApi *enc_mpi = ctx->enc_mpi; MppEncRcCfg rc_cfg; mpp_log("event %d occur, set fps %f, bps %d\n", e->idx, e->fps, e->bps); ret = enc_mpi->control(*enc_ctx, MPP_ENC_GET_RC_CFG, &rc_cfg); if (ret < 0) { mpp_err("get encoder rc config failed\n"); return; } rc_cfg.bps_target = e->bps; rc_cfg.bps_max = e->bps * 2; rc_cfg.bps_min = e->bps / 2; rc_cfg.fps_in_num = rc_cfg.fps_out_num = e->fps; rc_cfg.fps_in_flex = rc_cfg.fps_out_flex = 1; rc_cfg.change = MPP_ENC_RC_CFG_CHANGE_BPS; ret = enc_mpi->control(*enc_ctx, MPP_ENC_SET_RC_CFG, &rc_cfg); if (ret) { mpp_err("set encoder rc config failed\n"); } ctx->calc_base_idx = ctx->frm_idx; } static void mpi_rc_deinit(MpiRc2TestCtx *ctx) { MpiRcFile *file = &ctx->file; if (file->fp_enc_out) { fclose(file->fp_enc_out); file->fp_enc_out = NULL; } if (file->fp_stat) { fclose(file->fp_stat); file->fp_stat = NULL; } if (file->fp_input) { fclose(file->fp_input); file->fp_input = NULL; } MPP_FREE(ctx->com_buf); if (ctx->cmd.ectx) event_ctx_release(ctx->cmd.ectx); } static MPP_RET mpi_rc_init(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; MpiRcTestCmd *cmd = &ctx->cmd; MpiRcFile *file = &ctx->file; if (cmd->have_input) { file->fp_input = fopen(cmd->file_input, "rb"); if (NULL == file->fp_input) { mpp_err("failed to open input file %s\n", cmd->file_input); mpp_err("create default yuv image for test\n"); } } if (cmd->have_enc_out) { file->fp_enc_out = fopen(cmd->file_enc_out, "w+b"); if (NULL == file->fp_enc_out) { mpp_err("failed to open enc output file %s\n", cmd->file_enc_out); ret = MPP_ERR_OPEN_FILE; goto err; } } if (cmd->have_stat_out) { file->fp_stat = fopen(cmd->file_stat, "w+b"); if (NULL == file->fp_stat) { mpp_err("failed to open stat file %s\n", cmd->file_stat); ret = MPP_ERR_OPEN_FILE; goto err; } fprintf(file->fp_stat, "frame,size(byte),psnr,ssim,ins_bitrate(byte/s)," "avg_bitrate(byte/s)\n"); } ctx->com_buf = mpp_malloc(RK_U8, 1920 * 1080 * 2); if (ctx->com_buf == NULL) { mpp_err_f("ctx->com_buf malloc failed"); ret = MPP_NOK; goto err; } if (cmd->have_config_file) { if (0 > mpp_parse_config(cmd->file_config, &cmd->cfg)) { mpp_err("parse config failed\n"); cmd->have_config_file = 0; } else { int i; cmd->e.cnt = cmd->cfg.event_cnt; cmd->e.loop = cmd->cfg.loop; for (i = 0; i < cmd->cfg.event_cnt; ++i) { cmd->e.e[i].idx = cmd->cfg.event[i].idx; cmd->e.e[i].event = &cmd->cfg.event[i]; } } } err: return ret; } static MPP_RET mpi_rc_cmp_frame(MppFrame frame_in, MppFrame frame_out) { RK_U8 *enc_buf = (RK_U8 *)mpp_buffer_get_ptr(mpp_frame_get_buffer(frame_in)); RK_U8 *dec_buf = (RK_U8 *)mpp_buffer_get_ptr(mpp_frame_get_buffer(frame_out)); RK_U32 enc_width = mpp_frame_get_width(frame_in); RK_U32 enc_height = mpp_frame_get_height(frame_in); RK_U32 dec_width = mpp_frame_get_width(frame_out); RK_U32 dec_height = mpp_frame_get_height(frame_out); if (!enc_buf) { mpp_err_f("enc buf is NULL"); return MPP_NOK; } if (!dec_buf) { mpp_err_f("dec buf is NULL"); return MPP_NOK; } if (enc_width != dec_width) { mpp_err_f("enc_width %d != dec_width %d", enc_width, dec_width); return MPP_NOK; } if (enc_height != dec_height) { mpp_err_f("enc_height %d != dec_height %d", enc_height, dec_height); return MPP_NOK; } return MPP_OK; } static void mpi_rc_calc_psnr(MpiRcStat *stat, MppFrame frame_in, MppFrame frame_out) { RK_U32 x, y; RK_S32 tmp; double ssd_y = 0.0; double mse_y = 0.0; double psnr_y = 0.0; RK_U32 width = mpp_frame_get_width(frame_in); RK_U32 height = mpp_frame_get_height(frame_in); RK_U32 luma_size = width * height; RK_U32 enc_hor_stride = mpp_frame_get_hor_stride(frame_in); RK_U32 dec_hor_stride = mpp_frame_get_hor_stride(frame_out); RK_U8 *enc_buf = (RK_U8 *)mpp_buffer_get_ptr(mpp_frame_get_buffer(frame_in)); RK_U8 *dec_buf = (RK_U8 *)mpp_buffer_get_ptr(mpp_frame_get_buffer(frame_out)); RK_U8 *enc_buf_y = enc_buf; RK_U8 *dec_buf_y = dec_buf; for (y = 0 ; y < height; y++) { for (x = 0; x < width; x++) { tmp = enc_buf_y[x + y * enc_hor_stride] - dec_buf_y[x + y * dec_hor_stride]; tmp *= tmp; ssd_y += tmp; } } // NOTE: should be 65025.0 rather than 65025, // because 65025*luma_size may exceed // 1 << 32. mse_y = ssd_y / (65025.0 * luma_size); // 65025=255*255 if (mse_y <= 0.0000000001) psnr_y = 100; else psnr_y = -10.0 * log10f(mse_y); stat->psnr_y = psnr_y; } static float ssim_end1( int s1, int s2, int ss, int s12 ) { /* * Maximum value for 10-bit is: ss*64 = (2^10-1)^2*16*4*64 = 4286582784, * which will overflow in some cases. * s1*s1, s2*s2, and s1*s2 also obtain this value for edge cases: * ((2^10-1)*16*4)^2 = 4286582784. * Maximum value for 9-bit is: ss*64 = (2^9-1)^2*16*4*64 = 1069551616, * which will not overflow. */ static const int ssim_c1 = (int)(.01 * .01 * MPI_PIXEL_MAX * MPI_PIXEL_MAX * 64 + .5); static const int ssim_c2 = (int)(.03 * .03 * MPI_PIXEL_MAX * MPI_PIXEL_MAX * 64 * 63 + .5); int fs1 = s1; int fs2 = s2; int fss = ss; int fs12 = s12; int vars = fss * 64 - fs1 * fs1 - fs2 * fs2; int covar = fs12 * 64 - fs1 * fs2; return (float)(2 * fs1 * fs2 + ssim_c1) * (float)(2 * covar + ssim_c2) / ((float)(fs1 * fs1 + fs2 * fs2 + ssim_c1) * (float)(vars + ssim_c2)); } static float ssim_end4( int sum0[5][4], int sum1[5][4], int width ) { double ssim = 0.0; int i = 0; for (i = 0; i < width; i++ ) ssim += ssim_end1(sum0[i][0] + sum0[i + 1][0] + sum1[i][0] + sum1[i + 1][0], sum0[i][1] + sum0[i + 1][1] + sum1[i][1] + sum1[i + 1][1], sum0[i][2] + sum0[i + 1][2] + sum1[i][2] + sum1[i + 1][2], sum0[i][3] + sum0[i + 1][3] + sum1[i][3] + sum1[i + 1][3] ); return ssim; } static void ssim_4x4x2_core( const pixel *pix1, intptr_t stride1, const pixel *pix2, intptr_t stride2, int sums[2][4] ) { int a = 0; int b = 0; int x = 0; int y = 0; int z = 0; for (z = 0; z < 2; z++ ) { uint32_t s1 = 0, s2 = 0, ss = 0, s12 = 0; for (y = 0; y < 4; y++ ) for ( x = 0; x < 4; x++ ) { a = pix1[x + y * stride1]; b = pix2[x + y * stride2]; s1 += a; s2 += b; ss += a * a; ss += b * b; s12 += a * b; } sums[z][0] = s1; sums[z][1] = s2; sums[z][2] = ss; sums[z][3] = s12; pix1 += 4; pix2 += 4; } } static float calc_ssim(pixel *pix1, RK_U32 stride1, pixel *pix2, RK_U32 stride2, int width, int height, void *buf, int *cnt) { int x = 0; int y = 0; int z = 0; float ssim = 0.0; int (*sum0)[4] = buf; int (*sum1)[4] = sum0 + (width >> 2) + 3; width >>= 2; height >>= 2; for ( y = 1; y < height; y++ ) { for ( ; z <= y; z++ ) { MPP_SWAP( void*, sum0, sum1 ); for ( x = 0; x < width; x += 2 ) ssim_4x4x2_core(&pix1[4 * (x + z * stride1)], stride1, &pix2[4 * (x + z * stride2)], stride2, &sum0[x] ); } for ( x = 0; x < width - 1; x += 4 ) ssim += ssim_end4(sum0 + x, sum1 + x, MPP_MIN(4, width - x - 1)); } *cnt = (height - 1) * (width - 1); return ssim; } static void mpi_rc_calc_ssim(MpiRc2TestCtx *ctx, MppFrame frame_in, MppFrame frame_out) { int cnt = 0; float ssim = 0; MpiRcStat *stat = &ctx->stat; RK_U32 width = mpp_frame_get_width(frame_in); RK_U32 height = mpp_frame_get_height(frame_in); RK_U32 enc_hor_stride = mpp_frame_get_hor_stride(frame_in); RK_U32 dec_hor_stride = mpp_frame_get_hor_stride(frame_out); pixel *enc_buf = (RK_U8 *)mpp_buffer_get_ptr(mpp_frame_get_buffer(frame_in)); pixel *dec_buf = (RK_U8 *)mpp_buffer_get_ptr(mpp_frame_get_buffer(frame_out)); pixel *enc_buf_y = enc_buf; pixel *dec_buf_y = dec_buf; ssim = calc_ssim(enc_buf_y, enc_hor_stride, dec_buf_y, dec_hor_stride, width - 2, height - 2, ctx->com_buf, &cnt); ssim /= (float)cnt; stat->ssim_y = (double)ssim; } static MPP_RET mpi_rc_calc_stat(MpiRc2TestCtx *ctx, MppFrame frame_in, MppFrame frame_out) { MPP_RET ret = MPP_OK; MpiRcStat *stat = &ctx->stat; ret = mpi_rc_cmp_frame(frame_in, frame_out); if (MPP_OK != ret) { mpp_err_f("mpi_rc_cmp_frame failed ret %d", ret); return MPP_NOK; } if (ctx->cmd.psnr_en) mpi_rc_calc_psnr(stat, frame_in, frame_out); if (ctx->cmd.ssim_en) mpi_rc_calc_ssim(ctx, frame_in, frame_out); return ret; } static void mpi_rc_log_stat(MpiRc2TestCtx *ctx, RK_U32 frame_count, RK_U32 one_second, RK_U32 seq_end) { //static char rc_log_str[1024] = {0}; MpiRcStat *stat = &ctx->stat; MpiRcFile *file = &ctx->file; FILE *fp = file->fp_stat; mpp_log("frame %3d | frame_size %6d bytes | psnr %5.2f | ssim %5.5f", frame_count, stat->frame_size, stat->psnr_y, stat->ssim_y); if (one_second) mpp_log("ins_bitrate %f kbps", stat->ins_bitrate * 8.0 / 1000); if (fp) { fprintf(fp, "%3d,%6d,%5.2f,%5.5f,", frame_count, stat->frame_size, stat->psnr_y, stat->ssim_y); if (one_second) fprintf(fp, "bitsrate: %d,", stat->ins_bitrate); if (!seq_end) fprintf(fp, "\n"); } } static MPP_RET mpi_rc_enc_init(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; MppCtx *enc_ctx = &ctx->enc_ctx; MppApi *enc_mpi; MpiRcTestCmd *cmd = &ctx->cmd; MppPollType block = MPP_POLL_NON_BLOCK; MppCodingType type = cmd->type; MppEncRcCfg *rc_cfg = &ctx->rc_cfg; MppEncPrepCfg *prep_cfg = &ctx->prep_cfg; MppEncCodecCfg *codec_cfg = &ctx->codec_cfg; MppEncSeiMode sei_mode = MPP_ENC_SEI_MODE_ONE_SEQ; // encoder init ret = mpp_create(enc_ctx, &ctx->enc_mpi); if (MPP_OK != ret) { mpp_err("mpp_create encoder failed\n"); goto MPP_TEST_OUT; } enc_mpi = ctx->enc_mpi; ret = enc_mpi->control(*enc_ctx, MPP_SET_INPUT_TIMEOUT, (MppParam)&block); if (MPP_OK != ret) { mpp_err("enc_mpi->control MPP_SET_INPUT_TIMEOUT failed\n"); goto MPP_TEST_OUT; } ret = enc_mpi->control(*enc_ctx, MPP_SET_OUTPUT_TIMEOUT, (MppParam)&block); if (MPP_OK != ret) { mpp_err("enc_mpi->control MPP_SET_OUTPUT_TIMEOUT failed\n"); goto MPP_TEST_OUT; } ret = mpp_init(*enc_ctx, MPP_CTX_ENC, type); if (MPP_OK != ret) { mpp_err("mpp_init enc failed\n"); goto MPP_TEST_OUT; } rc_cfg->change = MPP_ENC_RC_CFG_CHANGE_ALL; rc_cfg->rc_mode = MPP_ENC_RC_MODE_VBR; rc_cfg->quality = MPP_ENC_RC_QUALITY_MEDIUM; rc_cfg->bps_target = 2000000; rc_cfg->bps_max = rc_cfg->bps_target * 3 / 2; rc_cfg->bps_min = rc_cfg->bps_target / 2; rc_cfg->fps_in_denorm = 1; rc_cfg->fps_out_denorm = 1; rc_cfg->fps_in_num = 30; rc_cfg->fps_out_num = 30; rc_cfg->fps_in_flex = 0; rc_cfg->fps_out_flex = 0; rc_cfg->gop = 60; rc_cfg->max_reenc_times = 1; ret = enc_mpi->control(*enc_ctx, MPP_ENC_SET_RC_CFG, rc_cfg); if (ret) { mpp_err("mpi control enc set rc cfg failed ret %d\n", ret); goto MPP_TEST_OUT; } codec_cfg->coding = type; codec_cfg->h264.change = MPP_ENC_H264_CFG_CHANGE_PROFILE | MPP_ENC_H264_CFG_CHANGE_ENTROPY; /* * H.264 profile_idc parameter * 66 - Baseline profile * 77 - Main profile * 100 - High profile */ codec_cfg->h264.profile = 100; /* * H.264 level_idc parameter * 10 / 11 / 12 / 13 - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps * 20 / 21 / 22 - cif@30fps / half-D1@@25fps / D1@12.5fps * 30 / 31 / 32 - D1@25fps / 720p@30fps / 720p@60fps * 40 / 41 / 42 - 1080p@30fps / 1080p@30fps / 1080p@60fps * 50 / 51 / 52 - 4K@30fps */ codec_cfg->h264.level = 40; codec_cfg->h264.entropy_coding_mode = 1; codec_cfg->h264.cabac_init_idc = 0; if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_FIXQP) { /* constant QP mode qp is fixed */ codec_cfg->h264.qp_max = 26; codec_cfg->h264.qp_min = 26; codec_cfg->h264.qp_max_step = 0; codec_cfg->h264.qp_init = 26; codec_cfg->h264.change |= MPP_ENC_H264_CFG_CHANGE_QP_LIMIT; } ret = enc_mpi->control(*enc_ctx, MPP_ENC_SET_CODEC_CFG, codec_cfg); if (ret) { mpp_err("mpi control enc set codec cfg failed ret %d\n", ret); goto MPP_TEST_OUT; } /* optional */ ret = enc_mpi->control(*enc_ctx, MPP_ENC_SET_SEI_CFG, &sei_mode); if (ret) { mpp_err("mpi control enc set sei cfg failed ret %d\n", ret); goto MPP_TEST_OUT; } prep_cfg->change = MPP_ENC_PREP_CFG_CHANGE_INPUT | MPP_ENC_PREP_CFG_CHANGE_FORMAT; prep_cfg->width = 1920; prep_cfg->height = 1080; prep_cfg->hor_stride = 1920; prep_cfg->ver_stride = 1088; prep_cfg->format = MPP_FMT_YUV420SP; ret = enc_mpi->control(*enc_ctx, MPP_ENC_SET_PREP_CFG, prep_cfg); if (ret) { mpp_err("mpi control enc set prep cfg failed ret %d\n", ret); goto MPP_TEST_OUT; } MPP_TEST_OUT: return ret; } static MPP_RET mpi_rc_enc_encode(MpiRc2TestCtx *ctx, MppFrame frm) { MPP_RET ret = MPP_OK; MppBuffer pkt_buf_out = ctx->enc_pkt_buf; MppApi *enc_mpi = ctx->enc_mpi; mpp_packet_init_with_buffer(&ctx->enc_pkt, pkt_buf_out); ret = enc_mpi->poll(ctx->enc_ctx, MPP_PORT_INPUT, MPP_POLL_BLOCK); if (ret) { mpp_err("mpp input poll failed\n"); goto MPP_TEST_OUT; } ret = enc_mpi->dequeue(ctx->enc_ctx, MPP_PORT_INPUT, &ctx->enc_in_task); if (ret) { mpp_err("mpp task input dequeue failed\n"); goto MPP_TEST_OUT; } mpp_task_meta_set_frame (ctx->enc_in_task, KEY_INPUT_FRAME, frm); mpp_task_meta_set_packet(ctx->enc_in_task, KEY_OUTPUT_PACKET, ctx->enc_pkt); ret = enc_mpi->enqueue(ctx->enc_ctx, MPP_PORT_INPUT, ctx->enc_in_task); if (ret) { mpp_err("mpp task input enqueue failed\n"); goto MPP_TEST_OUT; } MPP_TEST_OUT: return ret; } static MPP_RET mpi_rc_enc_result(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; ret = ctx->enc_mpi->poll(ctx->enc_ctx, MPP_PORT_OUTPUT, MPP_POLL_BLOCK); if (ret) { mpp_err("mpp task output poll failed ret %d\n", ret); goto MPP_TEST_OUT; } ret = ctx->enc_mpi->dequeue(ctx->enc_ctx, MPP_PORT_OUTPUT, &ctx->enc_out_task); if (ret) { mpp_err("mpp task output dequeue failed\n"); goto MPP_TEST_OUT; } if (!ctx->enc_out_task) { mpp_err("no enc_out_task available\n"); ret = MPP_NOK; goto MPP_TEST_OUT; } MppFrame packet_out = NULL; mpp_task_meta_get_packet(ctx->enc_out_task, KEY_OUTPUT_PACKET, &packet_out); mpp_assert(packet_out == ctx->enc_pkt); if (!ctx->enc_pkt) { ret = MPP_NOK; goto MPP_TEST_OUT; } MPP_TEST_OUT: return ret; } static MPP_RET mpi_rc_post_dec_init(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; MpiRcTestCmd *cmd = &ctx->cmd; MppCodingType type = cmd->type; RK_U32 need_split = 0; MppPollType block = MPP_POLL_NON_BLOCK; // decoder init ret = mpp_create(&ctx->dec_ctx_post, &ctx->dec_mpi_post); if (MPP_OK != ret) { mpp_err("mpp_create decoder failed\n"); goto MPP_TEST_OUT; } ret = mpp_packet_init(&ctx->dec_pkt_post, ctx->dec_in_buf_post, 1920 * 1080); if (ret) { mpp_err("mpp_packet_init failed\n"); goto MPP_TEST_OUT; } ret = ctx->dec_mpi_post->control(ctx->dec_ctx_post, MPP_DEC_SET_PARSER_SPLIT_MODE, &need_split); if (MPP_OK != ret) { mpp_err("dec_mpi->control failed\n"); goto MPP_TEST_OUT; } ret = ctx->dec_mpi_post->control(ctx->dec_ctx_post, MPP_SET_INPUT_TIMEOUT, (MppParam)&block); if (MPP_OK != ret) { mpp_err("dec_mpi->control dec MPP_SET_INPUT_TIMEOUT failed\n"); goto MPP_TEST_OUT; } block = MPP_POLL_BLOCK; ret = ctx->dec_mpi_post->control(ctx->dec_ctx_post, MPP_SET_OUTPUT_TIMEOUT, (MppParam)&block); if (MPP_OK != ret) { mpp_err("dec_mpi->control MPP_SET_OUTPUT_TIMEOUT failed\n"); goto MPP_TEST_OUT; } ret = mpp_init(ctx->dec_ctx_post, MPP_CTX_DEC, type); if (MPP_OK != ret) { mpp_err("mpp_init dec failed\n"); goto MPP_TEST_OUT; } MPP_TEST_OUT: return ret; } static MPP_RET mpi_rc_dec_post_decode(MpiRc2TestCtx *ctx, MppFrame orig_frm) { MPP_RET ret = MPP_OK; RK_S32 dec_pkt_done = 0; MppFrame out_frm = NULL; do { if (ctx->pkt_eos) mpp_packet_set_eos(ctx->dec_pkt_post); // send the packet first if packet is not done if (!dec_pkt_done) { ret = ctx->dec_mpi_post->decode_put_packet(ctx->dec_ctx_post, ctx->dec_pkt_post); if (MPP_OK == ret) dec_pkt_done = 1; } // then get all available frame and release do { RK_S32 dec_get_frm = 0; RK_U32 dec_frm_eos = 0; ret = ctx->dec_mpi_post->decode_get_frame(ctx->dec_ctx_post, &out_frm); if (MPP_OK != ret) { mpp_err("decode_get_frame failed ret %d\n", ret); break; } if (out_frm) { if (mpp_frame_get_info_change(out_frm)) { mpp_log("decode_get_frame get info changed found\n"); ctx->dec_mpi_post->control(ctx->dec_ctx_post, MPP_DEC_SET_INFO_CHANGE_READY, NULL); } else { mpi_rc_calc_stat(ctx, orig_frm, out_frm); mpi_rc_log_stat(ctx, ctx->frm_idx, !((ctx->frm_idx - ctx->calc_base_idx + 1) % ctx->rc_cfg.fps_in_num), 0); dec_get_frm = 1; } dec_frm_eos = mpp_frame_get_eos(out_frm); mpp_frame_deinit(&out_frm); out_frm = NULL; } // if last packet is send but last frame is not found continue if (ctx->pkt_eos && dec_pkt_done && !dec_frm_eos) continue; if (dec_get_frm) break; } while (1); if (dec_pkt_done) break; msleep(5); } while (1); return ret; } static MPP_RET mpi_rc_pre_dec_init(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; MpiRcTestCmd *cmd = &ctx->cmd; RK_U32 need_split = 1; MppPollType block = MPP_POLL_NON_BLOCK; MppApi *mpi = NULL; // decoder init ret = mpp_create(&ctx->dec_ctx_pre, &ctx->dec_mpi_pre); if (MPP_OK != ret) { mpp_err("mpp_create decoder failed\n"); goto MPP_TEST_OUT; } mpi = ctx->dec_mpi_pre; ret = mpp_packet_init(&ctx->dec_pkt_pre, ctx->dec_in_buf_pre, ctx->dec_in_buf_pre_size); if (ret) { mpp_err("mpp_packet_init failed\n"); goto MPP_TEST_OUT; } ret = mpi->control(ctx->dec_ctx_pre, MPP_DEC_SET_PARSER_SPLIT_MODE, &need_split); if (MPP_OK != ret) { mpp_err("dec_mpi->control failed\n"); goto MPP_TEST_OUT; } ret = mpi->control(ctx->dec_ctx_pre, MPP_SET_INPUT_TIMEOUT, (MppParam)&block); if (MPP_OK != ret) { mpp_err("dec_mpi->control dec MPP_SET_INPUT_TIMEOUT failed\n"); goto MPP_TEST_OUT; } block = MPP_POLL_BLOCK; ret = mpi->control(ctx->dec_ctx_pre, MPP_SET_OUTPUT_TIMEOUT, (MppParam)&block); if (MPP_OK != ret) { mpp_err("dec_mpi->control MPP_SET_OUTPUT_TIMEOUT failed\n"); goto MPP_TEST_OUT; } ret = mpp_init(ctx->dec_ctx_pre, MPP_CTX_DEC, cmd->type); if (MPP_OK != ret) { mpp_err("mpp_init dec failed\n"); goto MPP_TEST_OUT; } MPP_TEST_OUT: return ret; } static MPP_RET mpi_rc_info_change(MpiRc2TestCtx *ctx, MppFrame frm) { MPP_RET ret = MPP_OK; RK_U32 width; RK_U32 height; MppFrame frame_tmp = NULL; ctx->prep_cfg.width = width = mpp_frame_get_width(frm); ctx->prep_cfg.height = height = mpp_frame_get_height(frm); ctx->prep_cfg.format = mpp_frame_get_fmt(frm); ret = ctx->enc_mpi->control(ctx->enc_ctx, MPP_ENC_SET_PREP_CFG, &ctx->prep_cfg); mpp_frame_init(&frame_tmp); mpp_frame_set_width(frame_tmp, width); mpp_frame_set_height(frame_tmp, height); // NOTE: only for current version, otherwise there // will be info change mpp_frame_set_hor_stride(frame_tmp, MPP_ALIGN(width, 16)); mpp_frame_set_ver_stride(frame_tmp, MPP_ALIGN(height, 16)); mpp_frame_set_fmt(frame_tmp, MPP_FMT_YUV420SP); ctx->dec_mpi_post->control(ctx->dec_ctx_post, MPP_DEC_SET_FRAME_INFO, (MppParam)frame_tmp); ctx->enc_mpi->control(ctx->enc_ctx, MPP_ENC_GET_EXTRA_INFO, &ctx->enc_pkt); /* get and write sps/pps for H.264 */ if (ctx->enc_pkt) { void *ptr = mpp_packet_get_pos(ctx->enc_pkt); size_t len = mpp_packet_get_length(ctx->enc_pkt); MppPacket tmp_pkt = NULL; // write extra data to dec packet and send mpp_packet_init(&tmp_pkt, ptr, len); mpp_packet_set_extra_data(tmp_pkt); ctx->dec_mpi_post->decode_put_packet(ctx->dec_ctx_post, tmp_pkt); mpp_packet_deinit(&tmp_pkt); if (ctx->file.fp_enc_out) fwrite(ptr, 1, len, ctx->file.fp_enc_out); ctx->enc_pkt = NULL; } if (frame_tmp) mpp_frame_deinit(&frame_tmp); return ret; } static MPP_RET mpi_rc_dec_pre_decode(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; RK_S32 dec_pkt_done = 0; MppApi *mpi = ctx->dec_mpi_pre; MppCtx dec_ctx = ctx->dec_ctx_pre; MppFrame frm = NULL; do { // send the packet first if packet is not done if (!dec_pkt_done) { ret = mpi->decode_put_packet(dec_ctx, ctx->dec_pkt_pre); if (MPP_OK == ret) dec_pkt_done = 1; } // then get all available frame and release do { ret = mpi->decode_get_frame(dec_ctx, &frm); if (MPP_OK != ret) { mpp_err("decode_get_frame failed ret %d\n", ret); break; } if (frm) { ctx->frm_eos = mpp_frame_get_eos(frm); ctx->pkt_eos = mpp_packet_get_eos(ctx->dec_pkt_pre); if (ctx->frm_eos && mpp_frame_get_buffer(frm) == NULL) break; if (mpp_frame_get_info_change(frm)) { mpp_log("decode_get_frame get info changed found\n"); mpi->control(dec_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL); mpi_rc_info_change(ctx, frm); } else { void *ptr; size_t len; mpi_rc_enc_encode(ctx, frm); mpi_rc_enc_result(ctx); len = mpp_packet_get_length(ctx->enc_pkt); ctx->stat.frame_size = len; ctx->stream_size_1s += len; if ((ctx->frm_idx - ctx->calc_base_idx + 1) % ctx->rc_cfg.fps_in_num == 0) { ctx->stat.ins_bitrate = ctx->stream_size_1s; ctx->stream_size_1s = 0; } ptr = mpp_packet_get_pos(ctx->enc_pkt); if (ctx->file.fp_enc_out) fwrite(ptr, 1, len, ctx->file.fp_enc_out); /* decode one frame */ // write packet to dec input mpp_packet_write(ctx->dec_pkt_post, 0, ptr, len); // reset pos mpp_packet_set_pos(ctx->dec_pkt_post, ctx->dec_in_buf_post); mpp_packet_set_length(ctx->dec_pkt_post, len); mpp_packet_set_size(ctx->dec_pkt_post, len); mpi_rc_dec_post_decode(ctx, frm); mpp_packet_deinit(&ctx->enc_pkt); ctx->frm_idx++; if (ctx->cmd.have_config_file) ctx->cmd.ectx->notify(ctx->cmd.ectx); ret = ctx->enc_mpi->enqueue(ctx->enc_ctx, MPP_PORT_OUTPUT, ctx->enc_out_task); if (ret) { mpp_err("mpp task output enqueue failed\n"); continue; } } mpp_frame_deinit(&frm); frm = NULL; } else { break; } if (ctx->frm_idx >= ctx->cmd.num_frames) { mpp_log("test frame number fulfill\n"); ctx->frm_eos = 1; ctx->pkt_eos = 1; dec_pkt_done = 1; } // if last packet is send but last frame is not found continue if (ctx->pkt_eos && dec_pkt_done && !ctx->frm_eos) continue; if (ctx->frm_eos) break; } while (1); if (dec_pkt_done) break; msleep(5); } while (1); return ret; } static MPP_RET mpi_rc_buffer_init(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; /* NOTE: packet buffer may overflow */ size_t packet_size = 1920 * 1080; ret = mpp_buffer_group_get_internal(&ctx->pkt_grp, MPP_BUFFER_TYPE_ION); if (ret) { mpp_err("failed to get buffer group for output packet ret %d\n", ret); goto RET; } ret = mpp_buffer_get(ctx->pkt_grp, &ctx->enc_pkt_buf, packet_size); if (ret) { mpp_err("failed to get buffer for input frame ret %d\n", ret); goto RET; } ctx->dec_in_buf_post = mpp_calloc(RK_U8, packet_size); if (NULL == ctx->dec_in_buf_post) { mpp_err("mpi_dec_test malloc input stream buffer failed\n"); goto RET; } ctx->dec_in_buf_pre_size = MPI_DEC_STREAM_SIZE; ctx->dec_in_buf_pre = mpp_calloc(RK_U8, ctx->dec_in_buf_pre_size); if (NULL == ctx->dec_in_buf_pre) { mpp_err("mpi_dec_test malloc input stream buffer failed\n"); goto RET; } ctx->frm_idx = 0; ctx->calc_base_idx = 0; ctx->stream_size_1s = 0; return ret; RET: mpp_free(ctx->dec_in_buf_pre); ctx->dec_ctx_pre = NULL; mpp_free(ctx->dec_in_buf_post); ctx->dec_ctx_post = NULL; if (ctx->enc_pkt_buf) { mpp_buffer_put(ctx->enc_pkt_buf); ctx->enc_pkt_buf = NULL; } if (ctx->pkt_grp) { mpp_buffer_group_put(ctx->pkt_grp); ctx->pkt_grp = NULL; } return ret; } #define CHECK_RET(x) do { \ if (x < 0) { \ mpp_err_f("%d failed\n"); \ goto MPP_TEST_OUT; \ } \ } while (0) static MPP_RET mpi_rc_codec(MpiRc2TestCtx *ctx) { MPP_RET ret = MPP_OK; CHECK_RET(mpi_rc_buffer_init(ctx)); CHECK_RET(mpi_rc_post_dec_init(ctx)); CHECK_RET(mpi_rc_pre_dec_init(ctx)); CHECK_RET(mpi_rc_enc_init(ctx)); ctx->cmd.ectx = event_ctx_create(&ctx->cmd.e, event_occur, ctx); while (1) { size_t read_size = fread(ctx->dec_in_buf_pre, 1, ctx->dec_in_buf_pre_size, ctx->file.fp_input); if (read_size != ctx->dec_in_buf_pre_size || feof(ctx->file.fp_input)) { // setup eos flag mpp_packet_set_eos(ctx->dec_pkt_pre); } mpp_packet_set_pos(ctx->dec_pkt_pre, ctx->dec_in_buf_pre); mpp_packet_set_length(ctx->dec_pkt_pre, read_size); mpi_rc_dec_pre_decode(ctx); if (ctx->pkt_eos && ctx->frm_eos) { mpp_log("stream finish\n"); break; } } CHECK_RET(ctx->enc_mpi->reset(ctx->enc_ctx)); CHECK_RET(ctx->dec_mpi_pre->reset(ctx->dec_ctx_pre)); CHECK_RET(ctx->dec_mpi_post->reset(ctx->dec_ctx_post)); MPP_TEST_OUT: // encoder deinit if (ctx->enc_ctx) { mpp_destroy(ctx->enc_ctx); ctx->enc_ctx = NULL; } if (ctx->enc_pkt_buf) { mpp_buffer_put(ctx->enc_pkt_buf); ctx->enc_pkt_buf = NULL; } if (ctx->pkt_grp) { mpp_buffer_group_put(ctx->pkt_grp); ctx->pkt_grp = NULL; } // decoder deinit if (ctx->dec_pkt_post) { mpp_packet_deinit(&ctx->dec_pkt_post); ctx->dec_pkt_post = NULL; } if (ctx->dec_ctx_post) { mpp_destroy(ctx->dec_ctx_post); ctx->dec_ctx_post = NULL; } if (ctx->dec_ctx_pre) { mpp_destroy(ctx->dec_ctx_pre); ctx->dec_ctx_pre = NULL; } MPP_FREE(ctx->dec_in_buf_post); MPP_FREE(ctx->dec_in_buf_pre); return ret; } static void mpi_rc_test_help() { mpp_log("usage: mpi_rc_test [options]\n"); show_options(mpi_rc_cmd); mpp_show_support_format(); } static RK_S32 mpi_enc_test_parse_options(int argc, char **argv, MpiRcTestCmd* cmd) { const char *opt; const char *next; RK_S32 optindex = 1; RK_S32 handleoptions = 1; RK_S32 err = MPP_NOK; if ((argc < 2) || (cmd == NULL)) { err = 1; return err; } /* 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) { strncpy(cmd->file_input, next, MPI_RC_FILE_NAME_LEN); cmd->file_input[strlen(next)] = '\0'; cmd->have_input = 1; } else { mpp_err("input file is invalid\n"); goto PARSE_OPINIONS_OUT; } break; case 'o': if (next) { strncpy(cmd->file_enc_out, next, MPI_RC_FILE_NAME_LEN); cmd->file_enc_out[strlen(next)] = '\0'; cmd->have_enc_out = 1; } else { mpp_log("output file is invalid\n"); goto PARSE_OPINIONS_OUT; } break; case 'd': if (next) { cmd->debug = atoi(next);; } else { mpp_err("invalid debug flag\n"); goto PARSE_OPINIONS_OUT; } break; case 't': if (next) { cmd->type = (MppCodingType)atoi(next); err = mpp_check_support_format(MPP_CTX_ENC, cmd->type); } if (!next || err) { 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 's': if (next) { strncpy(cmd->file_stat, next, MPI_RC_FILE_NAME_LEN); cmd->file_stat[strlen(next)] = '\0'; cmd->have_stat_out = 1; } else { mpp_log("stat file is invalid\n"); goto PARSE_OPINIONS_OUT; } break; case 'g': if (next) { strncpy(cmd->file_config, next, MPI_RC_FILE_NAME_LEN); cmd->file_config[strlen(next)] = '\0'; cmd->have_config_file = 1; } break; case 'p': if (next) { cmd->psnr_en = atoi(next); } else { mpp_err("invalid psnr enable/disable value\n"); goto PARSE_OPINIONS_OUT; } break; case 'm': if (next) { cmd->ssim_en = atoi(next); } else { mpp_err("invalid ssim enable/disable value\n"); goto PARSE_OPINIONS_OUT; } default: goto PARSE_OPINIONS_OUT; break; } optindex++; } } err = 0; PARSE_OPINIONS_OUT: return err; } static void mpi_rc_test_show_options(MpiRcTestCmd* cmd) { mpp_log("cmd parse result:\n"); mpp_log("input file name: %s\n", cmd->file_input); mpp_log("enc out file name: %s\n", cmd->file_enc_out); mpp_log("stat file name: %s\n", cmd->file_stat); mpp_log("type : %d\n", cmd->type); mpp_log("debug flag : %x\n", cmd->debug); mpp_log("num frames : %d\n", cmd->num_frames); mpp_log("config file : %s\n", cmd->file_config); mpp_log("psnr enable : %d\n", cmd->psnr_en); mpp_log("ssim enable : %d\n", cmd->ssim_en); } int main(int argc, char **argv) { MPP_RET ret = MPP_OK; MpiRc2TestCtx ctx; MpiRcTestCmd* cmd = &ctx.cmd; mpp_log("=========== mpi rc test start ===========\n"); memset(&ctx, 0, sizeof(ctx)); // parse the cmd option ret = mpi_enc_test_parse_options(argc, argv, cmd); if (ret) { if (ret < 0) { mpp_err("mpi_rc_test_parse_options: input parameter invalid\n"); } mpi_rc_test_help(); return ret; } mpi_rc_test_show_options(cmd); mpp_env_set_u32("mpi_rc_debug", cmd->debug); ret = mpi_rc_init(&ctx); if (ret != MPP_OK) { mpp_err("mpi_rc_init failded ret %d", ret); goto err; } ret = mpi_rc_codec(&ctx); if (ret != MPP_OK) { mpp_err("mpi_rc_codec failded ret %d", ret); goto err; } err: mpi_rc_deinit(&ctx); mpp_log("=========== mpi rc test end ===========\n"); return (int)ret; }