| /* |
| * 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 ~ (63 + (bitdepth - 10)*6) \n" |
| " - 10bit input: 0 ~ 63\n" |
| " - 12bit input: 0 ~ 75\n" |
| }, |
| { |
| '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-12)\n" |
| " - Note: 8bit input will be converted to 10bit" |
| }, |
| { |
| ARGS_NO_KEY, "input-csp", ARGS_VAL_TYPE_INTEGER, 0, NULL, |
| "input color space (chroma format)\n" |
| " - 0: 400\n" |
| " - 2: 422\n" |
| " - 3: 444\n" |
| " - 4: 4444\n" |
| " - 5: P2(Planar Y, Combined CbCr, 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-offset-c1", ARGS_VAL_TYPE_INTEGER, 0, NULL, |
| "QP offset value for Component 1 (Cb)" |
| }, |
| { |
| ARGS_NO_KEY, "qp-offset-c2", ARGS_VAL_TYPE_INTEGER, 0, NULL, |
| "QP offset value for Component 2 (Cr)" |
| }, |
| { |
| ARGS_NO_KEY, "qp-offset-c3", ARGS_VAL_TYPE_INTEGER, 0, NULL, |
| "QP offset value for Component 3" |
| }, |
| { |
| 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-c0", ARGS_VAL_TYPE_STRING, 0, NULL, |
| "custom quantization matrix for component 0 (Y) \"q1 q2 ... q63 q64\"" |
| }, |
| { |
| ARGS_NO_KEY, "q-matrix-c1", ARGS_VAL_TYPE_STRING, 0, NULL, |
| "custom quantization matrix for component 1 (Cb) \"q1 q2 ... q63 q64\"" |
| }, |
| { |
| ARGS_NO_KEY, "q-matrix-c2", ARGS_VAL_TYPE_STRING, 0, NULL, |
| "custom quantization matrix for component 2 (Cr) \"q1 q2 ... q63 q64\"" |
| }, |
| { |
| ARGS_NO_KEY, "q-matrix-c3", ARGS_VAL_TYPE_STRING, 0, NULL, |
| "custom quantization matrix for component 3 \"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[OAPV_MAX_CC][512]; // raster-scan order |
| 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); |
| strcpy(vars->profile, "422-10"); |
| args_set_variable_by_key_long(opts, "level", vars->level); |
| strcpy(vars->level, "4.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); |
| strcpy(vars->fps, "60"); |
| args_set_variable_by_key_long(opts, "q-matrix-c0", vars->q_matrix[0]); |
| strcpy(vars->q_matrix[0], ""); |
| args_set_variable_by_key_long(opts, "q-matrix-c1", vars->q_matrix[1]); |
| strcpy(vars->q_matrix[1], ""); |
| args_set_variable_by_key_long(opts, "q-matrix-c2", vars->q_matrix[2]); |
| strcpy(vars->q_matrix[2], ""); |
| args_set_variable_by_key_long(opts, "q-matrix-c3", vars->q_matrix[3]); |
| strcpy(vars->q_matrix[3], ""); |
| args_set_variable_by_key_long(opts, "threads", &vars->threads); |
| vars->threads = 1; /* default */ |
| args_set_variable_by_key_long(opts, "preset", vars->preset); |
| strcpy(vars->preset, ""); |
| |
| 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_offset_c1); |
| ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, qp_offset_c2); |
| ARGS_SET_PARAM_VAR_KEY_LONG(opts, param, qp_offset_c3); |
| |
| 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 int write_rec_img(char *fname, oapv_imgb_t *img, int flag_y4m) |
| { |
| if(flag_y4m) { |
| if(write_y4m_frame_header(fname)) |
| return -1; |
| } |
| if(imgb_write(fname, img)) |
| return -1; |
| return 0; |
| } |
| |
| 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) |
| { |
| int q_len[OAPV_MAX_CC]; |
| /* 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 */ |
| for(int c = 0; c < OAPV_MAX_CC; c++) { |
| q_len[c] = (int)strlen(vars->q_matrix[c]); |
| if(q_len[c] > 0) { |
| param->use_q_matrix = 1; |
| char *qstr = vars->q_matrix[c]; |
| int qcnt = 0; |
| while(strlen(qstr) > 0 && qcnt < OAPV_BLK_D) { |
| int t0, read; |
| sscanf(qstr, "%d%n", &t0, &read); |
| if(t0 < 1 || t0 > 255) { |
| logerr("input value (%d) for q_matrix[%d][%d] is invalid\n", t0, c, qcnt); |
| return -1; |
| } |
| param->q_matrix[c][qcnt] = t0; |
| qstr += read; |
| qcnt++; |
| } |
| if(qcnt < OAPV_BLK_D) { |
| logerr("input number of q_matrix[%d] is not enough\n", c); |
| return -1; |
| } |
| } |
| } |
| |
| param->csp = vars->input_csp; |
| |
| /* update level idc */ |
| float tmp_level = 0; |
| sscanf(vars->level, "%f", &tmp_level); |
| param->level_idc = (int)((tmp_level * 30.0) + 0.5); |
| /* 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; |
| } |
| |
| /* update tile */ |
| if (param->tile_w_mb < OAPV_MIN_TILE_W_MB) { |
| param->tile_w_mb = OAPV_MIN_TILE_W_MB; |
| } |
| if (param->tile_h_mb < OAPV_MIN_TILE_H_MB) { |
| param->tile_h_mb = OAPV_MIN_TILE_H_MB; |
| } |
| |
| int tile_w = param->tile_w_mb << OAPV_LOG2_MB_W; |
| int tile_h = param->tile_h_mb << OAPV_LOG2_MB_H; |
| int tile_cols = (param->w + tile_w - 1) / tile_w; |
| int tile_rows = (param->h + tile_h - 1) / tile_h; |
| if (tile_cols > OAPV_MAX_TILE_COLS) { |
| param->tile_w_mb = (((param->w + OAPV_MB_W - 1) >> OAPV_LOG2_MB_W) + OAPV_MAX_TILE_COLS - 1) / OAPV_MAX_TILE_COLS; |
| } |
| if (tile_rows > OAPV_MAX_TILE_ROWS) { |
| param->tile_h_mb = (((param->h + OAPV_MB_H - 1) >> OAPV_LOG2_MB_H) + OAPV_MAX_TILE_ROWS - 1) / OAPV_MAX_TILE_ROWS; |
| } |
| |
| 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; |
| int frm_cnt[MAX_NUM_FRMS] = { 0 }; |
| double bitrate_tot; // total bitrate (byte) |
| double psnr_avg[MAX_NUM_FRMS][MAX_NUM_CC] = { 0 }; |
| int is_inp_y4m, is_rec_y4m = 0; |
| 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_inp_y4m = y4m_test(fp_inp); |
| if(is_inp_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) { |
| ret = check_file_name_type(args_var->fname_rec); |
| if(ret > 0) { |
| is_rec_y4m = 1; |
| } |
| else if(ret == 0) { |
| is_rec_y4m = 0; |
| } |
| else { // invalid or unknown file name type |
| logerr("unknown file name type for reconstructed video\n"); |
| ret = -1; goto ERR; |
| } |
| 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_inp_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 fidx = 0; fidx < num_frames; fidx++) { |
| if(is_rec) { |
| if(args_var->input_depth != 10) { |
| imgb_cpy(imgb_w, rfrms.frm[fidx].imgb); |
| imgb_o = imgb_w; |
| } |
| else { |
| imgb_o = rfrms.frm[fidx].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(frm_cnt[fidx] == 0 && is_rec_y4m) { |
| if(write_y4m_header(args_var->fname_rec, imgb_o)) { |
| logerr("cannot write Y4M header\n"); |
| ret = -1; |
| goto ERR; |
| } |
| } |
| if(write_rec_img(args_var->fname_rec, imgb_o, is_rec_y4m)) { |
| logerr("cannot write reconstructed video\n"); |
| ret = -1; |
| goto ERR; |
| } |
| } |
| print_stat_frms(&stat, &ifrms, &rfrms, psnr_avg); |
| frm_cnt[fidx] += 1; |
| } |
| 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; |
| } |