Add code for parsing a subgop config file format Adds support for subgop config file format. The parsing code is enhanced to append configs derived from a string and a config file. The code to parse a config file parameter from the commandline is to be added in the next patch. The example of a parseable subgop config file is as follows. Note anything within box brackets is optional and not parsed: ---- File starts ----- [num_configs:2] config:[0] num_frames:16 subgop_in_gop_code:0 num_steps:24 [step:0] disp_frame_idx:16 type_code:F pyr_level:1 references:1^1 [step:1] disp_frame_idx:8 type_code:F pyr_level:2 references:1^1^2^-1 [step:2] disp_frame_idx:4 type_code:U pyr_level:3 references:1^1^2^-2^-1 [step:3] disp_frame_idx:2 type_code:U pyr_level:4 references:1^1^-3^-2^-1 [step:4] disp_frame_idx:1 type_code:V pyr_level:5 references:1^1^-4^-3^-2^-1 [step:5] disp_frame_idx:2 type_code:S pyr_level:4 [step:6] disp_frame_idx:3 type_code:V pyr_level:5 references:1^5^4^-3^-2^-1 [step:7] disp_frame_idx:4 type_code:S pyr_level:3 [step:8] disp_frame_idx:6 type_code:U pyr_level:4 references:1^3^4^-2^-1 [step:9] disp_frame_idx:5 type_code:V pyr_level:5 references:1^4^5^3^-4^-2^-1 [step:10] disp_frame_idx:6 type_code:S pyr_level:4 [step:11] disp_frame_idx:7 type_code:V pyr_level:5 references:1^3^5^4^-2^-1 [step:12] disp_frame_idx:8 type_code:R pyr_level:2 references:1^2^-1 [step:13] disp_frame_idx:12 type_code:U pyr_level:3 references:1^3^2^-1 [step:14] disp_frame_idx:10 type_code:U pyr_level:4 references:1^3^4^2^-3^-1 [step:15] disp_frame_idx:9 type_code:V pyr_level:5 references:1^3^4^2^-4^-3^-1 [step:16] disp_frame_idx:10 type_code:S pyr_level:4 [step:17] disp_frame_idx:11 type_code:V pyr_level:5 references:1^2^4^5^-3^-1 [step:18] disp_frame_idx:12 type_code:S pyr_level:3 [step:19] disp_frame_idx:14 type_code:U pyr_level:4 references:1^2^4^3^-1 [step:20] disp_frame_idx:13 type_code:V pyr_level:5 references:1^2^4^5^3^-4^-1 [step:21] disp_frame_idx:14 type_code:S pyr_level:4 [step:22] disp_frame_idx:15 type_code:V pyr_level:5 references:1^2^3^4^5^-1 [step:23] disp_frame_idx:16 type_code:R pyr_level:1 references:1^1 config:[1] num_frames:16 subgop_in_gop_code:1 num_steps:25 [step:0] disp_frame_idx:14 type_code:F pyr_level:1 references:1^1 [step:1] disp_frame_idx:7 type_code:F pyr_level:2 references:1^1^2^-1 [step:2] disp_frame_idx:4 type_code:U pyr_level:3 references:1^1^2^-2^-1 [step:3] disp_frame_idx:2 type_code:U pyr_level:4 references:1^1^-3^-2^-1 [step:4] disp_frame_idx:1 type_code:V pyr_level:5 references:1^1^-4^-3^-2^-1 [step:5] disp_frame_idx:2 type_code:S pyr_level:4 [step:6] disp_frame_idx:3 type_code:V pyr_level:5 references:1^5^4^-3^-2^-1 [step:7] disp_frame_idx:4 type_code:S pyr_level:3 [step:8] disp_frame_idx:6 type_code:U pyr_level:4 references:1^3^4^-2^-1 [step:9] disp_frame_idx:5 type_code:V pyr_level:5 references:1^4^5^3^-4^-2^-1 [step:10] disp_frame_idx:6 type_code:S pyr_level:4 [step:11] disp_frame_idx:7 type_code:R pyr_level:2 references:1^2^-1 [step:12] disp_frame_idx:11 type_code:U pyr_level:3 references:1^3^2^-1 [step:13] disp_frame_idx:9 type_code:U pyr_level:4 references:1^3^4^2^-3^-1 [step:14] disp_frame_idx:8 type_code:V pyr_level:5 references:1^3^4^2^-4^-3^-1 [step:15] disp_frame_idx:9 type_code:S pyr_level:4 [step:16] disp_frame_idx:10 type_code:V pyr_level:5 references:1^2^4^5^-3^-1 [step:17] disp_frame_idx:11 type_code:S pyr_level:3 [step:18] disp_frame_idx:13 type_code:U pyr_level:4 references:1^2^4^3^-1 [step:19] disp_frame_idx:12 type_code:V pyr_level:5 references:1^2^4^5^3^-4^-1 [step:20] disp_frame_idx:13 type_code:S pyr_level:4 [step:21] disp_frame_idx:14 type_code:R pyr_level:1 references:1^-1 [step:22] disp_frame_idx:16 type_code:U pyr_level:4 references:1^1^2^3^4 [step:23] disp_frame_idx:15 type_code:V pyr_level:5 references:1^1^2^3^4^5 [step:24] disp_frame_idx:16 type_code:S pyr_level:4 ---- File end ----- Change-Id: I62ff654ff7bc438cba75891256b1f496335b001f
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index 0f90450..be04e23 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -805,6 +805,7 @@ sizeof(*oxcf->subgop_config_str)); strcpy(cpi->subgop_config_str, oxcf->subgop_config_str); if (cpi->compressor_stage == ENCODE_STAGE) { + av1_init_subgop_config_set(&cpi->subgop_config_set); av1_process_subgop_config_set(oxcf->subgop_config_str, &cpi->subgop_config_set); printf("Successfully processed %d subgop configs.\n",
diff --git a/av1/encoder/subgop.c b/av1/encoder/subgop.c index 7ec64ca..1f43311 100644 --- a/av1/encoder/subgop.c +++ b/av1/encoder/subgop.c
@@ -9,6 +9,7 @@ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. */ +#include <ctype.h> #include <stdio.h> #include <string.h> #include "av1/encoder/subgop.h" @@ -28,7 +29,42 @@ return ptr; } -static void init_subgop_config_set(SubGOPSetCfg *config_set) { +static char *read_token_after(char *str, const char *delim, char **saveptr) { + if (str == NULL) return NULL; + if (strlen(str) == 0) return NULL; + char *ptr = str; + char *x = strstr(str, delim); + if (x) { + ptr = x + strlen(delim); + while (*x != 0 && !isspace(*x)) x++; + *x = 0; + *saveptr = x + 1; + return ptr; + } else { + *saveptr = NULL; + return NULL; + } +} + +static bool readline(char *buf, int size, FILE *fp) { + buf[0] = '\0'; + buf[size - 1] = '\0'; + char *tmp; + while (1) { + if (fgets(buf, size, fp) == NULL) { + *buf = '\0'; + return false; + } else { + if ((tmp = strrchr(buf, '\n')) != NULL) *tmp = '\0'; + for (int i = 0; i < (int)strlen(buf); ++i) { + if (!isspace(buf[i])) return true; + } + } + } + return true; +} + +void av1_init_subgop_config_set(SubGOPSetCfg *config_set) { memset(config_set, 0, sizeof(*config_set)); config_set->num_configs = 0; for (int i = 0; i < MAX_SUBGOP_CONFIGS; ++i) { @@ -36,6 +72,21 @@ } } +static void check_duplicate_and_add(SubGOPSetCfg *config_set) { + int k; + const int n = config_set->num_configs; + for (k = 0; k < n; ++k) { + if (config_set->config[k].num_frames == config_set->config[n].num_frames && + config_set->config[k].subgop_in_gop_code == + config_set->config[n].subgop_in_gop_code) { + memcpy(&config_set->config[k], &config_set->config[n], + sizeof(config_set->config[k])); + return; + } + } + if (k == n) config_set->num_configs++; +} + static int process_subgop_step(char *str, SubGOPStepCfg *step) { char *ptr; step->num_references = 0; @@ -171,7 +222,6 @@ } int av1_process_subgop_config_set(const char *param, SubGOPSetCfg *config_set) { - init_subgop_config_set(config_set); if (!param) return 1; if (!strlen(param)) return 1; const int bufsize = (int)((strlen(param) + 1) * sizeof(*param)); @@ -187,7 +237,7 @@ if (res) { res = check_subgop_config(&config_set->config[config_set->num_configs]); if (res) { - config_set->num_configs++; + check_duplicate_and_add(config_set); } else { printf( "Warning: Subgop config validation failed for config #%d, " @@ -207,19 +257,155 @@ return 1; } +static int process_subgop_config_fromfile(FILE *fp, SubGOPCfg *config) { + char line[256]; + int linesize = 256; + int s; + if (!readline(line, linesize, fp)) return 0; + char *token; + char *str = line; + token = read_token_after(str, "num_frames:", &str); + if (!token) return 0; + if (strlen(token) == 0) return 0; + const int num_frames = atoi(token); + if (num_frames <= 0) return 0; + config->num_frames = num_frames; + + token = read_token_after(str, "subgop_in_gop_code:", &str); + if (!token) return 0; + if (strlen(token) == 0) return 0; + const int subgop_in_gop_code = atoi(token); + // check for invalid subgop_in_gop_code + if (subgop_in_gop_code < 0 || subgop_in_gop_code >= SUBGOP_IN_GOP_CODES) + return 0; + config->subgop_in_gop_code = (SUBGOP_IN_GOP_CODE)subgop_in_gop_code; + + token = read_token_after(str, "num_steps:", &str); + if (!token) return 0; + if (strlen(token) == 0) return 0; + config->num_steps = atoi(token); + for (s = 0; s < config->num_steps; ++s) { + SubGOPStepCfg *step = &config->step[s]; + step->num_references = 0; + if (!readline(line, linesize, fp)) return 0; + str = line; + + token = read_token_after(str, "disp_frame_idx:", &str); + if (!token) return 0; + if (strlen(token) == 0) return 0; + const int disp_frame_idx = atoi(token); + if (disp_frame_idx < 0 || disp_frame_idx > config->num_frames) return 0; + step->disp_frame_idx = disp_frame_idx; + token = read_token_after(str, "type_code:", &str); + if (!token) return 0; + if (strlen(token) != 1) return 0; + switch (*token) { + case 'V': step->type_code = FRAME_TYPE_INO_VISIBLE; break; + case 'R': step->type_code = FRAME_TYPE_INO_REPEAT; break; + case 'S': step->type_code = FRAME_TYPE_INO_SHOWEXISTING; break; + case 'F': step->type_code = FRAME_TYPE_OOO_FILTERED; break; + case 'U': step->type_code = FRAME_TYPE_OOO_UNFILTERED; break; + default: return 0; + } + if (step->type_code == FRAME_TYPE_INO_SHOWEXISTING) { + int k; + for (k = 0; k < s; ++k) { + if (config->step[k].disp_frame_idx == step->disp_frame_idx) { + step->pyr_level = config->step[k].pyr_level; + break; + } + } + // showexisting for a frame not coded before is invalid + if (k == s) return 0; + continue; + } + token = read_token_after(str, "pyr_level:", &str); + if (!token) return 0; + if (strlen(token) == 0) return 0; + const int pyr_level = atoi(token); + if (pyr_level <= 0) return 0; + step->pyr_level = pyr_level; + + token = read_token_after(str, "references:", &str); + if (!token) { // no references specified + step->num_references = -1; + continue; + } + if (strlen(token) == 0) continue; // no references + + char delim[] = "^"; + str = token; + while ((token = my_strtok_r(str, delim, &str)) != NULL) { + if (step->num_references >= INTER_REFS_PER_FRAME) return 0; + step->references[step->num_references] = (int8_t)strtol(token, NULL, 10); + step->num_references++; + } + } + return 1; +} + +int av1_process_subgop_config_set_fromfile(const char *paramfile, + SubGOPSetCfg *config_set) { + if (!paramfile) return 1; + if (!strlen(paramfile)) return 1; + FILE *fp = fopen(paramfile, "r"); + if (!fp) return 0; + char line[256]; + int linesize = 256; + char *tok, *str; + if (readline(line, linesize, fp)) { + tok = read_token_after(line, "[num_configs:", &str); + if (!tok) rewind(fp); + } else { + // Blank file + return 1; + } + while (readline(line, linesize, fp)) { + str = line; + tok = read_token_after(str, "config:", &str); + if (tok) { + int res = process_subgop_config_fromfile( + fp, &config_set->config[config_set->num_configs]); + if (res) { + res = check_subgop_config(&config_set->config[config_set->num_configs]); + if (res) { + check_duplicate_and_add(config_set); + } else { + printf( + "Warning: Subgop config validation failed for config #%d, " + "skipping the rest.\n", + config_set->num_configs); + fclose(fp); + return 0; + } + } else { + printf("Warning: config parsing failed, skipping the rest.\n"); + fclose(fp); + return 0; + } + } else { + printf("Warning: config not found, skipping the rest.\n"); + fclose(fp); + return 0; + } + } + fclose(fp); + return 1; +} + void av1_print_subgop_config_set(SubGOPSetCfg *config_set) { if (!config_set->num_configs) return; printf("SUBGOP CONFIG SET\n"); printf("=================\n"); - printf("num_configs:%d\n", config_set->num_configs); + printf("[num_configs:%d]\n", config_set->num_configs); for (int i = 0; i < config_set->num_configs; ++i) { - printf("config:%d ->\n", i); + printf("config:[%d]\n", i); SubGOPCfg *config = &config_set->config[i]; - printf(" num_frames:%d\n", config->num_frames); - printf(" subgop_in_gop_code:%d\n", config->subgop_in_gop_code); - printf(" num_steps:%d\n", config->num_steps); + printf(" num_frames:%d", config->num_frames); + printf(" subgop_in_gop_code:%d", config->subgop_in_gop_code); + printf(" num_steps:%d\n", config->num_steps); for (int j = 0; j < config->num_steps; ++j) { - printf(" step:%d ->", j); + printf(" [step:%d]", j); printf(" disp_frame_idx:%d", config->step[j].disp_frame_idx); printf(" type_code:%c", config->step[j].type_code); printf(" pyr_level:%d", config->step[j].pyr_level);
diff --git a/av1/encoder/subgop.h b/av1/encoder/subgop.h index 9c23dfe..464af04 100644 --- a/av1/encoder/subgop.h +++ b/av1/encoder/subgop.h
@@ -17,8 +17,11 @@ extern "C" { #endif +void av1_init_subgop_config_set(SubGOPSetCfg *config_set); int av1_process_subgop_config_set(const char *param, SubGOPSetCfg *config_set); void av1_print_subgop_config_set(SubGOPSetCfg *config_set); +int av1_process_subgop_config_set_fromfile(const char *paramfile, + SubGOPSetCfg *config_set); // Finds the ptr to the subgop config with the queried number of // frames and whether it is the last or first subgop in a gop.