diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index aa796231..976bbe20 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -43,3 +43,6 @@ add_mpp_test(mpi_dec) # mpi encoder unit test add_mpp_test(mpi_enc) + +# mpi rc unit test +add_mpp_test(mpi_rc) diff --git a/test/mpi_rc_test.c b/test/mpi_rc_test.c new file mode 100644 index 00000000..42f49f98 --- /dev/null +++ b/test/mpi_rc_test.c @@ -0,0 +1,1289 @@ +/* + * 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" + +#define MPI_RC_IO_COUNT 1 +#define MPI_RC_FILE_NAME_LEN 256 + +#define MPI_BIT_DEPTH 8 +#define MPI_PIXEL_MAX ((1 << MPI_BIT_DEPTH) - 1) + +#define MPI_RC_ITEM_BPS 0x00000001 +#define MPI_RC_ITEM_FPS 0x00000002 +#define MPI_RC_ITEM_GOP 0x00000004 +#define MPI_RC_ITEM_FORCE_I 0x00000008 +#define MPI_RC_ITEM_ROI 0x00000010 + +typedef RK_U8 pixel; + +typedef struct { + RK_U32 bitrate; +} MpiRcCfg; + +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_dec_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_dec_out[MPI_RC_FILE_NAME_LEN]; + char file_stat[MPI_RC_FILE_NAME_LEN]; + MppCodingType type; + RK_U32 width; + RK_U32 height; + MppFrameFormat format; + RK_U32 debug; + RK_U32 num_frames; + RK_U32 item_flag; + + RK_U32 have_input; + RK_U32 have_enc_out; + RK_U32 have_dec_out; + RK_U32 have_stat_out; +} MpiRcTestCmd; + +typedef struct { + MpiRcTestCmd cmd; + MpiRcCfg cfg; + MpiRcStat stat; + MpiRcFile file; + RK_U8 *com_buf; +} MpiRcTestCtx; + +static OptionInfo mpi_rc_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"}, + {"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"}, +}; + +static MPP_RET mpi_rc_read_yuv_image(RK_U8 *buf, MppEncConfig *mpp_cfg, FILE *fp) +{ + MPP_RET ret = MPP_OK; + RK_U32 read_size; + RK_U32 row = 0; + RK_U32 width = mpp_cfg->width; + RK_U32 height = mpp_cfg->height; + RK_U32 hor_stride = mpp_cfg->hor_stride; + RK_U32 ver_stride = mpp_cfg->ver_stride; + MppFrameFormat fmt = mpp_cfg->format; + RK_U8 *buf_y = buf; + RK_U8 *buf_u = buf_y + hor_stride * ver_stride; // NOTE: diff from gen_yuv_image + RK_U8 *buf_v = buf_u + hor_stride * ver_stride / 4; // NOTE: diff from gen_yuv_image + + switch (fmt) { + case MPP_FMT_YUV420SP : { + for (row = 0; row < height; row++) { + read_size = fread(buf_y + row * hor_stride, 1, width, fp); + if (read_size != width) { + mpp_err_f("read ori yuv file luma failed"); + ret = MPP_NOK; + goto err; + } + } + + for (row = 0; row < height / 2; row++) { + read_size = fread(buf_u + row * hor_stride, 1, width, fp); + if (read_size != width) { + mpp_err_f("read ori yuv file cb failed"); + ret = MPP_NOK; + goto err; + } + } + } break; + case MPP_FMT_YUV420P : { + for (row = 0; row < height; row++) { + read_size = fread(buf_y + row * hor_stride, 1, width, fp); + if (read_size != width) { + mpp_err_f("read ori yuv file luma failed"); + ret = MPP_NOK; + goto err; + } + } + + for (row = 0; row < height / 2; row++) { + read_size = fread(buf_u + row * hor_stride / 2, 1, width / 2, fp); + if (read_size != width / 2) { + mpp_err_f("read ori yuv file cb failed"); + ret = MPP_NOK; + goto err; + } + } + + for (row = 0; row < height / 2; row++) { + read_size = fread(buf_v + row * hor_stride / 2, 1, width / 2, fp); + if (read_size != width / 2) { + mpp_err_f("read ori yuv file cr failed"); + ret = MPP_NOK; + goto err; + } + } + } break; + default : { + mpp_err_f("read image do not support fmt %d\n", fmt); + ret = MPP_NOK; + } break; + } + +err: + + return ret; +} + +static MPP_RET gen_yuv_image(RK_U8 *buf, MppEncConfig *mpp_cfg, RK_U32 frame_count) +{ + MPP_RET ret = MPP_OK; + RK_U32 width = mpp_cfg->width; + RK_U32 height = mpp_cfg->height; + RK_U32 hor_stride = mpp_cfg->hor_stride; + RK_U32 ver_stride = mpp_cfg->ver_stride; + MppFrameFormat fmt = mpp_cfg->format; + RK_U8 *buf_y = buf; + RK_U8 *buf_c = buf + hor_stride * ver_stride; + RK_U32 x, y; + + switch (fmt) { + case MPP_FMT_YUV420SP : { + RK_U8 *p = buf_y; + + for (y = 0; y < height; y++, p += hor_stride) { + for (x = 0; x < width; x++) { + p[x] = x + y + frame_count * 3; + } + } + + p = buf_c; + for (y = 0; y < height / 2; y++, p += hor_stride) { + for (x = 0; x < width / 2; x++) { + p[x * 2 + 0] = 128 + y + frame_count * 2; + p[x * 2 + 1] = 64 + x + frame_count * 5; + } + } + } break; + case MPP_FMT_YUV420P : { + RK_U8 *p = buf_y; + + for (y = 0; y < height; y++, p += hor_stride) { + for (x = 0; x < width; x++) { + p[x] = x + y + frame_count * 3; + } + } + + p = buf_c; + for (y = 0; y < height / 2; y++, p += hor_stride / 2) { + for (x = 0; x < width / 2; x++) { + p[x] = 128 + y + frame_count * 2; + } + } + + p = buf_c + hor_stride * ver_stride / 4; + for (y = 0; y < height / 2; y++, p += hor_stride / 2) { + for (x = 0; x < width / 2; x++) { + p[x] = 64 + x + frame_count * 5; + } + } + } break; + default : { + mpp_err_f("filling function do not support type %d\n", fmt); + ret = MPP_NOK; + } break; + } + return ret; +} + +static void mpi_rc_deinit(MpiRcTestCtx *ctx) +{ + MpiRcFile *file = &ctx->file; + + if (file->fp_enc_out) { + fclose(file->fp_enc_out); + file->fp_enc_out = NULL; + } + if (file->fp_dec_out) { + fclose(file->fp_dec_out); + file->fp_dec_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); +} + +static MPP_RET mpi_rc_init(MpiRcTestCtx *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_dec_out) { + file->fp_dec_out = fopen(cmd->file_dec_out, "w+b"); + if (NULL == file->fp_dec_out) { + mpp_err("failed to open dec output file %s\n", cmd->file_dec_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, cmd->width * cmd->height * 2); + if (ctx->com_buf == NULL) { + mpp_err_f("ctx->com_buf malloc failed"); + ret = MPP_NOK; + goto err; + } + +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(MpiRcTestCtx *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(MpiRcTestCtx *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; + } + + mpi_rc_calc_psnr(stat, frame_in, frame_out); + mpi_rc_calc_ssim(ctx, frame_in, frame_out); + + return ret; +} + +static void mpi_rc_log_stat(MpiRcTestCtx *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 %d byte/s", stat->ins_bitrate); + + 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, "%d,", stat->ins_bitrate); + if (!seq_end) + fprintf(fp, "\n"); + } +} + +static void mpi_rc_change_cfg(MppEncConfig *mpp_cfg, MpiRcTestCmd *cmd, RK_U32 frame_count) +{ + RK_U32 item = cmd->item_flag; + if (item & MPI_RC_ITEM_BPS) { + mpp_log_f("change bps"); + } + + if (item & MPI_RC_ITEM_FPS) { + mpp_log_f("change fps"); + } + + if (item & MPI_RC_ITEM_GOP) { + mpp_log_f("change gop"); + } + + if (item & MPI_RC_ITEM_FORCE_I) { + mpp_log_f("change fps"); + } + + if (item & MPI_RC_ITEM_ROI) { + mpp_log_f("change roi"); + } + (void)mpp_cfg; + (void)frame_count; +} + +static MPP_RET mpi_rc_codec(MpiRcTestCtx *ctx) +{ + MPP_RET ret = MPP_OK; + MpiRcTestCmd *cmd = &ctx->cmd; + RK_U32 frm_eos = 0; + RK_U32 pkt_eos = 0; + MpiRcFile *file = &ctx->file; + MpiRcStat *stat = &ctx->stat; + FILE *fp_input = file->fp_input; + FILE *fp_enc_out = file->fp_enc_out; + FILE *fp_dec_out = file->fp_dec_out; + FILE *fp_stat = file->fp_stat; + RK_U8 *dec_in_buf = NULL; + RK_U32 need_split = 0; + RK_U32 block_input = 0; + RK_U32 block_output = 0; + + // base flow context + MppCtx enc_ctx = NULL; + MppApi *enc_mpi = NULL; + MppCtx dec_ctx = NULL; + MppApi *dec_mpi = NULL; + MppEncConfig mpp_cfg; + + // input / output + RK_S32 i; + MppBufferGroup frm_grp = NULL; + MppBufferGroup pkt_grp = NULL; + MppFrame frame_in = NULL; + MppFrame frame_out = NULL; + MppFrame frame_tmp = NULL; + MppPacket packet = NULL; + MppPacket dec_packet = NULL; + MppBuffer frm_buf[MPI_RC_IO_COUNT] = { NULL }; + MppBuffer pkt_buf[MPI_RC_IO_COUNT] = { NULL }; + MppEncSeiMode sei_mode = MPP_ENC_SEI_MODE_ONE_SEQ; + + // paramter for resource malloc + RK_U32 width = cmd->width; + RK_U32 height = cmd->height; + RK_U32 hor_stride = MPP_ALIGN(width, 16); + RK_U32 ver_stride = MPP_ALIGN(height, 16); + MppFrameFormat fmt = cmd->format; + MppCodingType type = cmd->type; + RK_U32 num_frames = cmd->num_frames; + + // resources + size_t frame_size = hor_stride * ver_stride * 3 / 2; + /* NOTE: packet buffer may overflow */ + size_t packet_size = width * height; + RK_U32 frame_count = 0; + RK_U64 stream_size = 0; + RK_U64 stream_size_1s = 0; + + mpp_log_f("test start width %d height %d codingtype %d\n", width, height, type); + + ret = mpp_buffer_group_get_internal(&frm_grp, MPP_BUFFER_TYPE_ION); + if (ret) { + mpp_err("failed to get buffer group for input frame ret %d\n", ret); + goto MPP_TEST_OUT; + } + + ret = mpp_buffer_group_get_internal(&pkt_grp, MPP_BUFFER_TYPE_ION); + if (ret) { + mpp_err("failed to get buffer group for output packet ret %d\n", ret); + goto MPP_TEST_OUT; + } + + for (i = 0; i < MPI_RC_IO_COUNT; i++) { + ret = mpp_buffer_get(frm_grp, &frm_buf[i], frame_size); + if (ret) { + mpp_err("failed to get buffer for input frame ret %d\n", ret); + goto MPP_TEST_OUT; + } + + ret = mpp_buffer_get(pkt_grp, &pkt_buf[i], packet_size); + if (ret) { + mpp_err("failed to get buffer for input frame ret %d\n", ret); + goto MPP_TEST_OUT; + } + } + + dec_in_buf = mpp_calloc(RK_U8, packet_size); + if (NULL == dec_in_buf) { + mpp_err("mpi_dec_test malloc input stream buffer failed\n"); + goto MPP_TEST_OUT; + } + + // decoder init + ret = mpp_create(&dec_ctx, &dec_mpi); + if (MPP_OK != ret) { + mpp_err("mpp_create decoder failed\n"); + goto MPP_TEST_OUT; + } + + ret = mpp_packet_init(&dec_packet, dec_in_buf, packet_size); + if (ret) { + mpp_err("mpp_packet_init failed\n"); + goto MPP_TEST_OUT; + } + + ret = dec_mpi->control(dec_ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, &need_split); + if (MPP_OK != ret) { + mpp_err("dec_mpi->control failed\n"); + goto MPP_TEST_OUT; + } + block_input = 0; + block_output = 1; + ret = dec_mpi->control(dec_ctx, MPP_SET_INPUT_BLOCK, (MppParam)&block_input); + if (MPP_OK != ret) { + mpp_err("dec_mpi->control dec MPP_SET_INPUT_BLOCK failed\n"); + goto MPP_TEST_OUT; + } + ret = dec_mpi->control(dec_ctx, MPP_SET_OUTPUT_BLOCK, (MppParam)&block_output); + if (MPP_OK != ret) { + mpp_err("dec_mpi->control MPP_SET_OUTPUT_BLOCK failed\n"); + goto MPP_TEST_OUT; + } + + ret = mpp_init(dec_ctx, MPP_CTX_DEC, type); + if (MPP_OK != ret) { + mpp_err("mpp_init dec failed\n"); + goto MPP_TEST_OUT; + } + + 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(hor_stride, 256) | 256)); + mpp_frame_set_ver_stride(frame_tmp, ver_stride); + mpp_frame_set_fmt(frame_tmp, MPP_FMT_YUV420SP); // dec out frame only support 420sp + ret = dec_mpi->control(dec_ctx, MPP_DEC_SET_FRAME_INFO, (MppParam)frame_tmp); + if (MPP_OK != ret) { + mpp_err("dec_mpi->control set frame info failed"); + goto MPP_TEST_OUT; + } + + // encoder init + ret = mpp_create(&enc_ctx, &enc_mpi); + if (MPP_OK != ret) { + mpp_err("mpp_create encoder failed\n"); + goto MPP_TEST_OUT; + } + block_input = 0; + block_output = 0; + ret = enc_mpi->control(enc_ctx, MPP_SET_INPUT_BLOCK, (MppParam)&block_input); + if (MPP_OK != ret) { + mpp_err("enc_mpi->control MPP_SET_INPUT_BLOCK failed\n"); + goto MPP_TEST_OUT; + } + ret = enc_mpi->control(enc_ctx, MPP_SET_OUTPUT_BLOCK, (MppParam)&block_output); + if (MPP_OK != ret) { + mpp_err("enc_mpi->control MPP_SET_OUTPUT_BLOCK 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; + } + + memset(&mpp_cfg, 0, sizeof(mpp_cfg)); + mpp_cfg.size = sizeof(mpp_cfg); + mpp_cfg.width = width; + mpp_cfg.height = height; + mpp_cfg.hor_stride = hor_stride; + mpp_cfg.ver_stride = ver_stride; + mpp_cfg.format = fmt; + mpp_cfg.rc_mode = 0; + mpp_cfg.skip_cnt = 0; + mpp_cfg.fps_in = 30; + mpp_cfg.fps_out = 30; + mpp_cfg.bps = width * height * 2 * mpp_cfg.fps_in; + mpp_cfg.qp = (type == MPP_VIDEO_CodingMJPEG) ? (10) : (24); + mpp_cfg.gop = 60; + + mpp_cfg.profile = 100; + mpp_cfg.level = 41; + mpp_cfg.cabac_en = 1; + + ret = enc_mpi->control(enc_ctx, MPP_ENC_SET_SEI_CFG, &sei_mode); + if (MPP_OK != ret) { + mpp_err("mpi control enc set sei cfg failed\n"); + goto MPP_TEST_OUT; + } + + ret = enc_mpi->control(enc_ctx, MPP_ENC_SET_CFG, &mpp_cfg); + if (MPP_OK != ret) { + mpp_err("mpi control enc set cfg failed\n"); + goto MPP_TEST_OUT; + } + + ret = enc_mpi->control(enc_ctx, MPP_ENC_GET_EXTRA_INFO, &packet); + if (MPP_OK != ret) { + mpp_err("mpi control enc get extra info failed\n"); + goto MPP_TEST_OUT; + } + + /* get and write sps/pps for H.264 */ + if (packet) { + void *ptr = mpp_packet_get_pos(packet); + size_t len = mpp_packet_get_length(packet); + 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); + dec_mpi->decode_put_packet(dec_ctx, tmp_pkt); + mpp_packet_deinit(&tmp_pkt); + + if (fp_enc_out) + fwrite(ptr, 1, len, fp_enc_out); + + packet = NULL; + } + + ret = mpp_frame_init(&frame_in); + if (MPP_OK != ret) { + mpp_err("mpp_frame_init failed\n"); + goto MPP_TEST_OUT; + } + + mpp_frame_set_width(frame_in, width); + mpp_frame_set_height(frame_in, height); + mpp_frame_set_hor_stride(frame_in, hor_stride); + mpp_frame_set_ver_stride(frame_in, ver_stride); + mpp_frame_set_fmt(frame_in, fmt); + + i = 0; + while (!pkt_eos) { + MppTask enc_task = NULL; + RK_S32 index = i++; + MppBuffer frm_buf_in = frm_buf[index]; + MppBuffer pkt_buf_out = pkt_buf[index]; + void *buf = mpp_buffer_get_ptr(frm_buf_in); + + if (i == MPI_RC_IO_COUNT) + i = 0; + + if (fp_input) { + ret = mpi_rc_read_yuv_image(buf, &mpp_cfg, fp_input); + if (MPP_OK != ret || feof(fp_input)) { + mpp_log("found last frame\n"); + frm_eos = 1; + } + } else { + ret = gen_yuv_image(buf, &mpp_cfg, frame_count); + if (ret) + goto MPP_TEST_OUT; + } + + mpp_frame_set_buffer(frame_in, frm_buf_in); + mpp_frame_set_eos(frame_in, frm_eos); + + mpp_packet_init_with_buffer(&packet, pkt_buf_out); + + do { + ret = enc_mpi->dequeue(enc_ctx, MPP_PORT_INPUT, &enc_task); + if (ret) { + mpp_err("mpp task input dequeue failed\n"); + goto MPP_TEST_OUT; + } + if (enc_task == NULL) { + mpp_log("mpi dequeue from MPP_PORT_INPUT fail, task equal with NULL!"); + msleep(3); + } else { + MppFrame tmp_frm = NULL; + + mpp_task_meta_get_frame(enc_task, KEY_INPUT_FRAME, &tmp_frm); + if (tmp_frm) + mpp_assert(tmp_frm == frame_in); + + break; + } + } while (1); + + mpp_task_meta_set_frame (enc_task, KEY_INPUT_FRAME, frame_in); + mpp_task_meta_set_packet(enc_task, KEY_OUTPUT_PACKET, packet); + + if (cmd->item_flag) { + mpi_rc_change_cfg(&mpp_cfg, cmd, frame_count); + ret = enc_mpi->control(enc_ctx, MPP_ENC_SET_CFG, &mpp_cfg); + if (MPP_OK != ret) { + mpp_err("mpi control enc set cfg failed\n"); + goto MPP_TEST_OUT; + } + } + + ret = enc_mpi->enqueue(enc_ctx, MPP_PORT_INPUT, enc_task); + if (ret) { + mpp_err("mpp task input enqueue failed\n"); + goto MPP_TEST_OUT; + } + + msleep(20); + + do { + ret = enc_mpi->dequeue(enc_ctx, MPP_PORT_OUTPUT, &enc_task); + if (ret) { + mpp_err("mpp task output dequeue failed\n"); + goto MPP_TEST_OUT; + } + + if (enc_task) { + MppFrame packet_out = NULL; + + mpp_task_meta_get_packet(enc_task, KEY_OUTPUT_PACKET, &packet_out); + + mpp_assert(packet_out == packet); + if (packet) { + // write packet to file here + void *ptr = mpp_packet_get_pos(packet); + size_t len = mpp_packet_get_length(packet); + RK_U32 dec_pkt_done = 0; + + pkt_eos = mpp_packet_get_eos(packet); + + if (fp_enc_out) + fwrite(ptr, 1, len, fp_enc_out); + + //mpp_log_f("encoded frame %d size %d\n", frame_count, len); + stream_size += len; + stream_size_1s += len; + stat->frame_size = len; + if ((frame_count + 1) % mpp_cfg.fps_in == 0) { + stat->ins_bitrate = stream_size_1s; + stream_size_1s = 0; + } + + if (pkt_eos) { + mpp_log("found last packet\n"); + mpp_assert(frm_eos); + } + + /* decode one frame */ + // write packet to dec input + memset(dec_in_buf, 0, packet_size); + mpp_packet_write(dec_packet, 0, ptr, len); + // reset pos + mpp_packet_set_pos(dec_packet, dec_in_buf); + mpp_packet_set_length(dec_packet, len); + mpp_packet_set_size(dec_packet, len); + frame_out = NULL; + + do { + // send the packet first if packet is not done + if (!dec_pkt_done) { + ret = dec_mpi->decode_put_packet(dec_ctx, dec_packet); + 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 = dec_mpi->decode_get_frame(dec_ctx, &frame_out); + if (MPP_OK != ret) { + mpp_err("decode_get_frame failed ret %d\n", ret); + break; + } + + if (frame_out) { + if (mpp_frame_get_info_change(frame_out)) { + mpp_log("decode_get_frame get info changed found\n"); + dec_mpi->control(dec_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL); + } else { + /* + mpp_log_f("decoded frame %d width %d height %d\n", + frame_count, mpp_frame_get_width(frame_out), + mpp_frame_get_height(frame_out)); + */ + if (fp_dec_out) + dump_mpp_frame_to_file(frame_out, fp_dec_out); + mpi_rc_calc_stat(ctx, frame_in, frame_out); + mpi_rc_log_stat(ctx, frame_count, (frame_count + 1) % mpp_cfg.fps_in == 0, + frame_count + 1 == num_frames); + dec_get_frm = 1; + } + dec_frm_eos = mpp_frame_get_eos(frame_out); + mpp_frame_deinit(&frame_out); + frame_out = NULL; + } + + // if last packet is send but last frame is not found continue + if (pkt_eos && dec_pkt_done && !dec_frm_eos) + continue; + + if (dec_get_frm) + break; + } while (1); + + if (dec_pkt_done) + break; + + msleep(50); + } while (1); + + mpp_packet_deinit(&packet); // encoder packet deinit + } + frame_count++; + + ret = enc_mpi->enqueue(enc_ctx, MPP_PORT_OUTPUT, enc_task); + if (ret) { + mpp_err("mpp task output enqueue failed\n"); + goto MPP_TEST_OUT; + } + break; + } + } while (1); + + if (num_frames && frame_count >= num_frames) { + mpp_log_f("codec max %d frames", frame_count); + break; + } + if (frm_eos && pkt_eos) + break; + } + + ret = enc_mpi->reset(enc_ctx); + if (MPP_OK != ret) { + mpp_err("mpi->reset enc failed\n"); + goto MPP_TEST_OUT; + } + + ret = dec_mpi->reset(dec_ctx); + if (MPP_OK != ret) { + mpp_err("mpi->reset dec failed\n"); + goto MPP_TEST_OUT; + } + + stat->avg_bitrate = (RK_U64)stream_size * mpp_cfg.fps_out / frame_count; + mpp_log_f("avg_bitrate %d byte/s", stat->avg_bitrate); + if (fp_stat) + fprintf(fp_stat, "%d\n", stat->avg_bitrate); + +MPP_TEST_OUT: + + if (frame_tmp) + mpp_frame_deinit(&frame_tmp); + // encoder deinit + if (enc_ctx) { + mpp_destroy(enc_ctx); + enc_ctx = NULL; + } + + if (frame_in) { + mpp_frame_deinit(&frame_in); + frame_in = NULL; + } + + for (i = 0; i < MPI_RC_IO_COUNT; i++) { + if (frm_buf[i]) { + mpp_buffer_put(frm_buf[i]); + frm_buf[i] = NULL; + } + + if (pkt_buf[i]) { + mpp_buffer_put(pkt_buf[i]); + pkt_buf[i] = NULL; + } + } + + if (frm_grp) { + mpp_buffer_group_put(frm_grp); + frm_grp = NULL; + } + + if (pkt_grp) { + mpp_buffer_group_put(pkt_grp); + pkt_grp = NULL; + } + + // decoder deinit + if (dec_packet) { + mpp_packet_deinit(&dec_packet); + dec_packet = NULL; + } + + if (frame_out) { + mpp_frame_deinit(&frame_out); + frame_out = NULL; + } + + if (dec_ctx) { + mpp_destroy(dec_ctx); + dec_ctx = NULL; + } + + MPP_FREE(dec_in_buf); + + if (MPP_OK == ret) + mpp_log("test success total frame %d bps %lld\n", + frame_count, (RK_U64)((stream_size * 8 * mpp_cfg.fps_out) / frame_count)); + else + mpp_err("mpi_rc_test failed ret %d\n", ret); + + 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 'w': + if (next) { + cmd->width = atoi(next); + } else { + mpp_err("invalid input width\n"); + goto PARSE_OPINIONS_OUT; + } + break; + case 'h': + if ((*(opt + 1) != '\0') && !strncmp(opt, "help", 4)) { + mpi_rc_test_help(); + err = 1; + goto PARSE_OPINIONS_OUT; + } else if (next) { + cmd->height = atoi(next); + } else { + mpp_log("input height is invalid\n"); + goto PARSE_OPINIONS_OUT; + } + break; + case 'f': + if (next) { + cmd->format = (MppFrameFormat)atoi(next); + err = ((cmd->format >= MPP_FMT_YUV_BUTT && cmd->format < MPP_FRAME_FMT_RGB) || + cmd->format >= MPP_FMT_RGB_BUTT); + } + + if (!next || err) { + mpp_err("invalid input format %d\n", cmd->format); + 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 'y': + if (next) { + strncpy(cmd->file_dec_out, next, MPI_RC_FILE_NAME_LEN); + cmd->file_dec_out[strlen(next)] = '\0'; + cmd->have_dec_out = 1; + } else { + mpp_log("dec output file is invalid\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 'c': + if (next) { + cmd->item_flag = strtol(next, NULL, 0); + } else { + mpp_err("invalid input item_flag\n"); + goto PARSE_OPINIONS_OUT; + } + break; + 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("dec out file name: %s\n", cmd->file_dec_out); + mpp_log("stat file name: %s\n", cmd->file_stat); + mpp_log("width : %d\n", cmd->width); + mpp_log("height : %d\n", cmd->height); + mpp_log("type : %d\n", cmd->type); + mpp_log("debug flag : %x\n", cmd->debug); + mpp_log("format : %d\n", cmd->format); + mpp_log("num frames : %d\n", cmd->num_frames); + mpp_log("item flag : 0x%x\n", cmd->item_flag); +} + +int main(int argc, char **argv) +{ + MPP_RET ret = MPP_OK; + MpiRcTestCtx 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; +} +