target level API: specify per operating point

Modify the API to allow specifying target level for each operating point.

BUG=aomedia:2332

Change-Id: I890103a7cbf06c2584585e7f6cbae37917eb91ab
diff --git a/aom/aomcx.h b/aom/aomcx.h
index 97b329f..fb79583 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -512,11 +512,19 @@
    */
   AV1E_SET_RENDER_SIZE,
 
-  /*!\brief Control to set target sequence level index
-   * Possible values are:
-   * -1: Not enabled (default)
-   * 0~23: Target for the given sequence level index
-   * 31: Passively measure the level of the encoded bitstream
+  /*!\brief Control to set target sequence level index for a certain operating
+   * point(OP).
+   * Possible values are in the form of "ABxy"(pad leading zeros if less than
+   * 4 digits).
+   *   AB: OP index.
+   *   xy: Target level index for the OP. Can be values 0~23(corresponding to
+   *   level 2.0 ~ 7.3) or 31(maximum level parameter, no level-based
+   *   constraints).
+   * E.g. "0" means target level index 0 for the 0th OP;
+   *      "111" means target level index 11 for the 1st OP;
+   *      "1021" means target level index 21 for the 10th OP.
+   * If the target level is not specified for an OP, the maximum level parameter
+   * of 31 is used as default.
    */
   AV1E_SET_TARGET_SEQ_LEVEL_IDX,
 
diff --git a/apps/aomenc.c b/apps/aomenc.c
index b16baa1..00b398f 100644
--- a/apps/aomenc.c
+++ b/apps/aomenc.c
@@ -628,9 +628,12 @@
 static const arg_def_t target_seq_level_idx =
     ARG_DEF(NULL, "target-seq-level-idx", 1,
             "Target sequence level index. "
-            "(0~23: Target for the given level index; "
-            "31(default): Maximum level parameter, no level-based "
-            "constraints.)");
+            "Possible values are in the form of \"ABxy\"(pad leading zeros if "
+            "less than 4 digits). "
+            "AB: Operating point(OP) index; "
+            "xy: Target level index for the OP. "
+            "E.g. \"0\" means target level index 0 for the 0th OP; "
+            "\"1021\" means target level index 21 for the 10th OP.");
 
 static const struct arg_enum_list color_primaries_enum[] = {
   { "bt709", AOM_CICP_CP_BT_709 },
@@ -1280,6 +1283,17 @@
     return;
   }
 
+  // For target level, the settings should accumulate rather than overwrite,
+  // so we simply append it.
+  if (key == AV1E_SET_TARGET_SEQ_LEVEL_IDX) {
+    j = config->arg_ctrl_cnt;
+    assert(j < (int)ARG_CTRL_CNT_MAX);
+    config->arg_ctrls[j][0] = key;
+    config->arg_ctrls[j][1] = arg_parse_enum_or_int(arg);
+    ++config->arg_ctrl_cnt;
+    return;
+  }
+
   /* Point either to the next free element or the first instance of this
    * control.
    */
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index b71a15f..a453588 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -129,7 +129,7 @@
   int use_inter_dct_only;
   int use_intra_default_tx_only;
   int quant_b_adapt;
-  AV1_LEVEL target_seq_level_idx;
+  AV1_LEVEL target_seq_level_idx[MAX_NUM_OPERATING_POINTS];
   // Bit mask to specify which tier each of the 32 possible operating points
   // conforms to.
   unsigned int tier_mask;
@@ -227,14 +227,17 @@
   0,   // noise_level
   32,  // noise_block_size
 #endif
-  0,            // chroma_subsampling_x
-  0,            // chroma_subsampling_y
-  0,            // reduced_tx_type_set
-  0,            // use_intra_dct_only
-  0,            // use_inter_dct_only
-  0,            // use_intra_default_tx_only
-  0,            // quant_b_adapt
-  31,           // target_seq_level_idx
+  0,  // chroma_subsampling_x
+  0,  // chroma_subsampling_y
+  0,  // reduced_tx_type_set
+  0,  // use_intra_dct_only
+  0,  // use_inter_dct_only
+  0,  // use_intra_default_tx_only
+  0,  // quant_b_adapt
+  {
+      31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+      31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+  },            // target_seq_level_idx
   0,            // tier_mask
   COST_UPD_SB,  // coeff_cost_upd_freq
   COST_UPD_SB,  // mode_cost_upd_freq
@@ -451,8 +454,10 @@
   RANGE_CHECK(extra_cfg, coeff_cost_upd_freq, 0, 2);
   RANGE_CHECK(extra_cfg, mode_cost_upd_freq, 0, 2);
 
-  if (!is_valid_seq_level_idx(extra_cfg->target_seq_level_idx))
-    ERROR("Target sequence level index is invalid");
+  for (int i = 0; i < MAX_NUM_OPERATING_POINTS; ++i) {
+    if (!is_valid_seq_level_idx(extra_cfg->target_seq_level_idx[i]))
+      ERROR("Target sequence level index is invalid");
+  }
 
   return AOM_CODEC_OK;
 }
@@ -814,7 +819,8 @@
   oxcf->border_in_pixels = (oxcf->resize_mode || oxcf->superres_mode)
                                ? AOM_BORDER_IN_PIXELS
                                : AOM_ENC_NO_SCALE_BORDER;
-  oxcf->target_seq_level_idx = extra_cfg->target_seq_level_idx;
+  memcpy(oxcf->target_seq_level_idx, extra_cfg->target_seq_level_idx,
+         sizeof(oxcf->target_seq_level_idx));
   oxcf->tier_mask = extra_cfg->tier_mask;
   return AOM_CODEC_OK;
 }
@@ -1463,7 +1469,13 @@
 static aom_codec_err_t ctrl_set_target_seq_level_idx(aom_codec_alg_priv_t *ctx,
                                                      va_list args) {
   struct av1_extracfg extra_cfg = ctx->extra_cfg;
-  extra_cfg.target_seq_level_idx = CAST(AV1E_SET_TARGET_SEQ_LEVEL_IDX, args);
+  const int val = CAST(AV1E_SET_TARGET_SEQ_LEVEL_IDX, args);
+  const int level = val % 100;
+  const int operating_point_idx = val / 100;
+  if (operating_point_idx >= 0 &&
+      operating_point_idx < MAX_NUM_OPERATING_POINTS) {
+    extra_cfg.target_seq_level_idx[operating_point_idx] = (AV1_LEVEL)level;
+  }
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 9366660..76a9b27 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2393,8 +2393,15 @@
   assert(IMPLIES(seq_params->profile <= PROFILE_1,
                  seq_params->bit_depth <= AOM_BITS_10));
 
-  cpi->target_seq_level_idx = oxcf->target_seq_level_idx;
-  cpi->keep_level_stats = cpi->target_seq_level_idx < SEQ_LEVELS;
+  memcpy(cpi->target_seq_level_idx, oxcf->target_seq_level_idx,
+         sizeof(cpi->target_seq_level_idx));
+  cpi->keep_level_stats = 0;
+  for (int i = 0; i < MAX_NUM_OPERATING_POINTS; ++i) {
+    if (cpi->target_seq_level_idx[i] < SEQ_LEVELS) {
+      cpi->keep_level_stats = 1;
+      break;
+    }
+  }
 
   cm->timing_info_present = oxcf->timing_info_present;
   cm->timing_info.num_units_in_display_tick =
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 9729e08..8b64998 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -392,7 +392,7 @@
   COST_UPDATE_TYPE coeff_cost_upd_freq;
   COST_UPDATE_TYPE mode_cost_upd_freq;
   int border_in_pixels;
-  AV1_LEVEL target_seq_level_idx;
+  AV1_LEVEL target_seq_level_idx[MAX_NUM_OPERATING_POINTS];
   // Bit mask to specify which tier each of the 32 possible operating points
   // conforms to.
   unsigned int tier_mask;
@@ -995,7 +995,7 @@
   uint64_t frame_component_time[kTimingComponents];
 #endif
   // level info and flags
-  AV1_LEVEL target_seq_level_idx;
+  AV1_LEVEL target_seq_level_idx[MAX_NUM_OPERATING_POINTS];
   int keep_level_stats;
   AV1LevelInfo level_info[MAX_NUM_OPERATING_POINTS];
 } AV1_COMP;
diff --git a/av1/encoder/level.c b/av1/encoder/level.c
index fdaab76..3a5f937 100644
--- a/av1/encoder/level.c
+++ b/av1/encoder/level.c
@@ -231,9 +231,10 @@
   "Too many tiles are used.",
 };
 
-static void check_level_constraints(AV1_COMP *cpi,
+static void check_level_constraints(AV1_COMP *cpi, int operating_point_idx,
                                     const AV1LevelSpec *level_spec) {
-  const AV1_LEVEL target_seq_level_idx = cpi->target_seq_level_idx;
+  const AV1_LEVEL target_seq_level_idx =
+      cpi->target_seq_level_idx[operating_point_idx];
   if (target_seq_level_idx >= SEQ_LEVELS) return;
   TARGET_LEVEL_FAIL_ID fail_id = TARGET_LEVEL_FAIL_IDS;
   // Check level conformance
@@ -351,6 +352,6 @@
     (void)ts_start;
     (void)ts_end;
 
-    check_level_constraints(cpi, level_spec);
+    check_level_constraints(cpi, i, level_spec);
   }
 }