diff --git a/app/oapv_app_args.h b/app/oapv_app_args.h index e7a1fff..d464e51 100644 --- a/app/oapv_app_args.h +++ b/app/oapv_app_args.h @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "oapv.h" #define ARGS_VAL_TYPE_MANDATORY (1 << 0) /* mandatory or not */ @@ -50,6 +52,12 @@ #define ARGS_MAX_NUM_CONF_FILES (16) #define ARGS_MAX_KEY_LONG (32) +#define ARGS_MAX_DESC (1024) /* max length of an option description */ +/* upper bound of the fixed prefix of a help line: key/key_long, value type, + * "(optional)", default value string and surrounding decoration */ +#define ARGS_HELP_PREFIX_MAX (512) +/* buffer size for a formatted help line: description plus its prefix */ +#define ARGS_HELP_BUF_SIZE (ARGS_MAX_DESC + ARGS_HELP_PREFIX_MAX) typedef struct args_opt { char key; /* option keyword. ex) -f */ @@ -57,7 +65,8 @@ typedef struct args_opt { int val_type; /* value type */ int flag; /* flag to setting or not */ void *val; /* actual value */ - char desc[1024]; /* description of option */ + int val_size; /* size of value buffer for STRING type (0 for non-STRING) */ + char desc[ARGS_MAX_DESC]; /* description of option */ } args_opt_t; typedef struct args_parser args_parser_t; @@ -65,7 +74,6 @@ struct args_parser { void (*release)(args_parser_t *args); int (*parse)(args_parser_t *args, int argc, const char *argv[], char **errstr); int (*get_help)(args_parser_t *args, int idx, char *help); - int (*get_str)(args_parser_t *args, char *keyl, char *str, int *flag); int (*get_int)(args_parser_t *args, char *keyl, int *val, int *flag); int (*set_str)(args_parser_t *args, char *keyl, char *str); int (*set_int)(args_parser_t *args, char *keyl, int val); @@ -120,11 +128,25 @@ static int args_read_value(args_opt_t *ops, const char *argv) switch(ARGS_GET_CMD_OPT_VAL_TYPE(ops->val_type)) { case ARGS_VAL_TYPE_INTEGER: - *((int *)ops->val) = atoi(argv); + { + char *endptr; + long val; + errno = 0; + val = strtol(argv, &endptr, 10); + if(endptr == argv || *endptr != '\0' || errno == ERANGE || val < INT_MIN || val > INT_MAX) { + return -1; + } + *((int *)ops->val) = (int)val; + } break; case ARGS_VAL_TYPE_STRING: - strcpy((char *)ops->val, argv); + if(ops->val_size > 0) { + strncpy((char *)ops->val, argv, ops->val_size - 1); + ((char *)ops->val)[ops->val_size - 1] = '\0'; + } else { + return -1; /* STRING option without a known buffer size: reject */ + } break; default: @@ -133,48 +155,13 @@ static int args_read_value(args_opt_t *ops, const char *argv) return 0; } -static int args_get_arg(args_opt_t *ops, int idx, char *result) -{ - char vtype[32]; - char value[512]; - args_opt_t *o = ops + idx; - - switch(ARGS_GET_CMD_OPT_VAL_TYPE(o->val_type)) { - case ARGS_VAL_TYPE_INTEGER: - strncpy(vtype, "INTEGER", sizeof(vtype) - 1); - sprintf(value, "%d", *((int *)o->val)); - break; - - case ARGS_VAL_TYPE_STRING: - strncpy(vtype, "STRING", sizeof(vtype) - 1); - sprintf(value, "%s", (char *)o->val); - break; - - case ARGS_VAL_TYPE_NONE: - default: - strncpy(vtype, "FLAG", sizeof(vtype) - 1); - sprintf(value, "%d", *((int *)o->val)); - break; - } - - if(o->flag) { - strcat(value, " (SET)"); - } - else { - strcat(value, " (DEFAULT)"); - } - - sprintf(result, " -%c(--%s) = %s\n : %s", o->key, o->key_long, - value, o->desc); - - return 0; -} - static int args_parse_int_x_int(char *str, int *num0, int *num1) { char str0_t[64]; int i, cnt0 = 0, cnt1; char *str0, *str1 = NULL; + char *endptr; + long tmp_val; str0 = str; cnt1 = (int)strlen(str); @@ -205,8 +192,19 @@ static int args_parse_int_x_int(char *str, int *num0, int *num1) strncpy(str0_t, str0, cnt0); str0_t[cnt0] = '\0'; - *num0 = atoi(str0_t); - *num1 = atoi(str1); + /* parse first number using strtol with overflow check */ + tmp_val = strtol(str0_t, &endptr, 10); + if(endptr == str0_t || tmp_val < INT_MIN || tmp_val > INT_MAX) { + return -1; /* conversion failed or overflow */ + } + *num0 = (int)tmp_val; + + /* parse second number using strtol with overflow check */ + tmp_val = strtol(str1, &endptr, 10); + if(endptr == str1 || tmp_val < INT_MIN || tmp_val > INT_MAX) { + return -1; /* conversion failed or overflow */ + } + *num1 = (int)tmp_val; return 0; } @@ -314,7 +312,7 @@ static int args_parse_cmd(int argc, const char *argv[], args_opt_t *ops, return -1; } -static int args_set_variable_by_key_long(args_opt_t *opts, char *key_long, void *var) +static int args_set_variable_by_key_long(args_opt_t *opts, char *key_long, void *var, size_t size) { int idx; char buf[ARGS_MAX_KEY_LONG]; @@ -337,6 +335,7 @@ static int args_set_variable_by_key_long(args_opt_t *opts, char *key_long, void if(idx < 0) return -1; opts[idx].val = var; + opts[idx].val_size = (int)size; return 0; } @@ -383,7 +382,12 @@ static int args_set_str(args_parser_t *args, char *keyl, char *str) idx = args_search_long_key(args->opts, keyl); if(idx >= 0) { - sprintf((char *)(args->opts[idx].val), "%s", str); + if(args->opts[idx].val_size > 0) { + snprintf((char *)(args->opts[idx].val), args->opts[idx].val_size, "%s", str); + } + else { + return -1; /* STRING option without a known buffer size: reject */ + } args->opts[idx].flag = 1; return 0; } @@ -434,17 +438,6 @@ static int args_set_flag(args_parser_t *args, char *keyl, int flag) return -1; } -static int args_get_str(args_parser_t *args, char *keyl, char *str, int *flag) -{ - char *p = NULL; - if(args_get(args, keyl, (void **)&p, flag)) - return -1; - if(p) { - if(str) - strcpy(str, p); - } - return 0; -} static int args_get_int(args_parser_t *args, char *keyl, int *val, int *flag) { @@ -516,7 +509,7 @@ static int args_get_help(args_parser_t *args, int idx, char *help) case ARGS_VAL_TYPE_STRING: strncpy(vtype, "STRING", sizeof(vtype) - 1); if(o->val != NULL) - sprintf(default_value, " [%s]", strlen((char *)(o->val)) == 0 ? "None" : (char *)(o->val)); + snprintf(default_value, sizeof(default_value), " [%s]", strlen((char *)(o->val)) == 0 ? "None" : (char *)(o->val)); break; case ARGS_VAL_TYPE_NONE: default: @@ -528,11 +521,11 @@ static int args_get_help(args_parser_t *args, int idx, char *help) optional = !(o->val_type & ARGS_VAL_TYPE_MANDATORY); if(o->key != ARGS_NO_KEY) { - sprintf(help, " -%c, --%s [%s]%s%s\n : %s", o->key, o->key_long, + snprintf(help, ARGS_HELP_BUF_SIZE, " -%c, --%s [%s]%s%s\n : %s", o->key, o->key_long, vtype, (optional) ? " (optional)" : "", (optional) ? default_value : "", o->desc); } else { - sprintf(help, " --%s [%s]%s%s\n : %s", o->key_long, + snprintf(help, ARGS_HELP_BUF_SIZE, " --%s [%s]%s%s\n : %s", o->key_long, vtype, (optional) ? " (optional)" : "", (optional) ? default_value : "", o->desc); } @@ -584,7 +577,6 @@ static args_parser_t *args_create(const args_opt_t *opt_table, int num_opt) args->release = args_release; args->parse = args_parse; args->get_help = args_get_help; - args->get_str = args_get_str; args->get_int = args_get_int; args->set_str = args_set_str; args->set_int = args_set_int; diff --git a/app/oapv_app_dec.c b/app/oapv_app_dec.c index 18c2db4..2a40904 100644 --- a/app/oapv_app_dec.c +++ b/app/oapv_app_dec.c @@ -34,7 +34,8 @@ #include "oapv_app_args.h" #include "oapv_app_y4m.h" -#define MAX_BS_BUF 128 * 1024 * 1024 /* byte */ +#define MAX_BS_BUF 128 * 1024 * 1024 /* byte */ +#define MAX_NUM_METADATA_PLDS 128 /* max number of metadata payloads per access unit */ // check generic frame or not #define IS_NON_AUX_FRM(frm) (((frm)->pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME) || ((frm)->pbu_type == OAPV_PBU_TYPE_NON_PRIMARY_FRAME)) @@ -49,7 +50,7 @@ /* define various command line options as a table */ static const args_opt_t dec_args_opts[] = { { - 'v', "verbose", ARGS_VAL_TYPE_INTEGER, 0, NULL, + 'v', "verbose", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "verbose (log) level\n" " - 0: no message\n" " - 1: only error message\n" @@ -57,43 +58,60 @@ static const args_opt_t dec_args_opts[] = { " - 3: frame-level messages" }, { - 'i', "input", ARGS_VAL_TYPE_STRING | ARGS_VAL_TYPE_MANDATORY, 0, NULL, + 'i', "input", ARGS_VAL_TYPE_STRING | ARGS_VAL_TYPE_MANDATORY, 0, NULL, 0, "file name of input bitstream" }, { - 'o', "output", ARGS_VAL_TYPE_STRING, 0, NULL, + 'o', "output", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "file name of decoded output" }, { - ARGS_NO_KEY, "max-au", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "max-au", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "maximum number of access units to be decoded" }, { - 'm', "threads", ARGS_VAL_TYPE_STRING, 0, NULL, + 'm', "threads", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "force use of a specific number of threads\n" " - 'auto' means that the value is internally determined" }, { - 'd', "output-depth", ARGS_VAL_TYPE_INTEGER, 0, NULL, + 'd', "output-depth", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "output bit depth (8, 10, 12)\n" " Note: This is option does not support 444/4444-16C12 profile." }, { - ARGS_NO_KEY, "hash", ARGS_VAL_TYPE_NONE, 0, NULL, + ARGS_NO_KEY, "hash", ARGS_VAL_TYPE_NONE, 0, NULL, 0, "parse frame hash value for conformance checking in decoding" }, { - ARGS_NO_KEY, "output-csp", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "output-csp", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "output color space (chroma format)\n" " - 0: coded CSP\n" - " - 1: convert to P210 in case of YCbCr422\n" + " - 1: convert to P210 in case of YCbCr422" }, { - ARGS_NO_KEY, "disable-companding", ARGS_VAL_TYPE_NONE, 0, NULL, + ARGS_NO_KEY, "disable-companding", ARGS_VAL_TYPE_NONE, 0, NULL, 0, + "forcely disable companding process\n" + " Note: this option forces to output 12 bits picture in case of 444-16C12\n" + " and 4444-16C12 profile" + }, + { + ARGS_NO_KEY, "api-set", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, + "testing with specific API set (0 or 1)\n" + " Note: API set 1 only supports 1.x or higher library version" + }, + { + ARGS_NO_KEY, "cyclic-tile-decoding", ARGS_VAL_TYPE_NONE, 0, NULL, 0, + "testing using tile-based decoding in cyclic way\n" + " Note: this option is just for testing tile-based decoding method and\n" + " API set 1 option is required to support this" + }, + { + ARGS_NO_KEY, "disable-companding", ARGS_VAL_TYPE_NONE, 0, NULL, 0, "forcely disable companding process\n" " Note: this option forces to output 12 bits picture in case of 444/4444-16C12 profile" }, - {ARGS_END_KEY, "", ARGS_VAL_TYPE_NONE, 0, NULL, ""} /* termination */ + {ARGS_END_KEY, "", ARGS_VAL_TYPE_NONE, 0, NULL, 0, ""} /* termination */ }; // clang-format on @@ -109,6 +127,8 @@ typedef struct args_var { int output_depth; int output_csp; int disable_companding; + int api_set; + int cyclic_tile_decoding; } args_var_t; static args_var_t *args_init_vars(args_parser_t *args) @@ -120,19 +140,21 @@ static args_var_t *args_init_vars(args_parser_t *args) assert_rv(vars != NULL, NULL); memset(vars, 0, sizeof(args_var_t)); - /*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, "max-au", &vars->max_au); - args_set_variable_by_key_long(opts, "hash", &vars->hash); - args_set_variable_by_key_long(opts, "disable-companding", &vars->disable_companding); + /*args_set_variable_by_key_long(opts, "config", args->fname_cfg, 0);*/ + args_set_variable_by_key_long(opts, "input", vars->fname_inp, sizeof(vars->fname_inp)); + args_set_variable_by_key_long(opts, "output", vars->fname_out, sizeof(vars->fname_out)); + args_set_variable_by_key_long(opts, "max-au", &vars->max_au, 0); + args_set_variable_by_key_long(opts, "hash", &vars->hash, 0); + args_set_variable_by_key_long(opts, "disable-companding", &vars->disable_companding, 0); vars->disable_companding = 0; /* default */ - args_set_variable_by_key_long(opts, "verbose", &op_verbose); + args_set_variable_by_key_long(opts, "api-set", &vars->api_set, 0); + args_set_variable_by_key_long(opts, "cyclic-tile-decoding", &vars->cyclic_tile_decoding, 0); + args_set_variable_by_key_long(opts, "verbose", &op_verbose, 0); op_verbose = VERBOSE_SIMPLE; /* default */ - args_set_variable_by_key_long(opts, "threads", vars->threads); + args_set_variable_by_key_long(opts, "threads", vars->threads, sizeof(vars->threads)); strcpy(vars->threads, "auto"); - args_set_variable_by_key_long(opts, "output-depth", &vars->output_depth); - args_set_variable_by_key_long(opts, "output-csp", &vars->output_csp); + args_set_variable_by_key_long(opts, "output-depth", &vars->output_depth, 0); + args_set_variable_by_key_long(opts, "output-csp", &vars->output_csp, 0); vars->output_csp = 0; /* default: coded CSP */ return vars; @@ -141,7 +163,7 @@ static args_var_t *args_init_vars(args_parser_t *args) static void print_usage(const char **argv) { int i; - char str[1024]; + char str[ARGS_HELP_BUF_SIZE]; args_var_t *args_var = NULL; args_parser_t *args; @@ -170,25 +192,61 @@ static void print_usage(const char **argv) free(args_var); } -static int read_au_size(FILE *fp) +/* read 4-byte access unit size. + * return values: + * 1 : success, *au_size holds the parsed size + * 0 : clean end of file (no byte was read before EOF) + * -1 : read error or partial read (1~3 bytes read then EOF/error) + */ +static int read_au_size(FILE *fp, unsigned int *au_size) { unsigned char buf[4]; + size_t n; for(int i = 0; i < 4; i++) { - if(1 != fread(&buf[i], 1, 1, fp)) + n = fread(&buf[i], 1, 1, fp); + if(n != 1) { + if(i == 0 && feof(fp)) { + return 0; /* clean EOF at an access unit boundary */ + } + /* partial read (truncated size field) or read error */ + if(ferror(fp)) { + logerr("ERR: file read error at byte %d of au_size field\n", i); + } else { + logerr("ERR: truncated au_size field (read %d/4 bytes before EOF)\n", i); + } return -1; + } } - return ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3])); + *au_size = ((unsigned int)buf[0] << 24) | ((unsigned int)buf[1] << 16) | + ((unsigned int)buf[2] << 8) | ((unsigned int)buf[3]); + return 1; } static int read_bitstream(FILE *fp, unsigned char *bs_buf, int *bs_buf_size) { - int au_size, read_size = 0; + unsigned int au_size; + int read_size = 0; unsigned char b = 0; + int ret; if(!fseek(fp, 0, SEEK_CUR)) { /* read size first */ - au_size = read_au_size(fp); + ret = read_au_size(fp, &au_size); + if(ret == 0) { + logv2_line(""); + logv2("End of file\n"); + return 0; + } + else if(ret < 0) { + logerr("Cannot read bitstream size!\n"); + return -1; + } if(au_size > 0) { + /* check if bitstream size exceeds MAX_BS_BUF */ + if(au_size > MAX_BS_BUF) { + logerr("ERR: bitstream size (%u bytes) exceeds maximum buffer size (%d bytes)\n", au_size, MAX_BS_BUF); + return -1; + } while(au_size > 0) { /* read byte */ if(1 != fread(&b, 1, 1, fp)) { @@ -202,15 +260,9 @@ static int read_bitstream(FILE *fp, unsigned char *bs_buf, int *bs_buf_size) *bs_buf_size = read_size; } else { - if(feof(fp)) { - logv2_line(""); - logv2("End of file\n"); - return 0; - } - else { - logerr("Cannot read bitstream size!\n") - return -1; - }; + /* size field present but zero: malformed bitstream */ + logerr("Cannot read bitstream size!\n"); + return -1; } } else { @@ -341,8 +393,7 @@ static void print_stat_frm(oapvd_stat_t *stat, oapv_frms_t *frms, oapvm_t mid, a : finfo[i].cs == OAPV_CS_YCBCR4444_10LE ? "4444-10" : finfo[i].cs == OAPV_CS_YCBCR4444_12LE ? "4444-12" : finfo[i].cs == OAPV_CS_YCBCR4444_16LE ? "4444-16" - : "Unknown"; - + : "unknown-cs"; // clang-format on logv2("- FRM %-2d GID %-5d %-11s %9d-bytes %5dx%4d %-10s", @@ -369,97 +420,30 @@ static void print_stat_frm(oapvd_stat_t *stat, oapv_frms_t *frms, oapvm_t mid, a } } -int main(int argc, const char **argv) +int dec_api_set_0(args_var_t *args_var, FILE *fp_bs, int is_y4m) { - args_parser_t *args; - args_var_t *args_var = NULL; - unsigned char *bs_buf = NULL; oapvd_t did = NULL; oapvm_t mid = NULL; oapvd_cdesc_t cdesc; + oapv_au_info_t aui; + oapvd_stat_t stat; + unsigned char *bs_buf = NULL; + oapv_frm_info_t *finfo = NULL; oapv_bitb_t bitb; oapv_frms_t ofrms; oapv_imgb_t *imgb_w = NULL; oapv_imgb_t *imgb_o = NULL; oapv_frm_t *frm = NULL; - oapv_au_info_t aui; - oapvd_stat_t stat; int i, ret = 0; oapv_clk_t clk_beg, clk_end, clk_tot; int au_cnt, frm_cnt[OAPV_MAX_NUM_FRAMES]; int read_size, bs_buf_size = 0; - FILE *fp_bs = NULL; - int is_y4m = 0; - char *errstr = NULL; - oapv_frm_info_t *finfo = NULL; + int prev_frm_w = -1, prev_frm_h = -1; memset(frm_cnt, 0, sizeof(int) * OAPV_MAX_NUM_FRAMES); memset(&aui, 0, sizeof(oapv_au_info_t)); memset(&ofrms, 0, sizeof(oapv_frms_t)); - // print logo - logv2(" ____ ___ ___ _ __\n"); - logv2(" / __ \\___ ___ ___ / _ | / _ \\ | / / Decoder (v%s)\n", oapv_version(NULL)); - logv2("/ /_/ / _ \\/ -_) _ \\/ __ |/ ___/ |/ / \n"); - logv2("\\____/ .__/\\__/_//_/_/ |_/_/ |___/ \n"); - logv2(" /_/ \n"); - logv2("\n"); - - /* help message */ - if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { - print_usage(argv); - return 0; - } - /* parse command line */ - args = args_create(dec_args_opts, NUM_ARGS_OPT); - if(args == NULL) { - logerr("ERR: cannot create argument parser\n"); - ret = -1; - goto ERR; - } - args_var = args_init_vars(args); - if(args_var == NULL) { - logerr("ERR: cannot initialize argument parser\n"); - ret = -1; - goto ERR; - } - if(args->parse(args, argc, argv, &errstr)) { - logerr("ERR: command parsing error (%s)\n", errstr); - ret = -1; - goto ERR; - } - // print command line string for information - print_commandline(argc, argv); - - if(args->check_mandatory(args, &errstr)) { - logerr("ERR: '--%s' argument is mandatory\n", errstr); - ret = -1; - goto ERR; - } - - /* open input file */ - fp_bs = fopen(args_var->fname_inp, "rb"); - if(fp_bs == NULL) { - logerr("ERR: cannot open bitstream file = %s\n", args_var->fname_inp); - print_usage(argv); - ret = -1; goto ERR; - } - /* open output file */ - if(strlen(args_var->fname_out) > 0) { - ret = check_file_name_type(args_var->fname_out); - if(ret > 0) { - is_y4m = 1; - } - else if(ret == 0) { - is_y4m = 0; - } - else { // invalid or unknown file name type - logerr("ERR: unknown file type name for decoded video\n"); - ret = -1; goto ERR; - } - clear_data(args_var->fname_out); /* remove decoded file contents if exists */ - } - // create bitstream buffer bs_buf = malloc(MAX_BS_BUF); if(bs_buf == NULL) { @@ -467,12 +451,20 @@ int main(int argc, const char **argv) ret = -1; goto ERR; } + // create decoder if(!strcmp(args_var->threads, "auto")){ cdesc.threads = OAPV_CDESC_THREADS_AUTO; } else { - cdesc.threads = atoi(args_var->threads); + char *endptr; + long threads_val = strtol(args_var->threads, &endptr, 10); + if(endptr == args_var->threads || threads_val <= 0 || threads_val > INT_MAX) { + logerr("ERR: invalid threads value: %s\n", args_var->threads); + ret = -1; + goto ERR; + } + cdesc.threads = (int)threads_val; } did = oapvd_create(&cdesc, &ret); if(did == NULL) { @@ -516,6 +508,22 @@ int main(int argc, const char **argv) goto ERR; } + /* validate num_frms from bitstream */ + if(aui.num_frms <= 0 || aui.num_frms > OAPV_MAX_NUM_FRAMES) { + logerr("ERR: invalid number of frames (%d), valid range is 1-%d\n", aui.num_frms, OAPV_MAX_NUM_FRAMES); + ret = -1; + goto ERR; + } + + /* check frame resolution change */ + if(au_cnt > 0 && aui.num_frms > 0) { + finfo = &aui.frm_info[0]; + if((prev_frm_w != -1 && prev_frm_w != finfo->w) || (prev_frm_h != -1 && prev_frm_h != finfo->h)) { + logv2("WARNING: frame resolution changed from (%dx%d) to (%dx%d) at AU %d\n", + prev_frm_w, prev_frm_h, finfo->w, finfo->h, au_cnt); + } + } + /* create decoding frame buffers */ ofrms.num_frms = aui.num_frms; for(i = 0; i < ofrms.num_frms; i++) { @@ -541,9 +549,14 @@ int main(int argc, const char **argv) goto ERR; } } + /* update previous frame resolution */ + if(IS_NON_AUX_FRM(finfo)) { + prev_frm_w = finfo->w; + prev_frm_h = finfo->h; + } } - if(args_var->disable_companding && finfo->use_companding == 1) { + if(args_var->disable_companding && finfo->use_companding) { args_var->output_depth = 12; } if(args_var->output_depth == 0) { @@ -584,8 +597,20 @@ int main(int argc, const char **argv) logerr("ERR: failed to read metadata\n"); goto END; } + + /* validate number of metadata payloads */ + if(num_plds < 0 || num_plds > MAX_NUM_METADATA_PLDS) { + logerr("ERR: invalid number of metadata payloads (%d), valid range is 0-%d\n", num_plds, MAX_NUM_METADATA_PLDS); + goto END; + } + if(num_plds > 0) { pld = malloc(sizeof(oapvm_payload_t) * num_plds); + if(pld == NULL) { + logerr("ERR: failed to allocate memory for metadata payloads (requested: %zu bytes)\n", + sizeof(oapvm_payload_t) * num_plds); + goto END; + } ret = oapvm_get_all(mid, pld, &num_plds); if(OAPV_FAILED(ret)) { logerr("ERR: failed to read metadata\n"); @@ -606,6 +631,11 @@ int main(int argc, const char **argv) frm = &ofrms.frm[i]; if(ofrms.num_frms > 0) { if(OAPV_CS_GET_BIT_DEPTH(frm->imgb->cs) != args_var->output_depth && args_var->output_csp != OUTPUT_CSP_P210) { + /* check if imgb_w needs to be reallocated due to resolution change */ + if(imgb_w != NULL && (imgb_w->w[0] != frm->imgb->w[0] || imgb_w->h[0] != frm->imgb->h[0])) { + imgb_w->release(imgb_w); + imgb_w = NULL; + } if(imgb_w == NULL) { imgb_w = imgb_create(frm->imgb->w[0], frm->imgb->h[0], OAPV_CS_SET(OAPV_CS_GET_FORMAT(frm->imgb->cs), args_var->output_depth, 0)); @@ -675,10 +705,447 @@ int main(int argc, const char **argv) } if(imgb_w != NULL) imgb_w->release(imgb_w); + + return 0; +} + +/* read a 4-byte big-endian unsigned integer. + * return values: + * 0 : success, *val holds the parsed value + * -1 : EOF or read error or partial read + */ +static int read_u32(FILE *fp, unsigned int * val) +{ + unsigned char buf[4]; + size_t n; + + for(int i = 0; i < 4; i++) { + n = fread(&buf[i], 1, 1, fp); + if(n != 1) { + if(i == 0 && feof(fp)) { + /* clean EOF at 4-byte boundary - normal termination */ + } else if(ferror(fp)) { + logerr("ERR: file read error at byte %d of u32 field\n", i); + } else { + logerr("ERR: truncated u32 field (read %d/4 bytes before EOF)\n", i); + } + return -1; + } + } + *val = ((unsigned int)buf[0] << 24) | ((unsigned int)buf[1] << 16) | + ((unsigned int)buf[2] << 8) | ((unsigned int)buf[3]); + return 0; +} + +static int read_pbu(FILE *fp, unsigned char *pbu, unsigned int pbu_size) +{ + if(pbu_size != fread(pbu, 1, pbu_size, fp)) { + logerr("Cannot read PDU!\n"); + return -1; + } + return 0; +} + +static const char * get_key_from_val(const oapv_dict_str_int_t * dict, int val) +{ + while(strlen(dict->key) > 0) { + if(dict->val == val){ + return dict->key; + } + dict++; + } + return NULL; +} + +int dec_api_set_1(args_var_t *args_var, FILE *fp_bs, int is_y4m) +{ + oapvd_t did = NULL; + oapvd_cdesc_t cdesc; + oapv_au_info_t aui; + oapvd_stat_t stat; + oapv_bitb_t bitb; + oapv_tile_info_t tinfo_in_frm; + oapv_tile_info_t *ptinfo_in_frm = NULL; + oapv_tile_info_t tinfo_dec; + oapv_tile_info_t *ptinfo_dec = NULL; + + oapv_imgb_t *imgb_dec = NULL; + oapv_imgb_t *imgb_out = NULL; + oapv_imgb_t *imgb_tmp = NULL; + + int ret = 0; + oapv_clk_t clk_beg, clk_end, clk_tot; + int au_cnt = 0; // number of decoded access unit; + int primary_frm_cnt = 0; // number of decoded primary frame + int primary_frm_gid = -1; // group id of primary frame + unsigned char *pbu = NULL; + int prev_frm_w = -1, prev_frm_h = -1; + + // create bitstream buffer + pbu = malloc(MAX_BS_BUF); + if(pbu == NULL) { + logerr("ERR: cannot allocate bitstream buffer, size=%d\n", MAX_BS_BUF); + ret = -1; goto ERR; + } + + if(args_var->cyclic_tile_decoding) { + if(args_var->api_set == 0) { + logerr("ERR: cyclic tile-based decoding cannot be supported in API set 0\n"); + ret = -1; goto ERR; + } + ptinfo_in_frm = &tinfo_in_frm; + ptinfo_dec = &tinfo_dec; + } + else { + ptinfo_in_frm = NULL; + ptinfo_dec = NULL; + } + + // create decoder + if(!strcmp(args_var->threads, "auto")){ + cdesc.threads = OAPV_CDESC_THREADS_AUTO; + } + else { + char *endptr; + long threads_val = strtol(args_var->threads, &endptr, 10); + if(endptr == args_var->threads || threads_val <= 0 || threads_val > INT_MAX) { + logerr("ERR: invalid threads value: %s\n", args_var->threads); + ret = -1; + goto ERR; + } + cdesc.threads = (int)threads_val; + } + did = oapvd_create(&cdesc, &ret); + if(did == NULL) { + logerr("ERR: cannot create OAPV decoder (err=%d)\n", ret); + ret = -1; + goto ERR; + } + if(set_extra_config(did, args_var)) { + logerr("ERR: cannot set extra configurations\n"); + ret = -1; + goto ERR; + } + + clk_tot = 0; + au_cnt = 0; + + /* AU loop ***************************************************************/ + while(args_var->max_au == 0 || (au_cnt < args_var->max_au)) { + /* read access unit size */ + unsigned int au_size; + if(read_u32(fp_bs, &au_size) < 0) { + logv2_line(""); + logv2("End of file\n"); + ret = 0; goto END; + } + /* check signature ('aPv1') */ + unsigned int signature = 0; + if(read_u32(fp_bs, &signature) < 0) { + logerr("ERR: cannot read signature\n"); + ret = -1; goto ERR; + } + if(signature != 0x61507631) { + logerr("ERR: signature mismatch (0x%08X)\n", signature); + ret = -1; goto ERR; + } + au_size -= 4; /* byte size of signature syntax */ + + logv3("AU[%03d] (%d byte)\n", au_cnt, au_size); + + /* PBU loop **********************************************************/ + int rsize = 0; + oapv_pbu_info_t pbu_info; + + while(rsize < au_size) { + /* read a PBU size */ + unsigned int pbu_size; + if(read_u32(fp_bs, &pbu_size) < 0) { + logerr("ERR: cannot read PBU size\n"); + ret = -1; goto ERR; + } + rsize += 4; /* byte size of pbu_size syntax */ + + /* check if PBU size exceeds MAX_BS_BUF */ + if(pbu_size > MAX_BS_BUF) { + logerr("ERR: PBU size (%u bytes) exceeds maximum buffer size (%d bytes)\n", pbu_size, MAX_BS_BUF); + ret = -1; goto ERR; + } + + /* read a PBU */ + if(read_pbu(fp_bs, pbu, pbu_size) < 0) { + ret = -1; goto ERR; + } + /* get PBU information */ + if(OAPV_FAILED(oapvd_info_pbu(pbu, pbu_size, &pbu_info))) { + logerr("ERR: cannot get PBU information\n"); + ret = -1; goto ERR; + } + + const char * pbu_type_str = get_key_from_val(oapv_dict_pbu_type, pbu_info.pbu_type); + if(pbu_type_str == NULL) { + logerr("ERR: unknown PBU type (%d)\n", pbu_info.pbu_type); + ret = -1; goto ERR; + } + logv3(" PBU(%s, gid=%d, %d byte)\n", pbu_type_str, pbu_info.group_id, pbu_size); + + if(pbu_info.pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME) { + // actual decoding is only supported for primary frames in this code + oapv_frm_info_t finfo; + + // check group id + if(primary_frm_gid < 0) primary_frm_gid = pbu_info.group_id; + else if(primary_frm_gid != pbu_info.group_id) { + logv2(" WARNING: group_id of primary frame is changed\n"); + primary_frm_gid = pbu_info.group_id; + } + + // get frame information + ret = oapvd_info_frame(pbu, pbu_size, &finfo, ptinfo_in_frm); + if(OAPV_FAILED(ret)) { + logerr("ERR: failed to get frame information (ret = %d)\n", ret); + ret = -1; goto ERR; + } + + /* check frame resolution change */ + if(primary_frm_cnt > 0) { + if((prev_frm_w != -1 && prev_frm_w != finfo.w) || (prev_frm_h != -1 && prev_frm_h != finfo.h)) { + logv2("WARNING: frame resolution changed from (%dx%d) to (%dx%d) at frame %d\n", + prev_frm_w, prev_frm_h, finfo.w, finfo.h, primary_frm_cnt); + } + } + + // create decoding frame buffers if needs + if(imgb_dec != NULL && (imgb_dec->w[0] != finfo.w || imgb_dec->h[0] != finfo.h)) { + imgb_dec->release(imgb_dec); + imgb_dec = NULL; + } + if(imgb_dec == NULL) { + if(args_var->output_csp == 1) { + imgb_dec = imgb_create(finfo.w, finfo.h, OAPV_CS_SET(OAPV_CF_PLANAR2, 10, 0)); + } + else { + imgb_dec = imgb_create(finfo.w, finfo.h, finfo.cs); + } + if(imgb_dec == NULL) { + logerr("ERR: cannot allocate image buffer (w:%d, h:%d, cs:%d)\n", + finfo.w, finfo.h, finfo.cs); + ret = -1; goto ERR; + } + } + + if(args_var->disable_companding && finfo.use_companding) { + args_var->output_depth = 12; + } + if(args_var->output_depth == 0) { + args_var->output_depth = OAPV_CS_GET_BIT_DEPTH(finfo.cs); + } + + if(args_var->cyclic_tile_decoding) { + ptinfo_dec->num_tiles = 1; // only one tile decoding in cyclic-way + ptinfo_dec->pos_tiles[0].idx = primary_frm_cnt % ptinfo_in_frm->num_tiles; + + // clear image buffer to remove previous frame's decoded tile image + imgb_clear(imgb_dec); + } + + // start to decode a frame + bitb.addr = pbu; + bitb.ssize = pbu_size; + + clk_beg = oapv_clk_get(); + + ret = oapvd_decode_frame(did, &bitb, imgb_dec, &stat, ptinfo_dec); + + clk_end = oapv_clk_from(clk_beg); + clk_tot += clk_end; + + if(OAPV_FAILED(ret)) { + logerr("ERR: failed to decode a frame (ret = %d)\n", ret); + ret = -1; goto ERR; + } + if(stat.read != pbu_size) { + logerr("\t=> different reading of bitstream (in:%d, read:%d)\n", + pbu_size, stat.read); + } + + /* write decoded frames into files */ + if(OAPV_CS_GET_BIT_DEPTH(imgb_dec->cs) != args_var->output_depth && args_var->output_csp != OUTPUT_CSP_P210) { + /* check if imgb_tmp needs to be reallocated due to resolution change */ + if(imgb_tmp != NULL && (imgb_tmp->w[0] != imgb_dec->w[0] || imgb_tmp->h[0] != imgb_dec->h[0])) { + imgb_tmp->release(imgb_tmp); + imgb_tmp = NULL; + } + if(imgb_tmp == NULL) { + imgb_tmp = imgb_create(imgb_dec->w[0], imgb_dec->h[0], + OAPV_CS_SET(OAPV_CS_GET_FORMAT(imgb_dec->cs), args_var->output_depth, 0)); + if(imgb_tmp == NULL) { + logerr("ERR: cannot allocate image buffer (w:%d, h:%d, cs:%d)\n", + imgb_dec->w[0], imgb_dec->h[0], imgb_dec->cs); + ret = -1; goto ERR; + } + } + imgb_cpy(imgb_tmp, imgb_dec); + imgb_out = imgb_tmp; + } + else { + imgb_out = imgb_dec; + } + + if(strlen(args_var->fname_out)) { + if(primary_frm_cnt == 0 && is_y4m) { + if(write_y4m_header(args_var->fname_out, imgb_out)) { + logerr("ERR: cannot write Y4M header\n"); + ret = -1; + goto ERR; + } + } + if(write_dec_img(args_var->fname_out, imgb_out, is_y4m)) { + logerr("ERR: cannot write decoded video\n"); + ret = -1; + goto ERR; + } + } + /* update previous frame resolution */ + prev_frm_w = finfo.w; + prev_frm_h = finfo.h; + primary_frm_cnt++; + fflush(stdout); + fflush(stderr); + } + else if (OAPV_PBU_TYPE_IS_FRAME(pbu_info.pbu_type)) { + // other types of frame PBU + } + else if (pbu_info.pbu_type == OAPV_PBU_TYPE_AU_INFO) { + ret = oapvd_decode_auinfo(did, &bitb, &aui); + if(OAPV_FAILED(ret)) { + logerr("ERR: cannot get PBU information\n"); + ret = -1; goto ERR; + } + } + else if (pbu_info.pbu_type == OAPV_PBU_TYPE_METADATA) { + } + rsize += pbu_size; + } + au_cnt++; + } + +END: + logv2_line("Summary"); + logv2("Processed access units = %d\n", au_cnt); + logv2("Decoded frame count = %d\n", primary_frm_cnt); + if(primary_frm_cnt > 0) { + logv2("Total decoding time = %d msec,", (int)oapv_clk_msec(clk_tot)); + logv2(" %.3f sec\n", (float)(oapv_clk_msec(clk_tot) / 1000.0)); + logv2("Average decoding time for a frame = %d msec\n", (int)oapv_clk_msec(clk_tot) / primary_frm_cnt); + logv2("Average decoding speed = %.3f frames/sec\n", ((float)primary_frm_cnt * 1000) / ((float)oapv_clk_msec(clk_tot))); + } + logv2_line(NULL); + +ERR: + if(did) + oapvd_delete(did); + + if(imgb_dec != NULL) + imgb_dec->release(imgb_dec); + + if(imgb_tmp != NULL) + imgb_tmp->release(imgb_tmp); + + if(pbu != NULL) + free(pbu); + + return 0; +} + +int main(int argc, const char **argv) +{ + args_parser_t *args; + args_var_t *args_var = NULL; + int ret = 0; + int is_y4m = 0; + char *errstr = NULL; + FILE *fp_bs = NULL; + + + // print logo + logv2(" ____ ___ ___ _ __\n"); + logv2(" / __ \\___ ___ ___ / _ | / _ \\ | / / Decoder (v%s)\n", oapv_version(NULL)); + logv2("/ /_/ / _ \\/ -_) _ \\/ __ |/ ___/ |/ / \n"); + logv2("\\____/ .__/\\__/_//_/_/ |_/_/ |___/ \n"); + logv2(" /_/ \n"); + logv2("\n"); + + /* help message */ + if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { + print_usage(argv); + return 0; + } + /* parse command line */ + args = args_create(dec_args_opts, NUM_ARGS_OPT); + if(args == NULL) { + logerr("ERR: cannot create argument parser\n"); + ret = -1; + goto ERR; + } + args_var = args_init_vars(args); + if(args_var == NULL) { + logerr("ERR: cannot initialize argument parser\n"); + ret = -1; + goto ERR; + } + if(args->parse(args, argc, argv, &errstr)) { + logerr("ERR: command parsing error (%s)\n", errstr); + ret = -1; + goto ERR; + } + // print command line string for information + print_commandline(argc, argv); + + if(args->check_mandatory(args, &errstr)) { + logerr("ERR: '--%s' argument is mandatory\n", errstr); + ret = -1; + goto ERR; + } + + /* open input file */ + fp_bs = fopen(args_var->fname_inp, "rb"); + if(fp_bs == NULL) { + logerr("ERR: cannot open bitstream file = %s\n", args_var->fname_inp); + print_usage(argv); + ret = -1; goto ERR; + } + /* open output file */ + if(strlen(args_var->fname_out) > 0) { + ret = check_file_name_type(args_var->fname_out); + if(ret > 0) { + is_y4m = 1; + } + else if(ret == 0) { + is_y4m = 0; + } + else { // invalid or unknown file name type + logerr("ERR: unknown file type name for decoded video\n"); + ret = -1; goto ERR; + } + clear_data(args_var->fname_out); /* remove decoded file contents if exists */ + } + + if(args_var->api_set == 0) { + ret = dec_api_set_0(args_var, fp_bs, is_y4m); + } + else if(args_var->api_set == 1){ + ret = dec_api_set_1(args_var, fp_bs, is_y4m); + } + else { + logerr("ERR: unknown API set number (%d)\n", args_var->api_set); + } + if(ret < 0) goto ERR; + + +ERR: if(fp_bs) fclose(fp_bs); - if(bs_buf) - free(bs_buf); if(args) args->release(args); if(args_var) diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c index 38857e1..9ef77ef 100644 --- a/app/oapv_app_enc.c +++ b/app/oapv_app_enc.c @@ -50,7 +50,7 @@ typedef enum _STATES { /* define various command line options as a table */ static const args_opt_t enc_args_opts[] = { { - 'v', "verbose", ARGS_VAL_TYPE_INTEGER, 0, NULL, + 'v', "verbose", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "verbose (log) level\n" " - 0: no message\n" " - 1: only error message\n" @@ -58,52 +58,52 @@ static const args_opt_t enc_args_opts[] = { " - 3: frame-level messages" }, { - 'i', "input", ARGS_VAL_TYPE_STRING | ARGS_VAL_TYPE_MANDATORY, 0, NULL, + 'i', "input", ARGS_VAL_TYPE_STRING | ARGS_VAL_TYPE_MANDATORY, 0, NULL, 0, "file name of input video" }, { - 'o', "output", ARGS_VAL_TYPE_STRING, 0, NULL, + 'o', "output", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "file name of output bitstream" }, { - 'r', "recon", ARGS_VAL_TYPE_STRING, 0, NULL, + 'r', "recon", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "file name of reconstructed video" }, { - 'w', "width", ARGS_VAL_TYPE_STRING, 0, NULL, + 'w', "width", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "pixel width of input video" }, { - 'h', "height", ARGS_VAL_TYPE_STRING, 0, NULL, + 'h', "height", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "pixel height of input video" }, { - 'q', "qp", ARGS_VAL_TYPE_STRING, 0, NULL, + 'q', "qp", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "QP value: 0 ~ (63 + (bitdepth - 10)*6) \n" " - 10bit input: 0 ~ 63\n" " - 12bit input and 12C16bit: 0 ~ 75\n" " - 'auto' means that the value is internally determined" }, { - 'z', "fps", ARGS_VAL_TYPE_STRING, 0, NULL, + 'z', "fps", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "frame rate (frames per second)" }, { - 'm', "threads", ARGS_VAL_TYPE_STRING, 0, NULL, + 'm', "threads", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "force use of a specific number of threads\n" " - 'auto' means that the value is internally determined" }, { - ARGS_NO_KEY, "preset", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "preset", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "encoder preset [fastest, fast, medium, slow, placebo]" }, { - 'd', "input-depth", ARGS_VAL_TYPE_INTEGER, 0, NULL, + 'd', "input-depth", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "input bit depth (8, 10-12, 16)\n" " - Note: 8bit input will be converted to 10bit" }, { - ARGS_NO_KEY, "input-csp", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "input-csp", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "input color space (chroma format)\n" " - 0: 400\n" " - 2: 422\n" @@ -112,7 +112,7 @@ static const args_opt_t enc_args_opts[] = { " - 5: P2(Planar Y, Combined CbCr, 422)" }, { - ARGS_NO_KEY, "family", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "family", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "family name for bitrate setting\n" " - 422-LQ: YCbCr422 low quality\n" " - 422-SQ: YCbCr422 standard quality\n" @@ -123,7 +123,7 @@ static const args_opt_t enc_args_opts[] = { " color space, if they coexists." }, { - ARGS_NO_KEY, "profile", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "profile", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "profile string\n" " - 422-10: YCbCr422 10bit (default)\n" " - 422-12: YCbCr422 12bit\n" @@ -139,67 +139,67 @@ static const args_opt_t enc_args_opts[] = { " color space, if they coexists." }, { - ARGS_NO_KEY, "level", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "level", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "level setting (1, 1.1, 2, 2.1, 3, 3.1, 4, 4.1, 5, 5.1, 6, 6.1, 7, 7.1)\n" " - 'auto' means that the value is internally determined" }, { - ARGS_NO_KEY, "band", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "band", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "band setting (0, 1, 2, 3)\n" " - 'auto' means that the value is internally determined" }, { - ARGS_NO_KEY, "max-au", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "max-au", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "maximum number of access units to be encoded" }, { - ARGS_NO_KEY, "seek", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "seek", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "number of skipped access units before encoding" }, { - ARGS_NO_KEY, "qp-offset-c1", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "qp-offset-c1", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "QP offset value for Component 1 (Cb)" }, { - ARGS_NO_KEY, "qp-offset-c2", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "qp-offset-c2", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "QP offset value for Component 2 (Cr)" }, { - ARGS_NO_KEY, "qp-offset-c3", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "qp-offset-c3", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "QP offset value for Component 3" }, { - ARGS_NO_KEY, "tile-w", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "tile-w", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "width of tile in units of pixels" }, { - ARGS_NO_KEY, "tile-h", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "tile-h", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "height of tile in units of pixels" }, { - ARGS_NO_KEY, "bitrate", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "bitrate", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "enable ABR rate control\n" " bitrate in terms of kbits per second: Kbps(none,K,k), Mbps(M,m)\n" " ex) 100 = 100K = 0.1M" }, { - ARGS_NO_KEY, "q-matrix-c0", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "q-matrix-c0", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "custom quantization matrix for component 0 (Y) \"q1 q2 ... q63 q64\"" }, { - ARGS_NO_KEY, "q-matrix-c1", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "q-matrix-c1", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "custom quantization matrix for component 1 (Cb) \"q1 q2 ... q63 q64\"" }, { - ARGS_NO_KEY, "q-matrix-c2", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "q-matrix-c2", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "custom quantization matrix for component 2 (Cr) \"q1 q2 ... q63 q64\"" }, { - ARGS_NO_KEY, "q-matrix-c3", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "q-matrix-c3", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "custom quantization matrix for component 3 \"q1 q2 ... q63 q64\"" }, { - ARGS_NO_KEY, "color-primaries", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "color-primaries", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "ColourPrimaries value defined in ITU-T H.273\n" " - 1: bt709\n" " - 2: unspecified\n" @@ -218,7 +218,7 @@ static const args_opt_t enc_args_opts[] = { " and 'color-range' should all be set." }, { - ARGS_NO_KEY, "color-transfer", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "color-transfer", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "TransferCharacteristics value defined in ITU-T H.273\n" " - 1: bt709\n" " - 2: unspecified\n" @@ -243,7 +243,7 @@ static const args_opt_t enc_args_opts[] = { }, { - ARGS_NO_KEY, "color-matrix", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "color-matrix", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "MatrixCoefficients value defined in ITU-T H.273\n" " - 0: gbr\n" " - 1: bt709\n" @@ -264,7 +264,7 @@ static const args_opt_t enc_args_opts[] = { " and 'color-range' should all be set." }, { - ARGS_NO_KEY, "color-range", ARGS_VAL_TYPE_INTEGER, 0, NULL, + ARGS_NO_KEY, "color-range", ARGS_VAL_TYPE_INTEGER, 0, NULL, 0, "Color range\n" " - 0: limited color range ('tv' color range) \n" " - 1: full color range ('pc' color range)\n" @@ -273,18 +273,18 @@ static const args_opt_t enc_args_opts[] = { " and 'color-range' should all be set." }, { - ARGS_NO_KEY, "hash", ARGS_VAL_TYPE_NONE, 0, NULL, + ARGS_NO_KEY, "hash", ARGS_VAL_TYPE_NONE, 0, NULL, 0, "embed frame hash value for conformance checking in decoding" }, { - ARGS_NO_KEY, "master-display", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "master-display", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "mastering display color volume metadata" }, { - ARGS_NO_KEY, "max-cll", ARGS_VAL_TYPE_STRING, 0, NULL, + ARGS_NO_KEY, "max-cll", ARGS_VAL_TYPE_STRING, 0, NULL, 0, "content light level information metadata" }, - {ARGS_END_KEY, "", ARGS_VAL_TYPE_NONE, 0, NULL, ""} /* termination */ + {ARGS_END_KEY, "", ARGS_VAL_TYPE_NONE, 0, NULL, 0, ""} /* termination */ }; // clang-format on @@ -350,63 +350,63 @@ static args_var_t *args_init_vars(args_parser_t *args, oapve_param_t *param) 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); + /*args_set_variable_by_key_long(opts, "config", args->fname_cfg, 0);*/ + args_set_variable_by_key_long(opts, "input", vars->fname_inp, sizeof(vars->fname_inp)); + args_set_variable_by_key_long(opts, "output", vars->fname_out, sizeof(vars->fname_out)); + args_set_variable_by_key_long(opts, "recon", vars->fname_rec, sizeof(vars->fname_rec)); + args_set_variable_by_key_long(opts, "max-au", &vars->max_au, 0); + args_set_variable_by_key_long(opts, "hash", &vars->hash, 0); + args_set_variable_by_key_long(opts, "verbose", &op_verbose, 0); op_verbose = VERBOSE_SIMPLE; /* default */ - args_set_variable_by_key_long(opts, "input-depth", &vars->input_depth); + args_set_variable_by_key_long(opts, "input-depth", &vars->input_depth, 0); vars->input_depth = 10; /* default */ - args_set_variable_by_key_long(opts, "input-csp", &vars->input_csp); + args_set_variable_by_key_long(opts, "input-csp", &vars->input_csp, 0); vars->input_csp = -1; - args_set_variable_by_key_long(opts, "seek", &vars->seek); - args_set_variable_by_key_long(opts, "profile", vars->profile); + args_set_variable_by_key_long(opts, "seek", &vars->seek, 0); + args_set_variable_by_key_long(opts, "profile", vars->profile, sizeof(vars->profile)); strcpy(vars->profile, "422-10"); - args_set_variable_by_key_long(opts, "level", vars->level); + args_set_variable_by_key_long(opts, "level", vars->level, sizeof(vars->level)); strcpy(vars->level, "auto"); /* default */ - args_set_variable_by_key_long(opts, "band", vars->band); + args_set_variable_by_key_long(opts, "band", vars->band, sizeof(vars->band)); strcpy(vars->band, "auto"); /* default */ - args_set_variable_by_key_long(opts, "width", vars->width); - args_set_variable_by_key_long(opts, "height", vars->height); - args_set_variable_by_key_long(opts, "fps", vars->fps); + args_set_variable_by_key_long(opts, "width", vars->width, sizeof(vars->width)); + args_set_variable_by_key_long(opts, "height", vars->height, sizeof(vars->height)); + args_set_variable_by_key_long(opts, "fps", vars->fps, sizeof(vars->fps)); - args_set_variable_by_key_long(opts, "qp", vars->qp); + args_set_variable_by_key_long(opts, "qp", vars->qp, sizeof(vars->qp)); strcpy(vars->qp, "auto"); /* default */ - args_set_variable_by_key_long(opts, "qp_offset_c1", vars->qp_offset_c1); - args_set_variable_by_key_long(opts, "qp_offset_c2", vars->qp_offset_c2); - args_set_variable_by_key_long(opts, "qp_offset_c3", vars->qp_offset_c3); + args_set_variable_by_key_long(opts, "qp_offset_c1", vars->qp_offset_c1, sizeof(vars->qp_offset_c1)); + args_set_variable_by_key_long(opts, "qp_offset_c2", vars->qp_offset_c2, sizeof(vars->qp_offset_c2)); + args_set_variable_by_key_long(opts, "qp_offset_c3", vars->qp_offset_c3, sizeof(vars->qp_offset_c3)); - args_set_variable_by_key_long(opts, "family", vars->family); - args_set_variable_by_key_long(opts, "bitrate", vars->bitrate); + args_set_variable_by_key_long(opts, "family", vars->family, sizeof(vars->family)); + args_set_variable_by_key_long(opts, "bitrate", vars->bitrate, sizeof(vars->bitrate)); - args_set_variable_by_key_long(opts, "q-matrix-c0", vars->q_matrix_c0); - args_set_variable_by_key_long(opts, "q-matrix-c1", vars->q_matrix_c1); - args_set_variable_by_key_long(opts, "q-matrix-c2", vars->q_matrix_c2); - args_set_variable_by_key_long(opts, "q-matrix-c3", vars->q_matrix_c3); + args_set_variable_by_key_long(opts, "q-matrix-c0", vars->q_matrix_c0, sizeof(vars->q_matrix_c0)); + args_set_variable_by_key_long(opts, "q-matrix-c1", vars->q_matrix_c1, sizeof(vars->q_matrix_c1)); + args_set_variable_by_key_long(opts, "q-matrix-c2", vars->q_matrix_c2, sizeof(vars->q_matrix_c2)); + args_set_variable_by_key_long(opts, "q-matrix-c3", vars->q_matrix_c3, sizeof(vars->q_matrix_c3)); - args_set_variable_by_key_long(opts, "threads", vars->threads); + args_set_variable_by_key_long(opts, "threads", vars->threads, sizeof(vars->threads)); strcpy(vars->threads, "auto"); - args_set_variable_by_key_long(opts, "tile-w", vars->tile_w); - args_set_variable_by_key_long(opts, "tile-h", vars->tile_h); + args_set_variable_by_key_long(opts, "tile-w", vars->tile_w, sizeof(vars->tile_w)); + args_set_variable_by_key_long(opts, "tile-h", vars->tile_h, sizeof(vars->tile_h)); - args_set_variable_by_key_long(opts, "preset", vars->preset); + args_set_variable_by_key_long(opts, "preset", vars->preset, sizeof(vars->preset)); - args_set_variable_by_key_long(opts, "color-primaries", &vars->color_primaries); + args_set_variable_by_key_long(opts, "color-primaries", &vars->color_primaries, 0); vars->color_primaries = -1; /* unset */ - args_set_variable_by_key_long(opts, "color-transfer", &vars->color_transfer); + args_set_variable_by_key_long(opts, "color-transfer", &vars->color_transfer, 0); vars->color_transfer = -1; /* unset */ - args_set_variable_by_key_long(opts, "color-matrix", &vars->color_matrix); + args_set_variable_by_key_long(opts, "color-matrix", &vars->color_matrix, 0); vars->color_matrix = -1; /* unset */ - args_set_variable_by_key_long(opts, "color-range", &vars->color_range); + args_set_variable_by_key_long(opts, "color-range", &vars->color_range, 0); vars->color_range = -1; /* unset */ - args_set_variable_by_key_long(opts, "master-display", vars->master_display); - args_set_variable_by_key_long(opts, "max-cll", vars->max_cll); + args_set_variable_by_key_long(opts, "master-display", vars->master_display, sizeof(vars->master_display)); + args_set_variable_by_key_long(opts, "max-cll", vars->max_cll, sizeof(vars->max_cll)); return vars; } @@ -414,7 +414,7 @@ static args_var_t *args_init_vars(args_parser_t *args, oapve_param_t *param) static void print_usage(const char **argv) { int i; - char str[1024]; + char str[ARGS_HELP_BUF_SIZE]; args_parser_t *args; args_var_t *args_var = NULL; oapve_param_t default_param; @@ -1017,7 +1017,14 @@ int main(int argc, const char **argv) cdesc.threads = OAPV_CDESC_THREADS_AUTO; } else { - cdesc.threads = atoi(args_var->threads); + char *endptr; + long threads_val = strtol(args_var->threads, &endptr, 10); + if(endptr == args_var->threads || threads_val <= 0 || threads_val > INT_MAX) { + logerr("ERR: invalid threads value: %s\n", args_var->threads); + ret = -1; + goto ERR; + } + cdesc.threads = (int)threads_val; } if(check_conf(&cdesc, args_var)) { diff --git a/app/oapv_app_util.h b/app/oapv_app_util.h index c25e9e8..9e19057 100644 --- a/app/oapv_app_util.h +++ b/app/oapv_app_util.h @@ -54,15 +54,19 @@ #define VERBOSE_SIMPLE 2 #define VERBOSE_FRAME 3 +/* max image resolution supported by imgb (32K: 2x of 8192x4320) */ +#define IMGB_MAX_W 16384 +#define IMGB_MAX_H 8640 + /* logging functions */ static void log_msg(char *filename, int line, const char *fmt, ...) { char str[1024] = { '\0' }; if(filename != NULL && line >= 0) - sprintf(str, "[%s:%d] ", filename, line); + snprintf(str, sizeof(str), "[%s:%d] ", filename, line); va_list args; va_start(args, fmt); - vsprintf(str + strlen(str), fmt, args); + vsnprintf(str + strlen(str), sizeof(str) - strlen(str), fmt, args); va_end(args); printf("%s", str); } @@ -79,7 +83,7 @@ static void log_line(char *pre) len = (pre == NULL) ? 0 : (int)strlen(pre); if(len > 0) { - sprintf(str + 3, " %s ", pre); + snprintf(str + 3, sizeof(str) - 3, " %s ", pre); len = (int)strlen(str); } @@ -316,6 +320,12 @@ oapv_imgb_t *imgb_create(int w, int h, int cs) bd = OAPV_CS_GET_BYTE_DEPTH(cs); /* byte unit */ + /* reject invalid or out-of-range resolution */ + if(w <= 0 || h <= 0 || w > IMGB_MAX_W || h > IMGB_MAX_H || bd <= 0) { + logerr("invalid image parameter (w=%d, h=%d, byte-depth=%d)\n", w, h, bd); + goto ERR; + } + imgb->w[0] = w; imgb->h[0] = h; switch(OAPV_CS_GET_FORMAT(cs)) { @@ -675,6 +685,14 @@ static void imgb_cpy(oapv_imgb_t *dst, oapv_imgb_t *src) } } +void imgb_clear(oapv_imgb_t *imgb) +{ + int i; + for(i = 0; i < imgb->np; i++) { + memset(imgb->a[i], 0, imgb->bsize[i]); + } +} + static void measure_psnr(oapv_imgb_t *org, oapv_imgb_t *rec, double psnr[4], int bit_depth) { double sum[4], mse[4]; diff --git a/inc/oapv.h b/inc/oapv.h index 56bddee..5388a02 100644 --- a/inc/oapv.h +++ b/inc/oapv.h @@ -55,8 +55,8 @@ extern "C" { #define OAPV_VER_GET_PATCH(v) (((v) >> 0) & 0xFF) /* version numbers (should be changed in case of new release) */ -#define OAPV_VER_APISET (0) -#define OAPV_VER_MAJOR (3) +#define OAPV_VER_APISET (1) +#define OAPV_VER_MAJOR (0) #define OAPV_VER_MINOR (0) #define OAPV_VER_PATCH (0) @@ -222,6 +222,9 @@ extern "C" { #define OAPV_PBU_NUMS (10) #define OAPV_PBU_FRAME_TYPE_NUM (5) +#define OAPV_PBU_TYPE_IS_FRAME(pbu_type) \ + ((pbu_type)==1 || (pbu_type)==2 || ((pbu_type)>=25 && (pbu_type)<=27)) + /***************************************************************************** * metadata types @@ -442,8 +445,23 @@ struct oapv_au_info { oapv_frm_info_t frm_info[OAPV_MAX_NUM_FRAMES]; }; +typedef struct oapv_tile_pos oapv_tile_pos_t; +struct oapv_tile_pos { + int idx; /* tile index in raster scan order */ + int x_mb; /* x-position in MB unit */ + int y_mb; /* y-position in MB unit */ + int w_mb; /* width in MB unit */ + int h_mb; /* height in MB unit */ +}; + +typedef struct oapv_tile_info oapv_tile_info_t; +struct oapv_tile_info { + int num_tiles; + oapv_tile_pos_t pos_tiles[OAPV_MAX_TILES]; +}; + /***************************************************************************** - * constant string values for oapve_param_parse() and command-line options + * constant string and value pairs *****************************************************************************/ typedef struct oapv_dict_str_int oapv_dict_str_int_t; // dictionary type struct oapv_dict_str_int { @@ -451,15 +469,27 @@ struct oapv_dict_str_int { const int val; }; +static const oapv_dict_str_int_t oapv_dict_pbu_type[] = { + {"primary frame", OAPV_PBU_TYPE_PRIMARY_FRAME}, + {"non-primary frame", OAPV_PBU_TYPE_NON_PRIMARY_FRAME}, + {"preview frame", OAPV_PBU_TYPE_PREVIEW_FRAME}, + {"depth frame", OAPV_PBU_TYPE_DEPTH_FRAME}, + {"alpha frame", OAPV_PBU_TYPE_ALPHA_FRAME}, + {"access unit information", OAPV_PBU_TYPE_AU_INFO}, + {"metadata", OAPV_PBU_TYPE_METADATA}, + {"filler", OAPV_PBU_TYPE_FILLER}, + {"", 0} // termination +}; + static const oapv_dict_str_int_t oapv_param_opts_profile[] = { - {"422-10", OAPV_PROFILE_422_10}, - {"422-12", OAPV_PROFILE_422_12}, - {"444-10", OAPV_PROFILE_444_10}, - {"444-12", OAPV_PROFILE_444_12}, - {"4444-10", OAPV_PROFILE_4444_10}, - {"4444-12", OAPV_PROFILE_4444_12}, - {"400-10", OAPV_PROFILE_400_10}, - {"4444-16C12", OAPV_PROFILE_4444_16C12}, + {"422-10", OAPV_PROFILE_422_10}, + {"422-12", OAPV_PROFILE_422_12}, + {"444-10", OAPV_PROFILE_444_10}, + {"444-12", OAPV_PROFILE_444_12}, + {"4444-10", OAPV_PROFILE_4444_10}, + {"4444-12", OAPV_PROFILE_4444_12}, + {"400-10", OAPV_PROFILE_400_10}, + {"4444-16C12", OAPV_PROFILE_4444_16C12}, {"", 0} // termination }; @@ -752,6 +782,26 @@ OAPV_EXPORT int oapvd_info(void *au, int au_size, oapv_au_info_t *aui); *****************************************************************************/ OAPV_EXPORT const char *oapv_version(unsigned int *ver_num); +/***************************************************************************** + * OpenAPV version 2 APIs + ****************************************************************************/ +/* PDU information */ +typedef struct oapv_pbu_info oapv_pbu_info_t; +struct oapv_pbu_info { + int pbu_type; + int group_id; +}; + +OAPV_EXPORT int oapv2d_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid, oapvd_stat_t *stat); + +OAPV_EXPORT int oapvd_info_pbu(void *pbu, int pbu_size, oapv_pbu_info_t *pbu_info); +OAPV_EXPORT int oapvd_info_frame(void *pbu, int pbu_size, oapv_frm_info_t *frm_info, oapv_tile_info_t *tile_info); + +OAPV_EXPORT int oapvd_decode_auinfo(oapvd_t did, oapv_bitb_t *bitb, oapv_au_info_t *aui); +OAPV_EXPORT int oapvd_decode_frame(oapvd_t did, oapv_bitb_t *bitb, oapv_imgb_t *imgb, oapvd_stat_t *stat, oapv_tile_info_t * part); + + + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/oapv.c b/src/oapv.c index ba6d09b..b15ca9e 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1405,12 +1405,40 @@ static int dec_set_tile_info(oapvd_tile_t* tile, int w_pel, int h_pel, int tile_ return OAPV_OK; } -static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_imgb_t *imgb) +static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_tile_info_t * part, oapv_imgb_t *imgb) { - if (imgb->w[0] < ctx->fh.fi.frame_width || imgb->h[0] < ctx->fh.fi.frame_height) { + int i; + + // the input image buffer must match the frame format signaled in the + // bitstream; a mismatch (e.g. caused by a resolution change without + // reallocation) is rejected as an invalid argument + if (imgb->w[0] != ctx->fh.fi.frame_width || imgb->h[0] != ctx->fh.fi.frame_height) { + return OAPV_ERR_INVALID_ARGUMENT; + } + // the color space of the input buffer must correspond to the bitstream's + // chroma format (note: OAPV_CF_PLANAR2 maps to chroma_format_idc 2 so the + // YCbCr422 -> P210 output path is accepted) + if (color_format_to_chroma_format_idc(OAPV_CS_GET_FORMAT(imgb->cs)) != ctx->fh.fi.chroma_format_idc) { return OAPV_ERR_INVALID_ARGUMENT; } + // validate buffer capacity for each component + // calculate required buffer size based on frame header information + int aligned_w = oapv_align_value(ctx->fh.fi.frame_width, OAPV_MB_W); + int aligned_h = oapv_align_value(ctx->fh.fi.frame_height, OAPV_MB_H); + int byte_depth = (ctx->fh.fi.bit_depth + 7) / 8; // bytes per pixel + + for(int c = 0; c < imgb->np; c++) { + int comp_w = aligned_w >> (c > 0 ? get_chroma_sft_w(ctx->fh.fi.chroma_format_idc) : 0); + int comp_h = aligned_h >> (c > 0 ? get_chroma_sft_h(ctx->fh.fi.chroma_format_idc) : 0); + int required_stride = comp_w * byte_depth; + int required_bsize = required_stride * comp_h; + + if(imgb->bsize[c] < required_bsize) { + return OAPV_ERR_INVALID_ARGUMENT; + } + } + ctx->imgb = imgb; imgb_addref(ctx->imgb); // increase reference count @@ -1432,8 +1460,6 @@ static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_imgb_t *imgb) ctx->disable_companding = 0; } - - if(OAPV_CS_GET_FORMAT(imgb->cs) == OAPV_CF_PLANAR2) { ctx->fn_blk_to_pic[Y_C] = oapv_blk_to_pic_p21x_y; ctx->fn_blk_to_pic[U_C] = oapv_blk_to_pic_p21x_uv; @@ -1442,18 +1468,18 @@ static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_imgb_t *imgb) else { if(ctx->fh.fi.profile_idc == OAPV_PROFILE_444_16C12 || ctx->fh.fi.profile_idc == OAPV_PROFILE_4444_16C12) { if(ctx->disable_companding){ - for(int i = 0; i < ctx->num_c; i++) { + for(i = 0; i < ctx->num_c; i++) { ctx->fn_blk_to_pic[i] = oapv_blk_to_pic_16; } } else{ - for(int i = 0; i < ctx->num_c; i++) { + for(i = 0; i < ctx->num_c; i++) { ctx->fn_blk_to_pic[i] = oapv_blk_to_pic_12E16; } } } else{ - for(int i = 0; i < ctx->num_c; i++) { + for(i = 0; i < ctx->num_c; i++) { ctx->fn_blk_to_pic[i] = oapv_blk_to_pic_16; } } @@ -1469,13 +1495,23 @@ static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_imgb_t *imgb) oapv_assert_rv((ctx->num_tile_cols <= OAPV_MAX_TILE_COLS) && (ctx->num_tile_rows <= OAPV_MAX_TILE_ROWS), OAPV_ERR_MALFORMED_BITSTREAM); dec_set_tile_info(ctx->tile, ctx->w, ctx->h, tile_w, tile_h, ctx->num_tile_cols, ctx->num_tiles); - for(int i = 0; i < ctx->num_tiles; i++) { + for(i = 0; i < ctx->num_tiles; i++) { ctx->tile[i].bs_beg = NULL; } ctx->tile[0].bs_beg = oapv_bsr_sink(&ctx->bs); - for(int i = 0; i < ctx->num_tiles; i++) { - ctx->tile[i].stat = DEC_TILE_STAT_NOT_DECODED; + if(part != NULL) { + for(i = 0; i < ctx->num_tiles; i++) { + ctx->tile[i].stat = DEC_TILE_STAT_DO(DEC_TILE_STAT_SKIP); /* bypass decoding */ + } + for(i = 0; i < part->num_tiles; i++) { + ctx->tile[part->pos_tiles[i].idx].stat = DEC_TILE_STAT_DO(DEC_TILE_STAT_DECODE); + } + } + else { + for(i = 0; i < ctx->num_tiles; i++) { + ctx->tile[i].stat = DEC_TILE_STAT_DO(DEC_TILE_STAT_DECODE); + } } return OAPV_OK; @@ -1607,15 +1643,15 @@ static int dec_thread_tile(void *arg) // find not decoded tile oapv_tpool_enter_cs(ctx->sync_obj); for(i = 0; i < ctx->num_tiles; i++) { - if(tile[i].stat == DEC_TILE_STAT_NOT_DECODED) { - tile[i].stat = DEC_TILE_STAT_ON_DECODING; + if(DEC_TILE_STAT_IS_DO(tile[i].stat)) { + tile[i].stat = DEC_TILE_STAT_ON(tile[i].stat); tidx = i; break; } } oapv_tpool_leave_cs(ctx->sync_obj); if(i == ctx->num_tiles) { - break; + break; // end of worker thread } // wait until to know bistream start position @@ -1644,24 +1680,25 @@ static int dec_thread_tile(void *arg) } oapv_tpool_leave_cs(ctx->sync_obj); - ret = dec_tile(core, &tile[tidx]); + if(DEC_TILE_STAT_IS_DECODE(tile[i].stat)) { + ret = dec_tile(core, &tile[tidx]); + } oapv_tpool_enter_cs(ctx->sync_obj); if (OAPV_SUCCEEDED(ret)) { - tile[tidx].stat = DEC_TILE_STAT_DECODED; + tile[tidx].stat = DEC_TILE_STAT_DONE(tile[tidx].stat); } else { - tile[tidx].stat = ret; + tile[tidx].stat = DEC_TILE_STAT_ERR(tile[tidx].stat); thread_ret = ret; } - tile[tidx].stat = OAPV_SUCCEEDED(ret) ? DEC_TILE_STAT_DECODED : ret; oapv_tpool_leave_cs(ctx->sync_obj); } return thread_ret; ERR: oapv_tpool_enter_cs(ctx->sync_obj); - tile[tidx].stat = DEC_TILE_STAT_SIZE_ERROR; + tile[tidx].stat = DEC_TILE_STAT_ERR(tile[tidx].stat); if (tidx + 1 < ctx->num_tiles) { tile[tidx + 1].bs_beg = tile[tidx].bs_beg; @@ -1870,7 +1907,7 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid ret = oapvd_vlc_frame_header(bs, &ctx->fh); oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); - ret = dec_frm_prepare(ctx, ofrms->frm[nfrms].imgb); + ret = dec_frm_prepare(ctx, NULL, ofrms->frm[nfrms].imgb); oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); int thread_ret; @@ -1914,7 +1951,7 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid ofrms->frm[nfrms].pbu_type = pbuh.pbu_type; ofrms->frm[nfrms].group_id = pbuh.group_id; - stat->frm_size[nfrms] = pbu_size + 4 /* byte size of 'pbu_size' syntax */; + stat->frm_size[nfrms] = pbu_size + 4; /* byte size of 'pbu_size' syntax */ nfrms++; // go to the end of frame data for next PDU @@ -2004,11 +2041,7 @@ int oapvd_info(void *au, int au_size, oapv_au_info_t *aui) } return OAPV_OK; // founded access_unit_info, no need to read more PBUs } - if(pbuh.pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME || - pbuh.pbu_type == OAPV_PBU_TYPE_NON_PRIMARY_FRAME || - pbuh.pbu_type == OAPV_PBU_TYPE_PREVIEW_FRAME || - pbuh.pbu_type == OAPV_PBU_TYPE_DEPTH_FRAME || - pbuh.pbu_type == OAPV_PBU_TYPE_ALPHA_FRAME) { + if(OAPV_PBU_TYPE_IS_FRAME(pbuh.pbu_type)) { // parse frame_info in PBU oapv_fi_t fi; @@ -2026,6 +2059,201 @@ int oapvd_info(void *au, int au_size, oapv_au_info_t *aui) return OAPV_OK; } +int oapvd_info_pbu(void *pbu, int pbu_size, oapv_pbu_info_t *pbu_info) +{ + oapv_bs_t bs; + oapv_assert_rv(pbu_size >= 4, OAPV_ERR_INVALID_ARGUMENT); + + oapv_bsr_init(&bs, pbu, pbu_size, NULL); + + /* parse pbu_header() */ + pbu_info->pbu_type = oapv_bsr_read(&bs, 8); + pbu_info->group_id = oapv_bsr_read(&bs, 16); + + return OAPV_OK; +} + +int oapvd_info_frame(void *pbu, int pbu_size, oapv_frm_info_t *frm_info, oapv_tile_info_t *tile_info) +{ + oapv_bs_t bs; + oapv_pbuh_t pbuh; + oapv_fh_t fh; + int i, j, ret = OAPV_OK; + + oapv_assert_rv(pbu_size >= (OAPV_PBU_HEADER_BYTE + OAPV_FRAME_INFO_BYTE), OAPV_ERR_INVALID_ARGUMENT); + oapv_bsr_init(&bs, pbu, pbu_size, NULL); + + DUMP_SET(0); + // decode PBU header + ret = oapvd_vlc_pbu_header(&bs, &pbuh); + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + + // check frame type PBU + oapv_assert_gv(OAPV_PBU_TYPE_IS_FRAME(pbuh.pbu_type), ret, OAPV_ERR_INVALID_ARGUMENT, ERR); + + // decode frame header + ret = oapvd_vlc_frame_header(&bs, &fh); + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + + fh_to_finfo(&fh, pbuh.pbu_type, pbuh.group_id, frm_info); + + if(tile_info != NULL) { + int pic_w, pic_h, tile_w, tile_h, tile_cols, tile_rows; + + oapv_mset(tile_info, 0, sizeof(oapv_tile_info_t)); // clear + + tile_w = fh.tile_width_in_mbs * OAPV_MB_W; + tile_h = fh.tile_height_in_mbs * OAPV_MB_H; + + pic_w = ((fh.fi.frame_width + (OAPV_MB_W - 1)) >> OAPV_LOG2_MB_W) << OAPV_LOG2_MB_W; + pic_h = ((fh.fi.frame_height + (OAPV_MB_H - 1)) >> OAPV_LOG2_MB_H) << OAPV_LOG2_MB_H; + + tile_cols = (pic_w + (tile_w - 1)) / tile_w; + tile_rows = (pic_h + (tile_h - 1)) / tile_h; + + oapv_tile_pos_t * tpos = tile_info->pos_tiles; + + for(i = 0; i < tile_rows; i++) { + for(j = 0; j < tile_cols; j++) { + tpos->x_mb = fh.tile_width_in_mbs * j; + tpos->y_mb = fh.tile_height_in_mbs * i; + + if(tpos->x_mb + fh.tile_width_in_mbs > (pic_w >> OAPV_LOG2_MB_W)) { + tpos->w_mb = (pic_w >> OAPV_LOG2_MB_W) - tpos->x_mb; + } + else { + tpos->w_mb = fh.tile_width_in_mbs; + } + if(tpos->h_mb + fh.tile_height_in_mbs > (pic_h >> OAPV_LOG2_MB_H)) { + tpos->h_mb = (pic_h >> OAPV_LOG2_MB_H) - tpos->y_mb; + } + else { + tpos->h_mb = fh.tile_height_in_mbs; + } + tpos->idx = i * tile_cols + j; + tpos++; + } + } + tile_info->num_tiles = tile_cols * tile_rows; + } + +ERR: + DUMP_SET(1); + return ret; +} + +int oapvd_decode_frame(oapvd_t did, oapv_bitb_t *bitb, oapv_imgb_t *imgb, oapvd_stat_t *stat, oapv_tile_info_t * part) +{ + oapvd_ctx_t *ctx; + oapv_pbuh_t pbuh; + int ret = OAPV_OK; + + ctx = dec_id_to_ctx(did); + oapv_assert_rv(ctx, OAPV_ERR_INVALID_ARGUMENT); + oapv_mset(stat, 0, sizeof(oapvd_stat_t)); + + oapv_bs_t *bs; + oapv_assert_gv((bitb->ssize >= 8), ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); + oapv_bsr_init(&ctx->bs, (u8 *)bitb->addr, bitb->ssize, NULL); + bs = &ctx->bs; + + // parse PBU header + ret = oapvd_vlc_pbu_header(bs, &pbuh); + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + // check frame type PBU + oapv_assert_gv(OAPV_PBU_TYPE_IS_FRAME(pbuh.pbu_type), ret, OAPV_ERR_INVALID_ARGUMENT, ERR); + + // parse frame header + ret = oapvd_vlc_frame_header(bs, &ctx->fh); + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + + // be ready to decode start + ret = dec_frm_prepare(ctx, part, imgb); + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + + int thread_ret; + oapv_tpool_t *tpool = ctx->tpool; + int parallel_task = 1; + int tidx = 0; + + if(part != NULL) { + oapv_assert_gv(part->num_tiles > 0 && part->num_tiles <= ctx->num_tiles, ret, OAPV_ERR_INVALID_ARGUMENT, ERR); + parallel_task = (ctx->threads > part->num_tiles) ? part->num_tiles : ctx->threads; + } + else { + parallel_task = (ctx->threads > ctx->num_tiles) ? ctx->num_tiles : ctx->threads; + } + + /* decode tiles ************************************/ + for(tidx = 0; tidx < (parallel_task - 1); tidx++) { + tpool->run(ctx->thread_id[tidx], dec_thread_tile, + (void *)ctx->core[tidx]); + } + ret = dec_thread_tile((void *)ctx->core[tidx]); + for(tidx = 0; tidx < parallel_task - 1; tidx++) { + tpool->join(ctx->thread_id[tidx], &thread_ret); + if(OAPV_FAILED(thread_ret)) { + ret = thread_ret; + } + } + /****************************************************/ + + /* READ FILLER HERE !!! */ + if(OAPV_SUCCEEDED(ret) && ctx->use_frm_hash) { + oapv_imgb_set_md5(imgb); + } + else { + oapv_imgb_clr_md5(imgb); + } + // following function should be called even error cases, + // because input imgb's ref count needs to decreased. + // after this function, ctx->imgb cannot be accessed. + dec_frm_finish(ctx); + + // check thread's return value + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + + // go to the end of frame data + oapv_bsr_move(bs, ctx->tile_end); + // set stat + fh_to_finfo(&ctx->fh, pbuh.pbu_type, pbuh.group_id, &stat->aui.frm_info[0]); + stat->read = stat->frm_size[0] = BSR_GET_READ_BYTE(bs); + stat->aui.num_frms = 1; + return ret; + +ERR: + return ret; + + return OAPV_OK; +} + +int oapvd_decode_auinfo(oapvd_t did, oapv_bitb_t *bitb, oapv_au_info_t *aui) +{ + int ret; + oapv_bs_t bs; + oapv_aui_t ai; + + oapv_bsr_init(&bs, bitb->addr, bitb->ssize, NULL); + + DUMP_SET(0); + ret = oapvd_vlc_au_info(&bs, &ai); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); + + oapv_mset(aui, 0, sizeof(oapv_au_info_t)); // clear + + aui->num_frms = ai.num_frames; + for(int i = 0; i < ai.num_frames; i++) { + fi_to_finfo(&ai.frame_info[i], ai.pbu_type[i], ai.group_id[i], &aui->frm_info[i]); + } + DUMP_SET(1); + return OAPV_OK; +} + +int oapvd_decode_metadata(oapvd_t did, oapv_bitb_t *bitb, oapvm_payload_t *pld) +{ + return OAPV_OK; +} + /////////////////////////////////////////////////////////////////////////////// // end of decoder code #endif // ENABLE_DECODER diff --git a/src/oapv_def.h b/src/oapv_def.h index 7089600..b834526 100644 --- a/src/oapv_def.h +++ b/src/oapv_def.h @@ -328,10 +328,27 @@ struct oapve_ctx { // start of decoder code #if ENABLE_DECODER /////////////////////////////////////////////////////////////////////////////// -#define DEC_TILE_STAT_NOT_DECODED 0 -#define DEC_TILE_STAT_ON_DECODING 1 -#define DEC_TILE_STAT_DECODED 2 -#define DEC_TILE_STAT_SIZE_ERROR -1 + +#define DEC_TILE_STAT_DECODE (1 << 0) +#define DEC_TILE_STAT_SKIP (1 << 1) + +#define DEC_TILE_STAT_IS_DECODE(stat) ((stat) & DEC_TILE_STAT_DECODE) +#define DEC_TILE_STAT_IS_SKIP(stat) ((stat) & DEC_TILE_STAT_SKIP) + +#define DEC_TILE_STAT_FLAG_DO (1 << 4) +#define DEC_TILE_STAT_FLAG_ON (1 << 5) +#define DEC_TILE_STAT_FLAG_DONE (1 << 6) +#define DEC_TILE_STAT_FLAG_ERR (1 << 7) + +#define DEC_TILE_STAT_DO(stat) (((stat) & 0x0F) | DEC_TILE_STAT_FLAG_DO) +#define DEC_TILE_STAT_ON(stat) (((stat) & 0x0F) | DEC_TILE_STAT_FLAG_ON) +#define DEC_TILE_STAT_DONE(stat) (((stat) & 0x0F) | DEC_TILE_STAT_FLAG_DONE) +#define DEC_TILE_STAT_ERR(stat) (((stat) & 0x0F) | DEC_TILE_STAT_FLAG_ERR) + +#define DEC_TILE_STAT_IS_DO(stat) ((stat) & DEC_TILE_STAT_FLAG_DO) +#define DEC_TILE_STAT_IS_ON(stat) ((stat) & DEC_TILE_STAT_FLAG_ON) +#define DEC_TILE_STAT_IS_DONE(stat) ((stat) & DEC_TILE_STAT_FLAG_DONE) + typedef struct oapvd_tile oapvd_tile_t; struct oapvd_tile { @@ -343,9 +360,9 @@ struct oapvd_tile { int h; /* tile height in unit of pixel */ u32 tile_size; /* tile data size (byte size of tile(tileIdx) syntax) */ - u8 *bs_beg; /* start position of tile in input bistream */ - u8 *bs_end; /* end position of tile() in input bistream */ - volatile s32 stat; // decoding status + u8 *bs_beg; /* start position of tile in input bistream */ + u8 *bs_end; /* end position of tile() in input bistream */ + volatile s32 stat; /* decoding status */ }; typedef struct oapvd_core oapvd_core_t; @@ -408,8 +425,8 @@ struct oapvd_ctx { #endif // ENABLE_DECODER /////////////////////////////////////////////////////////////////////////////// -#define OAPV_FRAME_INFO_BYTE (112) -#define OAPV_PBU_HEADER_BYTE (32) +#define OAPV_PBU_HEADER_BYTE (4) +#define OAPV_FRAME_INFO_BYTE (12) #include "oapv_metadata.h" #include "oapv_vlc.h" diff --git a/src/oapv_param.c b/src/oapv_param.c index 7a164aa..81b5022 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -95,18 +95,46 @@ static int kbps_str_to_int(const char *str) char *s = (char *)str; if(strchr(s, 'K') || strchr(s, 'k')) { char *tmp = strtok(s, "Kk "); - kbps = (int)(atof(tmp)); + char *endptr; + float fval; + errno = 0; + fval = strtof(tmp, &endptr); + if(endptr == tmp || *endptr != '\0' || errno == ERANGE) { + return -1; + } + kbps = (int)fval; } else if(strchr(s, 'M') || strchr(s, 'm')) { char *tmp = strtok(s, "Mm "); - kbps = (int)(atof(tmp) * 1000); + char *endptr; + float fval; + errno = 0; + fval = strtof(tmp, &endptr); + if(endptr == tmp || *endptr != '\0' || errno == ERANGE) { + return -1; + } + kbps = (int)(fval * 1000); } else if(strchr(s, 'G') || strchr(s, 'g')) { char *tmp = strtok(s, "Gg "); - kbps = (int)(atof(tmp) * 1000000); + char *endptr; + float fval; + errno = 0; + fval = strtof(tmp, &endptr); + if(endptr == tmp || *endptr != '\0' || errno == ERANGE) { + return -1; + } + kbps = (int)(fval * 1000000); } else { - kbps = atoi(s); + char *endptr; + long val; + errno = 0; + val = strtol(s, &endptr, 10); + if(endptr == s || *endptr != '\0' || errno == ERANGE || val < INT_MIN || val > INT_MAX) { + return -1; + } + kbps = (int)val; } return kbps; } @@ -262,6 +290,7 @@ int oapve_param_parse(oapve_param_t *param, const char *name, const char *value } NAME_CMP("bitrate") { if(strlen(value) > 0) { + if(strlen(value) + 1 > sizeof(str_buf)) return OAPV_ERR_INVALID_ARGUMENT; strcpy(str_buf, value); // to maintain original value param->bitrate = kbps_str_to_int(str_buf); // unit: kbps if(param->bitrate <= 0) return OAPV_ERR_INVALID_ARGUMENT; diff --git a/src/oapv_port.c b/src/oapv_port.c index 24d60f0..ed9c2de 100644 --- a/src/oapv_port.c +++ b/src/oapv_port.c @@ -63,11 +63,11 @@ void oapv_trace0(char *filename, int line, const char *fmt, ...) char str[1024] = { '\0' }; if(filename != NULL && line >= 0) { - sprintf(str, "[%s:%d] ", filename, line); + snprintf(str, sizeof(str), "[%s:%d] ", filename, line); } va_list args; va_start(args, fmt); - vsprintf(str + strlen(str), fmt, args); + vsnprintf(str + strlen(str), sizeof(str) - strlen(str), fmt, args); va_end(args); printf("%s", str); } @@ -79,7 +79,7 @@ void oapv_trace_line(char *pre) int len = (pre == NULL) ? 0 : (int)strlen(pre); if(len > 0) { - sprintf(str, "%s ", pre); + snprintf(str, sizeof(str), "%s ", pre); len = (int)strlen(str); } for(int i = len; i < chars; i++) { diff --git a/src/oapv_port.h b/src/oapv_port.h index a084bc1..1cdd576 100644 --- a/src/oapv_port.h +++ b/src/oapv_port.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include diff --git a/src/oapv_rc.c b/src/oapv_rc.c index 7d99763..fed8b04 100644 --- a/src/oapv_rc.c +++ b/src/oapv_rc.c @@ -56,8 +56,9 @@ int oapve_rc_get_tile_cost(oapve_ctx_t* ctx, oapve_core_t* core, oapve_tile_t* t for (int y = 0; y < tile->h; y += step_h) { for (int x = 0; x < tile->w; x += step_w) { - int tx = tile->x + x; - int ty = tile->y + y; + int tx = (tile->x + x) >> ctx->c_sft[c][0]; + int ty = (tile->y + y) >> ctx->c_sft[c][1]; + pic = (s16*)((u8*)org + ty * org_s) + tx; ctx->fn_blk_from_pic[c](OAPV_BLK_W, OAPV_BLK_H, pic, 0, org_s, core->coef, (OAPV_BLK_W << 1), ctx->bit_depth, 0); sum += ctx->fn_had8x8(core->coef, 8);