/* * Copyright (c) 2022 Samsung Electronics Co., Ltd. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * - Neither the name of the copyright owner, nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "oapv.h" #include "oapv_app_util.h" #include "oapv_app_args.h" #include "oapv_app_y4m.h" #define MAX_BS_BUF (128 * 1024 * 1024) #define MAX_NUM_FRMS (1) // supports only 1-frame in an access unit #define FRM_IDX (0) // supports only 1-frame in an access unit #define MAX_NUM_CC (OAPV_MAX_CC) // Max number of color componets (upto 4:4:4:4) typedef enum _STATES { STATE_ENCODING, STATE_SKIPPING, STATE_STOP } STATES; // clang-format off /* define various command line options as a table */ static const args_opt_t enc_args_opts[] = { { 'v', "verbose", ARGS_VAL_TYPE_INTEGER, 0, NULL, "verbose (log) level\n" " - 0: no message\n" " - 1: only error message\n" " - 2: simple messages\n" " - 3: frame-level messages" }, { 'i', "input", ARGS_VAL_TYPE_STRING | ARGS_VAL_TYPE_MANDATORY, 0, NULL, "file name of input video" }, { 'o', "output", ARGS_VAL_TYPE_STRING, 0, NULL, "file name of output bitstream" }, { 'r', "recon", ARGS_VAL_TYPE_STRING, 0, NULL, "file name of reconstructed video" }, { 'w', "width", ARGS_VAL_TYPE_INTEGER | ARGS_VAL_TYPE_MANDATORY, 0, NULL, "pixel width of input video" }, { 'h', "height", ARGS_VAL_TYPE_INTEGER | ARGS_VAL_TYPE_MANDATORY, 0, NULL, "pixel height of input video" }, { 'q', "qp", ARGS_VAL_TYPE_INTEGER, 0, NULL, "QP value (0~51)" }, { 'z', "fps", ARGS_VAL_TYPE_STRING | ARGS_VAL_TYPE_MANDATORY, 0, NULL, "frame rate (frame per second))" }, { 'm', "threads", ARGS_VAL_TYPE_INTEGER, 0, NULL, "force to use a specific number of threads" }, { ARGS_NO_KEY, "preset", ARGS_VAL_TYPE_STRING, 0, NULL, "encoder preset [fastest, fast, medium, slow, placebo]" }, { 'd', "input-depth", ARGS_VAL_TYPE_INTEGER, 0, NULL, "input bit depth (8, 10) " }, { ARGS_NO_KEY, "input-csp", ARGS_VAL_TYPE_INTEGER, 0, NULL, "input color space (chroma format)\n" " - 0: YUV400\n" " - 1: YUV420\n" " - 2: YUV422\n" " - 3: YUV444\n" " - 4: YUV4444\n" " - 5: P2(Planar Y, Combined UV, 422)" }, { ARGS_NO_KEY, "profile", ARGS_VAL_TYPE_STRING, 0, NULL, "profile setting flag (422-10)" }, { ARGS_NO_KEY, "level", ARGS_VAL_TYPE_STRING, 0, NULL, "level setting (1, 1.1, 2, 2.1, 3, 3.1, 4, 4.1, 5, 5.1, 6, 6.1, 7, 7.1)" }, { ARGS_NO_KEY, "band", ARGS_VAL_TYPE_INTEGER, 0, NULL, "band setting (0, 1, 2, 3)" }, { ARGS_NO_KEY, "max-au", ARGS_VAL_TYPE_INTEGER, 0, NULL, "maximum number of access units to be encoded" }, { ARGS_NO_KEY, "seek", ARGS_VAL_TYPE_INTEGER, 0, NULL, "number of skipped access units before encoding" }, { ARGS_NO_KEY, "qp-cb-offset", ARGS_VAL_TYPE_INTEGER, 0, NULL, "QP offset value for Cb" }, { ARGS_NO_KEY, "qp-cr-offset", ARGS_VAL_TYPE_INTEGER, 0, NULL, "QP offset value for Cr" }, { ARGS_NO_KEY, "tile-w-mb", ARGS_VAL_TYPE_INTEGER, 0, NULL, "width of tile in units of MBs" }, { ARGS_NO_KEY, "tile-h-mb", ARGS_VAL_TYPE_INTEGER, 0, NULL, "height of tile in units of MBs" }, { ARGS_NO_KEY, "bitrate", ARGS_VAL_TYPE_STRING, 0, NULL, "enable ABR rate control\n" " bitrate in terms of kilo-bits per second: Kbps(none,K,k), Mbps(M,m)\n" " ex) 100 = 100K = 0.1M" }, { ARGS_NO_KEY, "use-filler", ARGS_VAL_TYPE_INTEGER, 0, NULL, "user filler flag" }, { ARGS_NO_KEY, "q-matrix-y", ARGS_VAL_TYPE_STRING, 0, NULL, "custom quantization matrix for Y \"q1 q2 ... q63 q64\"" }, { ARGS_NO_KEY, "q-matrix-u", ARGS_VAL_TYPE_STRING, 0, NULL, "custom quantization matrix for U \"q1 q2 ... q63 q64\"" }, { ARGS_NO_KEY, "q-matrix-v", ARGS_VAL_TYPE_STRING, 0, NULL, "custom quantization matrix for V \"q1 q2 ... q63 q64\"" }, { ARGS_NO_KEY, "q-matrix-x", ARGS_VAL_TYPE_STRING, 0, NULL, "custom quantization matrix for X \"q1 q2 ... q63 q64\"" }, { ARGS_NO_KEY, "hash", ARGS_VAL_TYPE_NONE, 0, NULL, "embed frame hash value for conformance checking in decoding" }, {ARGS_END_KEY, "", ARGS_VAL_TYPE_NONE, 0, NULL, ""} /* termination */ }; // clang-format on #define NUM_ARGS_OPT ((int)(sizeof(enc_args_opts) / sizeof(enc_args_opts[0]))) typedef struct args_var { /* variables for options */ char fname_inp[256]; char fname_out[256]; char fname_rec[256]; int max_au; int hash; int input_depth; int input_csp; int seek; int threads; char profile[32]; char level[32]; int band; char bitrate[64]; char fps[256]; char q_matrix_y[512]; char q_matrix_u[512]; char q_matrix_v[512]; char q_matrix_x[512]; char preset[32]; oapve_param_t *param; } args_var_t; static args_var_t *args_init_vars(args_parser_t *args, oapve_param_t *param) { args_opt_t *opts; args_var_t *vars; opts = args->opts; vars = malloc(sizeof(args_var_t)); assert_rv(vars != NULL, NULL); memset(vars, 0, sizeof(args_var_t)); vars->param = param; /*args_set_variable_by_key_long(opts, "config", args->fname_cfg);*/ args_set_variable_by_key_long(opts, "input", vars->fname_inp); args_set_variable_by_key_long(opts, "output", vars->fname_out); args_set_variable_by_key_long(opts, "recon", vars->fname_rec); args_set_variable_by_key_long(opts, "max-au", &vars->max_au); args_set_variable_by_key_long(opts, "hash", &vars->hash); args_set_variable_by_key_long(opts, "verbose", &op_verbose); op_verbose = VERBOSE_SIMPLE; /* default */ args_set_variable_by_key_long(opts, "input-depth", &vars->input_depth); vars->input_depth = 10; /* default */ args_set_variable_by_key_long(opts, "input-csp", &vars->input_csp); vars->input_csp = -1; args_set_variable_by_key_long(opts, "seek", &vars->seek); args_set_variable_by_key_long(opts, "profile", vars->profile); strncpy(vars->profile, "422-10", sizeof(vars->profile) - 1); args_set_variable_by_key_long(opts, "level", vars->level); strncpy(vars->level, "4.1", sizeof(vars->level) - 1); args_set_variable_by_key_long(opts, "band", &vars->band); vars->band = 2; /* default */ args_set_variable_by_key_long(opts, "bitrate", vars->bitrate); args_set_variable_by_key_long(opts, "fps", vars->fps); strncpy(vars->fps, "60", sizeof(vars->fps) - 1); args_set_variable_by_key_long(opts, "q-matrix-y", vars->q_matrix_y); strncpy(vars->q_matrix_y, "", sizeof(vars->q_matrix_y) - 1); args_set_variable_by_key_long(opts, "q-matrix-u", vars->q_matrix_u); strncpy(vars->q_matrix_u, "", sizeof(vars->q_matrix_y) - 1); args_set_variable_by_key_long(opts, "q-matrix-v", vars->q_matrix_v); strncpy(vars->q_matrix_v, "", sizeof(vars->q_matrix_y) - 1); args_set_variable_by_key_long(opts, "q-matrix-x", vars->q_matrix_x); strncpy(vars->q_matrix_x, "", sizeof(vars->q_matrix_x) - 1); args_set_variable_by_key_long(opts, "threads", &vars->threads); vars->threads = 1; /* default */ args_set_variable_by_key_long(opts, "preset", vars->preset); strncpy(vars->preset, "", sizeof(vars->preset) - 1); ARGS_SET_PARAM_VAR_KEY(opts, param, w); ARGS_SET_PARAM_VAR_KEY(opts, param, h); ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, qp); ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, use_filler); ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, tile_w_mb); ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, tile_h_mb); ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, qp_cb_offset); ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, qp_cr_offset); return vars; } static void print_usage(const char **argv) { int i; char str[1024]; args_parser_t *args; args_var_t *args_var = NULL; oapve_param_t default_param; oapve_param_default(&default_param); args = args_create(enc_args_opts, NUM_ARGS_OPT); if(args == NULL) goto ERR; args_var = args_init_vars(args, &default_param); if(args_var == NULL) goto ERR; logv2("Syntax: \n"); logv2(" %s -i 'input-file' [ options ] \n\n", argv[0]); logv2("Options:\n"); logv2(" --help\n : list options\n"); for(i = 0; i < args->num_option; i++) { if(args->get_help(args, i, str) < 0) return; logv2("%s\n", str); } ERR: if(args) args->release(args); if(args_var) free(args_var); } static int check_conf(oapve_cdesc_t *cdesc, args_var_t *vars) { int i; for(i = 0; i < cdesc->max_num_frms; i++) { if(vars->hash && strlen(vars->fname_rec) == 0) { logerr("cannot use frame hash without reconstructed picture option!\n"); return -1; } } return 0; } static int set_extra_config(oapve_t id, args_var_t *vars, oapve_param_t *param) { int ret = 0, size, value; if(vars->hash) { value = 1; size = 4; ret = oapve_config(id, OAPV_CFG_SET_USE_FRM_HASH, &value, &size); if(OAPV_FAILED(ret)) { logerr("failed to set config for using frame hash\n"); return -1; } } return ret; } static void print_commandline(int argc, const char **argv) { int i; if(op_verbose < VERBOSE_FRAME) return; logv3("Command line: "); for(i = 0; i < argc; i++) { logv3("%s ", argv[i]); } logv3("\n\n"); } static void print_config(args_var_t *vars, oapve_param_t *param) { if(op_verbose < VERBOSE_FRAME) return; logv3_line("Configurations"); logv3("Input sequence : %s \n", vars->fname_inp); if(strlen(vars->fname_out) > 0) { logv3("Output bitstream : %s \n", vars->fname_out); } if(strlen(vars->fname_rec) > 0) { logv3("Reconstructed sequence : %s \n", vars->fname_rec); } logv3(" profile = %s\n", vars->profile); logv3(" width = %d\n", param->w); logv3(" height = %d\n", param->h); logv3(" FPS = %.2f\n", (float)param->fps_num / param->fps_den); logv3(" QP = %d\n", param->qp); logv3(" max number of AUs = %d\n", vars->max_au); logv3(" rate control type = %s\n", (param->rc_type == OAPV_RC_ABR) ? "average Bitrate" : "constant QP"); if(param->rc_type == OAPV_RC_ABR) { logv3(" target bitrate = %dkbps\n", param->bitrate); } logv3(" tile size = %d x %d\n", param->tile_w_mb * OAPV_MB_W, param->tile_h_mb * OAPV_MB_H); } static void print_stat_au(oapve_stat_t *stat, int au_cnt, oapve_param_t *param, int max_au, double bitrate_tot, oapv_clk_t clk_au, oapv_clk_t clk_tot) { if(op_verbose >= VERBOSE_FRAME) { logv3_line(""); logv3("AU %-5d %10d-bytes %3d-frame(s) %10d msec\n", au_cnt, stat->write, stat->aui.num_frms, oapv_clk_msec(clk_au)); } else { int total_time = ((int)oapv_clk_msec(clk_tot) / 1000); int h = total_time / 3600; total_time = total_time % 3600; int m = total_time / 60; total_time = total_time % 60; int s = total_time; double curr_bitrate = bitrate_tot; curr_bitrate *= (((float)param->fps_num / param->fps_den) * 8); curr_bitrate /= (au_cnt + 1); curr_bitrate /= 1000; logv2("[ %d / %d AU(s) ] [ %.2f AU/sec ] [ %.4f kbps ] [ %2dh %2dm %2ds ] \r", au_cnt, max_au, ((float)(au_cnt + 1) * 1000) / ((float)oapv_clk_msec(clk_tot)), curr_bitrate, h, m, s); fflush(stdout); } } static void print_stat_frms(oapve_stat_t *stat, oapv_frms_t *ifrms, oapv_frms_t *rfrms, double psnr_avg[MAX_NUM_FRMS][MAX_NUM_CC]) { int i, j; oapv_frm_info_t *finfo; double psnr[MAX_NUM_FRMS][MAX_NUM_CC] = { 0 }; assert(stat->aui.num_frms == ifrms->num_frms); assert(stat->aui.num_frms <= MAX_NUM_FRMS); // calculate PSNRs if(rfrms != NULL) { for(i = 0; i < stat->aui.num_frms; i++) { if(rfrms->frm[i].imgb) { measure_psnr(ifrms->frm[i].imgb, rfrms->frm[i].imgb, psnr[i], OAPV_CS_GET_BIT_DEPTH(ifrms->frm[i].imgb->cs)); for(j = 0; j < MAX_NUM_CC; j++) { psnr_avg[i][j] += psnr[i][j]; } } } } // print verbose messages if(op_verbose < VERBOSE_FRAME) return; finfo = stat->aui.frm_info; for(i = 0; i < stat->aui.num_frms; i++) { // clang-format off const char* str_frm_type = finfo[i].pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME ? "PRIMARY" : finfo[i].pbu_type == OAPV_PBU_TYPE_NON_PRIMARY_FRAME ? "NON-PRIMARY" : finfo[i].pbu_type == OAPV_PBU_TYPE_PREVIEW_FRAME ? "PREVIEW" : finfo[i].pbu_type == OAPV_PBU_TYPE_DEPTH_FRAME ? "DEPTH" : finfo[i].pbu_type == OAPV_PBU_TYPE_ALPHA_FRAME ? "ALPHA" : "UNKNOWN"; // clang-format on logv3("- FRM %-2d GID %-5d %-11s %9d-bytes %8.4fdB %8.4fdB %8.4fdB\n", i, finfo[i].group_id, str_frm_type, stat->frm_size[i], psnr[i][0], psnr[i][1], psnr[i][2]); } fflush(stdout); fflush(stderr); } static int kbps_str_to_int(char *str) { int kbps; if(strchr(str, 'K') || strchr(str, 'k')) { char *tmp = strtok(str, "Kk "); kbps = (int)(atof(tmp)); } else if(strchr(str, 'M') || strchr(str, 'm')) { char *tmp = strtok(str, "Mm "); kbps = (int)(atof(tmp) * 1000); } else { kbps = atoi(str); } return kbps; } static int update_param(args_var_t *vars, oapve_param_t *param) { /* update reate controller parameters */ if(strlen(vars->bitrate) > 0) { param->bitrate = kbps_str_to_int(vars->bitrate); param->rc_type = OAPV_RC_ABR; } /* update q_matrix */ int len_y = (int)strlen(vars->q_matrix_y); if(len_y > 0) { param->use_q_matrix = 1; char *tmp = vars->q_matrix_y; int cnt = 0; int len_cnt = 0; while(len_cnt < len_y && cnt < OAPV_BLK_D) { sscanf(tmp, "%d", ¶m->q_matrix_y[cnt]); if(param->q_matrix_y[cnt] < 1 || param->q_matrix_y[cnt] > 255) { logerr("input value of q_matrix_y is invalid\n"); return -1; } len_cnt += (int)log10(param->q_matrix_y[cnt]) + 2; tmp = vars->q_matrix_y + len_cnt; cnt++; } if(cnt < OAPV_BLK_D) { logerr("input number of q_matrix_y is not enough\n"); return -1; } } int len_u = (int)strlen(vars->q_matrix_u); if(len_u > 0) { param->use_q_matrix = 1; char *tmp = vars->q_matrix_u; int cnt = 0; int len_cnt = 0; while(len_cnt < len_u && cnt < OAPV_BLK_D) { sscanf(tmp, "%d", ¶m->q_matrix_u[cnt]); if(param->q_matrix_u[cnt] < 1 || param->q_matrix_u[cnt] > 255) { logerr("input value of q_matrix_u is invalid\n"); return -1; } len_cnt += (int)log10(param->q_matrix_u[cnt]) + 2; tmp = vars->q_matrix_u + len_cnt; cnt++; } if(cnt < OAPV_BLK_D) { logerr("input number of q_matrix_u is not enough\n"); return -1; } } int len_v = (int)strlen(vars->q_matrix_v); if(len_v > 0) { param->use_q_matrix = 1; char *tmp = vars->q_matrix_v; int cnt = 0; int len_cnt = 0; while(len_cnt < len_v && cnt < OAPV_BLK_D) { sscanf(tmp, "%d", ¶m->q_matrix_v[cnt]); if(param->q_matrix_v[cnt] < 1 || param->q_matrix_v[cnt] > 255) { logerr("input value of q_matrix_v is invalid\n"); return -1; } len_cnt += (int)log10(param->q_matrix_v[cnt]) + 2; tmp = vars->q_matrix_v + len_cnt; cnt++; } if(cnt < OAPV_BLK_D) { logerr("input number of q_matrix_v is not enough\n"); return -1; } } int len_x = (int)strlen(vars->q_matrix_x); if (len_x > 0) { param->use_q_matrix = 1; char* tmp = vars->q_matrix_x; int cnt = 0; int len_cnt = 0; while (len_cnt < len_x && cnt < OAPV_BLK_D) { sscanf(tmp, "%d", ¶m->q_matrix_x[cnt]); if (param->q_matrix_x[cnt] < 1 || param->q_matrix_x[cnt] > 255) { logerr("input value of q_matrix_x is invalid\n"); return -1; } len_cnt += (int)log10(param->q_matrix_x[cnt]) + 2; tmp = vars->q_matrix_x + len_cnt; cnt++; } if (cnt < OAPV_BLK_D) { logerr("input number of q_matrix_x is not enough\n"); return -1; } } if(param->use_q_matrix) { if(len_y == 0) { for(int i = 0; i < OAPV_BLK_D; i++) { param->q_matrix_y[i] = 16; } } if(len_u == 0) { for(int i = 0; i < OAPV_BLK_D; i++) { param->q_matrix_u[i] = 16; } } if(len_v == 0) { for(int i = 0; i < OAPV_BLK_D; i++) { param->q_matrix_v[i] = 16; } } if (len_x == 0) { for (int i = 0; i < OAPV_BLK_D; i++) { param->q_matrix_x[i] = 16; } } } param->csp = vars->input_csp; /* update level idc */ double tmp_level = 0; sscanf(vars->level, "%lf", &tmp_level); param->level_idc = tmp_level * 30; /* update band idc */ param->band_idc = vars->band; /* update fps */ if(strpbrk(vars->fps, "/") != NULL) { sscanf(vars->fps, "%d/%d", ¶m->fps_num, ¶m->fps_den); } else if(strpbrk(vars->fps, ".") != NULL) { float tmp_fps = 0; sscanf(vars->fps, "%f", &tmp_fps); param->fps_num = tmp_fps * 10000; param->fps_den = 10000; } else { sscanf(vars->fps, "%d", ¶m->fps_num); param->fps_den = 1; } if(strlen(vars->preset) > 0) { if(strcmp(vars->preset, "fastest") == 0) { param->preset = OAPV_PRESET_FASTEST; } else if(strcmp(vars->preset, "fast") == 0) { param->preset = OAPV_PRESET_FAST; } else if(strcmp(vars->preset, "medium") == 0) { param->preset = OAPV_PRESET_MEDIUM; } else if(strcmp(vars->preset, "slow") == 0) { param->preset = OAPV_PRESET_SLOW; } else if(strcmp(vars->preset, "placebo") == 0) { param->preset = OAPV_PRESET_PLACEBO; } else { logerr("input value of preset is invalid\n"); return -1; } } else { param->preset = OAPV_PRESET_DEFAULT; } return 0; } int main(int argc, const char **argv) { args_parser_t *args = NULL; args_var_t *args_var = NULL; STATES state = STATE_ENCODING; unsigned char *bs_buf = NULL; FILE *fp_inp = NULL; oapve_t id = NULL; oapvm_t mid = NULL; oapve_cdesc_t cdesc; oapve_param_t *param = NULL; oapv_bitb_t bitb; oapve_stat_t stat; oapv_imgb_t *imgb_r = NULL; // image buffer for read oapv_imgb_t *imgb_w = NULL; // image buffer for write oapv_imgb_t *imgb_i = NULL; // image buffer for input oapv_imgb_t *imgb_o = NULL; // image buffer for output oapv_frms_t ifrms = { 0 }; // frames for input oapv_frms_t rfrms = { 0 }; // frames for reconstruction int ret; oapv_clk_t clk_beg, clk_end, clk_tot; oapv_mtime_t au_cnt, au_skip; double bitrate_tot; // total bitrate (byte) double psnr_avg[MAX_NUM_FRMS][MAX_NUM_CC] = { 0 }; int is_y4m; y4m_params_t y4m; int is_out = 0, is_rec = 0; char *errstr = NULL; int cfmt; // color format const int num_frames = MAX_NUM_FRMS; // number of frames in an access unit /* help message */ if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { print_usage(argv); return 0; } /* set default parameters */ memset(&cdesc, 0, sizeof(oapve_cdesc_t)); param = &cdesc.param[FRM_IDX]; ret = oapve_param_default(param); if(OAPV_FAILED(ret)) { logerr("cannot set default parameter\n"); ret = -1; goto ERR; } /* parse command line */ args = args_create(enc_args_opts, NUM_ARGS_OPT); if(args == NULL) { logerr("cannot create argument parser\n"); ret = -1; goto ERR; } args_var = args_init_vars(args, param); if(args_var == NULL) { logerr("cannot initialize argument parser\n"); ret = -1; goto ERR; } if(args->parse(args, argc, argv, &errstr)) { logerr("command parsing error (%s)\n", errstr); ret = -1; goto ERR; } // print logo logv2(" ____ ___ ___ _ __\n"); logv2(" / __ \\___ ___ ___ / _ | / _ \\ | / / Encoder\n"); logv2("/ /_/ / _ \\/ -_) _ \\/ __ |/ ___/ |/ / \n"); logv2("\\____/ .__/\\__/_//_/_/ |_/_/ |___/ \n"); logv2(" /_/ \n"); logv2("\n"); // print command line string for information print_commandline(argc, argv); // check mandatory arguments if(args->check_mandatory(args, &errstr)) { logerr("'--%s' argument is mandatory\n", errstr); ret = -1; goto ERR; } /* try to open input file */ fp_inp = fopen(args_var->fname_inp, "rb"); if(fp_inp == NULL) { logerr("ERROR: cannot open input file = (%s)\n", args_var->fname_inp); ret = -1; goto ERR; } /* y4m header parsing */ is_y4m = y4m_test(fp_inp); if(is_y4m) { if(y4m_header_parser(fp_inp, &y4m)) { logerr("This y4m is not supported (%s)\n", args_var->fname_inp); ret = -1; goto ERR; } y4m_update_param(args, &y4m); cfmt = y4m.color_format; // clang-format off args_var->input_csp = (cfmt == OAPV_CF_YCBCR400 ? 0 : \ (cfmt == OAPV_CF_YCBCR420 ? 1 : \ (cfmt == OAPV_CF_YCBCR422 ? 2 : \ (cfmt == OAPV_CF_YCBCR444 ? 3 : \ (cfmt == OAPV_CF_YCBCR4444 ? 4 : \ (cfmt == OAPV_CF_PLANAR2 ? 5 : -1)))))); // clang-format on if(args_var->input_csp != -1) { // force "input-csp" argument has set args->set_flag(args, "input-csp", 1); } } else { // clang-format off cfmt = (args_var->input_csp == 0 ? OAPV_CF_YCBCR400 : \ (args_var->input_csp == 1 ? OAPV_CF_YCBCR420 : \ (args_var->input_csp == 2 ? OAPV_CF_YCBCR422 : \ (args_var->input_csp == 3 ? OAPV_CF_YCBCR444 : \ (args_var->input_csp == 4 ? OAPV_CF_YCBCR4444 : \ (args_var->input_csp == 5 ? OAPV_CF_PLANAR2 : OAPV_CF_UNKNOWN)))))); // clang-format on } if(args_var->input_csp == -1) { logerr("Unknown input color space. set '--input-csp' argument\n"); ret = -1; goto ERR; } /* update parameters */ if(update_param(args_var, param)) { logerr("parameters is not proper\n"); ret = -1; goto ERR; } cdesc.max_bs_buf_size = MAX_BS_BUF; /* maximum bitstream buffer size */ cdesc.max_num_frms = MAX_NUM_FRMS; cdesc.threads = args_var->threads; if(check_conf(&cdesc, args_var)) { logerr("invalid configuration\n"); ret = -1; goto ERR; } if(strlen(args_var->fname_out) > 0) { clear_data(args_var->fname_out); is_out = 1; } if(strlen(args_var->fname_rec) > 0) { clear_data(args_var->fname_rec); is_rec = 1; } /* allocate bitstream buffer */ bs_buf = (unsigned char *)malloc(MAX_BS_BUF); if(bs_buf == NULL) { logerr("cannot allocate bitstream buffer, size=%d", MAX_BS_BUF); ret = -1; goto ERR; } /* create encoder */ id = oapve_create(&cdesc, NULL); if(id == NULL) { logerr("cannot create OAPV encoder\n"); ret = -1; goto ERR; } /* create metadata handler */ mid = oapvm_create(&ret); if(mid == NULL || OAPV_FAILED(ret)) { logerr("cannot create OAPV metadata handler\n"); ret = -1; goto ERR; } if(set_extra_config(id, args_var, param)) { logerr("cannot set extra configurations\n"); ret = -1; goto ERR; } print_config(args_var, param); bitrate_tot = 0; bitb.addr = bs_buf; bitb.bsize = MAX_BS_BUF; if(args_var->seek > 0) { state = STATE_SKIPPING; } clk_tot = 0; au_cnt = 0; au_skip = 0; // create input and reconstruction image buffers memset(&ifrms, 0, sizeof(oapv_frm_t)); memset(&rfrms, 0, sizeof(oapv_frm_t)); for(int i = 0; i < num_frames; i++) { if(args_var->input_depth == 10) { ifrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); } else { imgb_r = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); ifrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, 10, 0)); } if(is_rec) { if(args_var->input_depth == 10) { rfrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); } else { imgb_w = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); rfrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, 10, 0)); } rfrms.num_frms++; } ifrms.num_frms++; } /* encode pictures *******************************************************/ while(args_var->max_au == 0 || (au_cnt < args_var->max_au)) { for(int i = 0; i < num_frames; i++) { if(args_var->input_depth == 10) { imgb_i = ifrms.frm[i].imgb; } else { imgb_i = imgb_r; } ret = imgb_read(fp_inp, imgb_i, param->w, param->h, is_y4m); if(ret < 0) { logv3("reached out the end of input file\n"); ret = OAPV_OK; state = STATE_STOP; break; } if(args_var->input_depth != 10) { imgb_cpy(ifrms.frm[i].imgb, imgb_i); } ifrms.frm[i].group_id = 1; // FIX-ME : need to set properly in case of multi-frame ifrms.frm[i].pbu_type = OAPV_PBU_TYPE_PRIMARY_FRAME; } if(state == STATE_ENCODING) { /* encoding */ clk_beg = oapv_clk_get(); ret = oapve_encode(id, &ifrms, mid, &bitb, &stat, &rfrms); clk_end = oapv_clk_from(clk_beg); clk_tot += clk_end; bitrate_tot += stat.frm_size[FRM_IDX]; print_stat_au(&stat, au_cnt, param, args_var->max_au, bitrate_tot, clk_end, clk_tot); for(int i = 0; i < num_frames; i++) { if(is_rec) { if(args_var->input_depth != 10) { imgb_cpy(imgb_w, rfrms.frm[i].imgb); imgb_o = imgb_w; } else { imgb_o = rfrms.frm[i].imgb; } } /* store bitstream */ if(OAPV_SUCCEEDED(ret)) { if(is_out && stat.write > 0) { if(write_data(args_var->fname_out, bs_buf, stat.write)) { logerr("cannot write bitstream\n"); ret = -1; goto ERR; } } } else { logerr("failed to encode\n"); ret = -1; goto ERR; } // store recon image if(is_rec) { if(imgb_write(args_var->fname_rec, imgb_o)) { logerr("cannot write reconstruction image\n"); ret = -1; goto ERR; } } print_stat_frms(&stat, &ifrms, &rfrms, psnr_avg); au_cnt++; } } else if(state == STATE_SKIPPING) { if(au_skip < args_var->seek) { au_skip++; continue; } else { state = STATE_ENCODING; } } else if(state == STATE_STOP) { break; } oapvm_rem_all(mid); } logv2_line("Summary"); psnr_avg[FRM_IDX][0] /= au_cnt; psnr_avg[FRM_IDX][1] /= au_cnt; psnr_avg[FRM_IDX][2] /= au_cnt; if(cfmt == OAPV_CF_YCBCR4444) { psnr_avg[FRM_IDX][3] /= au_cnt; } logv3(" PSNR Y(dB) : %-5.4f\n", psnr_avg[FRM_IDX][0]); logv3(" PSNR U(dB) : %-5.4f\n", psnr_avg[FRM_IDX][1]); logv3(" PSNR V(dB) : %-5.4f\n", psnr_avg[FRM_IDX][2]); if(cfmt == OAPV_CF_YCBCR4444) { logv3(" PSNR T(dB) : %-5.4f\n", psnr_avg[FRM_IDX][3]); } logv3(" Total bits(bits) : %.0f\n", bitrate_tot * 8); bitrate_tot *= (((float)param->fps_num / param->fps_den) * 8); bitrate_tot /= au_cnt; bitrate_tot /= 1000; logv3(" -----------------: bitrate(kbps)\tPSNR-Y\tPSNR-U\tPSNR-V\n"); if(cfmt == OAPV_CF_YCBCR4444) { logv3(" Summary : %-4.4f\t%-5.4f\t%-5.4f\t%-5.4f\t%-5.4f\n", bitrate_tot, psnr_avg[FRM_IDX][0], psnr_avg[FRM_IDX][1], psnr_avg[FRM_IDX][2], psnr_avg[FRM_IDX][3]); } else { logv3(" Summary : %-5.4f\t%-5.4f\t%-5.4f\t%-5.4f\n", bitrate_tot, psnr_avg[FRM_IDX][0], psnr_avg[FRM_IDX][1], psnr_avg[FRM_IDX][2]); } logv2("Bitrate = %.4f kbps\n", bitrate_tot); logv2("Encoded frame count = %d\n", (int)au_cnt); logv2("Total encoding time = %.3f msec,", (float)oapv_clk_msec(clk_tot)); logv2(" %.3f sec\n", (float)(oapv_clk_msec(clk_tot) / 1000.0)); logv2("Average encoding time for a frame = %.3f msec\n", (float)oapv_clk_msec(clk_tot) / au_cnt); logv2("Average encoding speed = %.3f frames/sec\n", ((float)au_cnt * 1000) / ((float)oapv_clk_msec(clk_tot))); logv2_line(NULL); if(args_var->max_au > 0 && au_cnt != args_var->max_au) { logv3("Wrong frames count: should be %d was %d\n", args_var->max_au, (int)au_cnt); } ERR: if(imgb_r != NULL) imgb_r->release(imgb_r); if(imgb_w != NULL) imgb_w->release(imgb_w); for(int i = 0; i < num_frames; i++) { if(ifrms.frm[i].imgb != NULL) { ifrms.frm[i].imgb->release(ifrms.frm[i].imgb); } } for(int i = 0; i < num_frames; i++) { if(rfrms.frm[i].imgb != NULL) { rfrms.frm[i].imgb->release(rfrms.frm[i].imgb); } } if(id) oapve_delete(id); if(mid) oapvm_delete(mid); if(fp_inp) fclose(fp_inp); if(bs_buf) free(bs_buf); /* release bitstream buffer */ if(args) args->release(args); if(args_var) free(args_var); return ret; }