Adding config file parsing implementation

Parameters from config file will be added at --cfg options location to
be processed

Config file example:
 #ignore comment
 ext-partition   : 1 #ignore as well
 codec           : av1
 psnr            : ON

Note(s):
    - Config file is a simple text file
    - Comment starts with hash(#)
      Can be full line or part of the line, after hash(#) details are
ignored
    - Format: field : value
      colon(:) as delimeter, otherwise full line will be ignored
      Space(s) and tab(s) can be used, not inside field
    - long names for field are prefered
       existing --long_name option format
    - "no value" fields should contain ON as value

Example of usage:
    aomenc --cfg=some.cfg src_filename

Configurations support matrix:
enable-ext-partition         : done
enable-loop-restoration      : wip
enable-deblocking            : wip
...

Change-Id: Iad867c5d2da64271cdafa825c89f7d6444582f61
diff --git a/aom/aom_codec.h b/aom/aom_codec.h
index 6800bb2..ff9647a 100644
--- a/aom/aom_codec.h
+++ b/aom/aom_codec.h
@@ -499,6 +499,19 @@
   OBU_METADATA_TYPE_SCALABILITY = 3,
 } OBU_METADATA_TYPE;
 
+/*!\brief Config Options
+ *
+ * This type allows to enumerate and control options defined for control
+ * via config file at runtime.
+ */
+typedef struct cfg_options {
+  /*!\brief Reflects if ext_partition should be enabled
+   *
+   * If this value is non-zero it enabled the feature
+   */
+  unsigned int ext_partition;
+} cfg_options_t;
+
 /*!@} - end defgroup codec*/
 #ifdef __cplusplus
 }
diff --git a/aom/aom_decoder.h b/aom/aom_decoder.h
index d92e43a..0f62bed 100644
--- a/aom/aom_decoder.h
+++ b/aom/aom_decoder.h
@@ -105,6 +105,7 @@
   unsigned int w;       /**< Width */
   unsigned int h;       /**< Height */
   unsigned int allow_lowbitdepth; /**< Allow use of low-bitdepth coding path */
+  cfg_options_t cfg;              /**< Options defined per config attributes */
 } aom_codec_dec_cfg_t;            /**< alias for struct aom_codec_dec_cfg */
 
 /*!\brief Initialize a decoder instance
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h
index 516d797..c32e515 100644
--- a/aom/aom_encoder.h
+++ b/aom/aom_encoder.h
@@ -668,6 +668,11 @@
    * The number of heights specified is given by tile_height_count
    */
   int tile_heights[MAX_TILE_HEIGHTS];
+
+  /*!\brief Options defined per config file
+   *
+   */
+  cfg_options_t cfg;
 } aom_codec_enc_cfg_t; /**< alias for struct aom_codec_enc_cfg */
 
 /*!\brief Initialize an encoder instance
diff --git a/aom/src/aom_encoder.c b/aom/src/aom_encoder.c
index c5abb9f..e3ac522 100644
--- a/aom/src/aom_encoder.c
+++ b/aom/src/aom_encoder.c
@@ -171,6 +171,11 @@
     }
   }
 
+  /* default values */
+  if (cfg) {
+    cfg->cfg.ext_partition = CONFIG_EXT_PARTITION;
+  }
+
   return res;
 }
 
diff --git a/aomdec.c b/aomdec.c
index 5f9181f..739cd69 100644
--- a/aomdec.c
+++ b/aomdec.c
@@ -513,7 +513,9 @@
   int opt_yv12 = 0;
   int opt_i420 = 0;
   int opt_raw = 0;
-  aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH };
+  aom_codec_dec_cfg_t cfg = {
+    0, 0, 0, CONFIG_LOWBITDEPTH, { CONFIG_EXT_PARTITION }
+  };
   unsigned int output_bit_depth = 0;
 #if CONFIG_EXT_TILE
   unsigned int tile_mode = 0;
diff --git a/aomenc.c b/aomenc.c
index f6921eb..aa2e32c 100644
--- a/aomenc.c
+++ b/aomenc.c
@@ -165,6 +165,11 @@
     ARG_DEF("v", "verbose", 0, "Show encoder parameters");
 static const arg_def_t psnrarg =
     ARG_DEF(NULL, "psnr", 0, "Show PSNR in status line");
+#if CONFIG_FILEOPTIONS
+static const arg_def_t use_cfg = ARG_DEF("c", "cfg", 1, "Config file to use");
+static const arg_def_t ext_partition =
+    ARG_DEF(NULL, "ext-partition", 1, "corresponds to CONFIG_EXT_PARTITION");
+#endif
 
 static const struct arg_enum_list test_decode_enum[] = {
   { "off", TEST_DECODE_OFF },
@@ -206,6 +211,9 @@
 static const arg_def_t inbitdeptharg =
     ARG_DEF(NULL, "input-bit-depth", 1, "Bit depth of input");
 static const arg_def_t *main_args[] = { &help,
+#if CONFIG_FILEOPTIONS
+                                        &use_cfg,
+#endif
                                         &debugmode,
                                         &outputfile,
                                         &codecarg,
@@ -915,11 +923,15 @@
   if (!rat->den) die("Error: %s has zero denominator\n", msg);
 }
 
-static void parse_global_config(struct AvxEncoderConfig *global, char **argv) {
+static void parse_global_config(struct AvxEncoderConfig *global, int *argc,
+                                char ***argv) {
   char **argi, **argj;
   struct arg arg;
   const int num_encoder = get_aom_encoder_count();
-
+  char **argv_local = (char **)*argv;
+#if CONFIG_FILEOPTIONS
+  int argc_local = *argc;
+#endif
   if (num_encoder < 1) die("Error: no valid encoder available\n");
 
   /* Initialize default parameters */
@@ -928,9 +940,27 @@
   global->passes = 0;
   global->color_type = I420;
 
-  for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
+#if CONFIG_FILEOPTIONS
+  const char *cfg = NULL;
+  int cfg_included = 0;
+#endif
+  for (argi = argj = argv_local; (*argj = *argi); argi += arg.argv_step) {
     arg.argv_step = 1;
 
+#if CONFIG_FILEOPTIONS
+    if (arg_match(&arg, &use_cfg, argi)) {
+      if (cfg_included) continue;
+      cfg = arg.val;
+
+      arg_cfg(&argc_local, &argv_local, cfg);
+
+      *argj = *argi = *argv_local;
+      argj = argi = argv_local;
+      *argv = argv_local;
+      cfg_included = 1;
+      continue;
+    }
+#endif
     if (arg_match(&arg, &help, argi)) {
       show_help(stdout, 0);
       exit(EXIT_SUCCESS);
@@ -1311,6 +1341,10 @@
       config->cfg.tile_height_count =
           arg_parse_list(&arg, config->cfg.tile_heights, MAX_TILE_HEIGHTS);
 #endif
+#if CONFIG_FILEOPTIONS
+    } else if (arg_match(&arg, &ext_partition, argi)) {
+      config->cfg.cfg.ext_partition = !!arg_parse_uint(&arg) > 0;
+#endif
     } else {
       int i, match = 0;
       for (i = 0; ctrl_args[i]; i++) {
@@ -1614,7 +1648,9 @@
 #if CONFIG_AV1_DECODER
   if (global->test_decode != TEST_DECODE_OFF) {
     const AvxInterface *decoder = get_aom_decoder_by_name(global->codec->name);
-    aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH };
+    aom_codec_dec_cfg_t cfg = {
+      0, 0, 0, CONFIG_LOWBITDEPTH, { CONFIG_EXT_PARTITION }
+    };
     aom_codec_dec_init(&stream->decoder, decoder->codec_interface(), &cfg, 0);
 
 #if CONFIG_EXT_TILE
@@ -1959,9 +1995,13 @@
    * codec.
    */
   argv = argv_dup(argc - 1, argv_ + 1);
-  parse_global_config(&global, argv);
+  parse_global_config(&global, &argc, &argv);
 
+#if CONFIG_FILEOPTIONS
+  if (argc < 2) usage_exit();
+#else
   if (argc < 3) usage_exit();
+#endif
 
   switch (global.color_type) {
     case I420: input.fmt = AOM_IMG_FMT_I420; break;
diff --git a/args.c b/args.c
index b9384de..0ecbf4b 100644
--- a/args.c
+++ b/args.c
@@ -34,6 +34,65 @@
   return a;
 }
 
+char *ignore_front_spaces(const char *str) {
+  while (str[0] == ' ' || str[0] == '\t') ++str;
+  return (char *)str;
+}
+
+void ignore_end_spaces(char *str) {
+  char *end = str + strlen(str);
+  while (end > str && (end[0] == ' ' || end[0] == '\t' || end[0] == '\n' ||
+                       end[0] == '\r' || end[0] == '\0'))
+    --end;
+  if (end >= str) end[1] = '\0';
+}
+
+int arg_cfg(int *argc, char ***argv, const char *file) {
+  char **argv_local = (char **)*argv;
+  char **argv_org = (char **)*argv;
+  char line[1024 * 10];
+  FILE *f = fopen(file, "r");
+  if (!f) return 1;
+
+  while (fgets(line, sizeof(line) - 1, f)) {
+    char *actual_line = ignore_front_spaces(line);
+    char *left, *right, *comment;
+    size_t length = strlen(actual_line);
+
+    if (length == 0 || actual_line[0] == '#') continue;
+    right = strchr(actual_line, ':');
+    if (right == NULL) continue;
+    right[0] = '\0';
+
+    left = ignore_front_spaces(actual_line);
+    right = ignore_front_spaces(right + 1);
+
+    comment = strchr(right, '#');
+    if (comment != NULL) comment[0] = '\0';
+
+    ignore_end_spaces(left);
+    ignore_end_spaces(right);
+
+    char **new_args = argv_dup(*argc, (const char **)argv_local);
+    char *new_line = (char *)malloc(sizeof(*new_line) * 128);
+
+    if (argv_local != argv_org) free(argv_local);
+
+    if (!strcmp(right, "ON"))
+      snprintf(new_line, sizeof(*new_line) * 128, "--%s", left);
+    else
+      snprintf(new_line, sizeof(*new_line) * 128, "--%s=%s", left, right);
+
+    new_args[(*argc) - 1] = new_args[(*argc) - 2];
+    new_args[(*argc) - 2] = new_line;
+    argv_local = new_args;
+    *argv = new_args;
+    (*argc)++;
+  }
+  fclose(f);
+  return 0;
+}
+
 int arg_match(struct arg *arg_, const struct arg_def *def, char **argv) {
   struct arg arg;
 
diff --git a/args.h b/args.h
index c3427bc..d129736 100644
--- a/args.h
+++ b/args.h
@@ -48,6 +48,9 @@
 
 struct arg arg_init(char **argv);
 int arg_match(struct arg *arg_, const struct arg_def *def, char **argv);
+char *ignore_front_spaces(const char *str);
+void ignore_end_spaces(char *str);
+int arg_cfg(int *argc, char ***argv, const char *file);
 const char *arg_next(struct arg *arg);
 void arg_show_usage(FILE *fp, const struct arg_def *const *defs);
 char **argv_dup(int argc, const char **argv);
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 024926f..3304634 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -540,6 +540,7 @@
 
 #endif
   oxcf->mode = GOOD;
+  oxcf->cfg = &cfg->cfg;
 
   switch (cfg->g_pass) {
     case AOM_RC_ONE_PASS: oxcf->pass = 0; break;
@@ -1886,15 +1887,16 @@
         2000,  // rc_two_pass_vbrmax_section
 
         // keyframing settings (kf)
-        AOM_KF_AUTO,  // g_kfmode
-        0,            // kf_min_dist
-        9999,         // kf_max_dist
-        0,            // large_scale_tile
-        0,            // monochrome
-        0,            // tile_width_count
-        0,            // tile_height_count
-        { 0 },        // tile_widths
-        { 0 },        // tile_heights
+        AOM_KF_AUTO,               // g_kfmode
+        0,                         // kf_min_dist
+        9999,                      // kf_max_dist
+        0,                         // large_scale_tile
+        0,                         // monochrome
+        0,                         // tile_width_count
+        0,                         // tile_height_count
+        { 0 },                     // tile_widths
+        { 0 },                     // tile_heights
+        { CONFIG_EXT_PARTITION },  // config file
     } },
 };
 
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index 6ede519..b80cba4 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -119,6 +119,8 @@
     if (ctx->config.dec) {
       priv->cfg = *ctx->config.dec;
       ctx->config.dec = &priv->cfg;
+      // default values
+      priv->cfg.cfg.ext_partition = CONFIG_EXT_PARTITION;
     }
 #if CONFIG_FILM_GRAIN
     priv->image_with_grain = NULL;
@@ -597,6 +599,7 @@
       set_error_detail(ctx, "Failed to allocate frame_worker_data");
       return AOM_CODEC_MEM_ERROR;
     }
+    frame_worker_data->pbi->common.options = &ctx->cfg.cfg;
     frame_worker_data->pbi->frame_worker_owner = worker;
     frame_worker_data->worker_id = i;
     frame_worker_data->scratch_buffer = NULL;
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index cc19377..df510bd 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -621,6 +621,7 @@
   int64_t txcoeff_cost_timer;
   int64_t txcoeff_cost_count;
 #endif
+  const cfg_options_t *options;
 } AV1_COMMON;
 
 // TODO(hkuang): Don't need to lock the whole pool after implementing atomic
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 881abdc..95c2288 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -288,9 +288,11 @@
 #if CONFIG_EXT_PARTITION
   if (cpi->oxcf.superblock_size == AOM_SUPERBLOCK_SIZE_64X64)
     return BLOCK_64X64;
-
-  if (cpi->oxcf.superblock_size == AOM_SUPERBLOCK_SIZE_128X128)
-    return BLOCK_128X128;
+#if CONFIG_FILEOPTIONS
+  if (cpi->common.options && cpi->common.options->ext_partition)
+#endif
+    if (cpi->oxcf.superblock_size == AOM_SUPERBLOCK_SIZE_128X128)
+      return BLOCK_128X128;
 
   assert(cpi->oxcf.superblock_size == AOM_SUPERBLOCK_SIZE_DYNAMIC);
 
@@ -303,7 +305,11 @@
                  cpi->common.tile_height % MAX_MIB_SIZE == 0));
 #endif
 
-  // TODO(any): Possibly could improve this with a heuristic.
+// TODO(any): Possibly could improve this with a heuristic.
+#if CONFIG_FILEOPTIONS
+  if (cpi->common.options && !cpi->common.options->ext_partition)
+    return BLOCK_64X64;
+#endif
   return BLOCK_128X128;
 #else
   (void)cpi;
@@ -3153,6 +3159,7 @@
 #endif
 
   cpi->oxcf = *oxcf;
+  cpi->common.options = oxcf->cfg;
   x->e_mbd.bd = (int)cm->bit_depth;
   x->e_mbd.global_motion = cm->global_motion;
 
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 404fa48..a3ac132 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -318,6 +318,7 @@
 #endif  // CONFIG_MONO_VIDEO
 
   unsigned int motion_vector_unit_test;
+  const cfg_options_t *cfg;
 } AV1EncoderConfig;
 
 static INLINE int is_lossless_requested(const AV1EncoderConfig *cfg) {
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index e1a70ce..2bdc273 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -157,3 +157,4 @@
 set(CONFIG_TMV 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TX64X64 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TXK_SEL 1 CACHE NUMBER "AV1 experiment flag.")
+set(CONFIG_FILEOPTIONS 1 CACHE NUMBER "AV1 config option flag.")