Support parsing subgop config from file

Change-Id: I74ec5fa5d771588e8bdda203c8a016e4581bcd08
diff --git a/aom/aomcx.h b/aom/aomcx.h
index bccf882..6d852ba 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -1266,9 +1266,13 @@
    */
   AV1E_SET_SUBGOP_CONFIG_STR = 158,
 
+  /*!\brief Control to set the subgop config path.
+   */
+  AV1E_SET_SUBGOP_CONFIG_PATH = 159,
+
   /*!\brief Control to get baseline gf interval
    */
-  AV1E_GET_BASELINE_GF_INTERVAL = 159,
+  AV1E_GET_BASELINE_GF_INTERVAL = 160,
 };
 
 /*!\brief aom 1-D scaling mode
@@ -1787,6 +1791,9 @@
 AOM_CTRL_USE_TYPE(AV1E_SET_SUBGOP_CONFIG_STR, const char *)
 #define AOM_CTRL_AV1E_SET_SUBGOP_CONFIG_STR
 
+AOM_CTRL_USE_TYPE(AV1E_SET_SUBGOP_CONFIG_PATH, const char *)
+#define AOM_CTRL_AV1E_SET_SUBGOP_CONFIG_PATH
+
 /*!\endcond */
 /*! @} - end defgroup aom_encoder */
 #ifdef __cplusplus
diff --git a/apps/aomenc.c b/apps/aomenc.c
index f82d0f9..ae9483f 100644
--- a/apps/aomenc.c
+++ b/apps/aomenc.c
@@ -848,7 +848,15 @@
 
 static const arg_def_t subgop_config_str =
     ARG_DEF(NULL, "subgop-config-str", 1,
-            "Set specified SubGOP configurations for various SubGOP lengths. "
+            "Set specified SubGOP configurations in string format provided "
+            "for various SubGOP lengths. "
+            "If this option is not specified (default), the configurations "
+            "are chosen by the encoder using a default algorithm.");
+
+static const arg_def_t subgop_config_path =
+    ARG_DEF(NULL, "subgop-config-path", 1,
+            "Set specified SubGOP configurations in config file path provided "
+            "for various SubGOP lengths. "
             "If this option is not specified (default), the configurations "
             "are chosen by the encoder using a default algorithm.");
 
@@ -952,6 +960,7 @@
                                        &vmaf_model_path,
 #endif
                                        &subgop_config_str,
+                                       &subgop_config_path,
                                        NULL };
 static const int av1_arg_ctrl_map[] = { AOME_SET_CPUUSED,
                                         AOME_SET_ENABLEAUTOALTREF,
@@ -1053,6 +1062,7 @@
                                         AV1E_SET_VMAF_MODEL_PATH,
 #endif
                                         AV1E_SET_SUBGOP_CONFIG_STR,
+                                        AV1E_SET_SUBGOP_CONFIG_PATH,
                                         0 };
 #endif  // CONFIG_AV1_ENCODER
 
@@ -1132,6 +1142,7 @@
   const char *vmaf_model_path;
 #endif
   const char *subgop_config_str;
+  const char *subgop_config_path;
 };
 
 struct stream_state {
@@ -1455,6 +1466,10 @@
     config->subgop_config_str = arg->val;
     return;
   }
+  if (key == AV1E_SET_SUBGOP_CONFIG_PATH) {
+    config->subgop_config_path = arg->val;
+    return;
+  }
 
   // For target level, the settings should accumulate rather than overwrite,
   // so we simply append it.
@@ -2119,6 +2134,10 @@
     AOM_CODEC_CONTROL_TYPECHECKED(&stream->encoder, AV1E_SET_SUBGOP_CONFIG_STR,
                                   stream->config.subgop_config_str);
   }
+  if (stream->config.subgop_config_path) {
+    AOM_CODEC_CONTROL_TYPECHECKED(&stream->encoder, AV1E_SET_SUBGOP_CONFIG_PATH,
+                                  stream->config.subgop_config_path);
+  }
 #if CONFIG_AV1_DECODER
   if (global->test_decode != TEST_DECODE_OFF) {
     aom_codec_iface_t *decoder = get_aom_decoder_by_short_name(
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 86473c0..dfde0fd 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -53,6 +53,7 @@
   aom_tune_metric tuning;
   const char *vmaf_model_path;
   const char *subgop_config_str;
+  const char *subgop_config_path;
   unsigned int qp;  // constant/constrained quality level
   unsigned int rc_max_intra_bitrate_pct;
   unsigned int rc_max_inter_bitrate_pct;
@@ -227,7 +228,8 @@
   5,              // gf_max_pyr_height
   AOM_TUNE_PSNR,  // tuning
   "/usr/local/share/model/vmaf_v0.6.1.pkl",  // VMAF model path
-  NULL,                                      // SubGOP config string
+  NULL,                                      // subgop_config_str
+  NULL,                                      // subgop_config_path
   40,                                        // qp
   0,                                         // rc_max_intra_bitrate_pct
   0,                                         // rc_max_inter_bitrate_pct
@@ -1063,6 +1065,7 @@
   gf_cfg->gf_max_pyr_height = extra_cfg->gf_max_pyr_height;
 
   oxcf->subgop_config_str = extra_cfg->subgop_config_str;
+  oxcf->subgop_config_path = extra_cfg->subgop_config_path;
 
   // Set tune related configuration.
   tune_cfg->tuning = extra_cfg->tuning;
@@ -1898,6 +1901,13 @@
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
+static aom_codec_err_t ctrl_set_subgop_config_path(aom_codec_alg_priv_t *ctx,
+                                                   va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.subgop_config_path = CAST(AV1E_SET_SUBGOP_CONFIG_PATH, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+
 static aom_codec_err_t ctrl_set_film_grain_test_vector(
     aom_codec_alg_priv_t *ctx, va_list args) {
   struct av1_extracfg extra_cfg = ctx->extra_cfg;
@@ -3090,6 +3100,7 @@
   { AV1E_SET_SINGLE_TILE_DECODING, ctrl_set_single_tile_decoding },
   { AV1E_SET_VMAF_MODEL_PATH, ctrl_set_vmaf_model_path },
   { AV1E_SET_SUBGOP_CONFIG_STR, ctrl_set_subgop_config_str },
+  { AV1E_SET_SUBGOP_CONFIG_PATH, ctrl_set_subgop_config_path },
   { AV1E_SET_FILM_GRAIN_TEST_VECTOR, ctrl_set_film_grain_test_vector },
   { AV1E_SET_FILM_GRAIN_TABLE, ctrl_set_film_grain_table },
   { AV1E_SET_DENOISE_NOISE_LEVEL, ctrl_set_denoise_noise_level },
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index be04e23..069630e 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -580,6 +580,13 @@
   av1_noise_estimate_init(&cpi->noise_estimate, cm->width, cm->height);
 }
 
+int aom_strcmp(const char *a, const char *b) {
+  if (a == NULL && b == NULL) return 0;
+  if (a == NULL && b != NULL) return -1;
+  if (a != NULL && b == NULL) return 1;
+  return strcmp(a, b);
+}
+
 void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf) {
   AV1_COMMON *const cm = &cpi->common;
   SequenceHeader *const seq_params = &cm->seq_params;
@@ -796,24 +803,40 @@
     cpi->oxcf.gf_cfg.lag_in_frames = lap_lag_in_frames;
   }
 
-  if (cpi->subgop_config_str == NULL ||
-      strcmp(cpi->subgop_config_str, oxcf->subgop_config_str)) {
+  bool subgop_config_changed = false;
+  if (aom_strcmp(cpi->subgop_config_path, oxcf->subgop_config_path)) {
+    aom_free(cpi->subgop_config_path);
+    if (oxcf->subgop_config_path != NULL) {
+      cpi->subgop_config_path =
+          (char *)aom_malloc((strlen(oxcf->subgop_config_path) + 1) *
+                             sizeof(*oxcf->subgop_config_path));
+      strcpy(cpi->subgop_config_path, oxcf->subgop_config_path);
+    }
+    subgop_config_changed = true;
+  }
+  if (aom_strcmp(cpi->subgop_config_str, oxcf->subgop_config_str)) {
     aom_free(cpi->subgop_config_str);
     if (oxcf->subgop_config_str != NULL) {
       cpi->subgop_config_str =
           (char *)aom_malloc((strlen(oxcf->subgop_config_str) + 1) *
                              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",
-               cpi->subgop_config_set.num_configs);
-        // Uncomment to print out the configuration
-        // av1_print_subgop_config_set(&cpi->subgop_config_set);
-      }
     }
+    subgop_config_changed = true;
+  }
+  if (subgop_config_changed && cpi->compressor_stage == ENCODE_STAGE) {
+    av1_init_subgop_config_set(&cpi->subgop_config_set);
+    // Parse config file first
+    av1_process_subgop_config_set_fromfile(cpi->subgop_config_path,
+                                           &cpi->subgop_config_set);
+    // Parse config string next, which may override config file configs
+    // or append to it.
+    av1_process_subgop_config_set(cpi->subgop_config_str,
+                                  &cpi->subgop_config_set);
+    printf("Successfully processed %d subgop configs.\n",
+           cpi->subgop_config_set.num_configs);
+    // Uncomment to print out the configuration
+    // av1_print_subgop_config_set(&cpi->subgop_config_set);
   }
 }
 
@@ -1506,6 +1529,7 @@
   av1_free_ref_frame_buffers(cm->buffer_pool);
 
   aom_free(cpi->subgop_config_str);
+  aom_free(cpi->subgop_config_path);
   aom_free(cpi);
 
 #ifdef OUTPUT_YUV_REC
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 1ca4fde..742f49a 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -902,6 +902,9 @@
   // SubGOP config.
   const char *subgop_config_str;
 
+  // SubGOP config.
+  const char *subgop_config_path;
+
   // Configuration related to encoder toolsets.
   ToolCfg tool_cfg;
 
@@ -2288,6 +2291,11 @@
   char *subgop_config_str;
 
   /*!
+   * SubGOP configuration file path
+   */
+  char *subgop_config_path;
+
+  /*!
    * Information related to subGOP configuration if specified.
    */
   SubGOPSetCfg subgop_config_set;