Add support for level targetting

Enable the encoder to encode to the given target level.
Use existing encoder config to control the average bitrate, number of
tiles, and minimum compression ratio. Limit the bits spent on keyframes
and alt-ref frames to prevent bitrate spikes, which may cause the
decoder model to fail.

BUG=aomedia:2332

Change-Id: I1b0de6a79f074de54e102823c0d19a528c456600
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index d2abfd7..269f003 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -1402,7 +1402,19 @@
 }
 
 static INLINE int is_valid_seq_level_idx(AV1_LEVEL seq_level_idx) {
-  return seq_level_idx < SEQ_LEVELS || seq_level_idx == SEQ_LEVEL_MAX;
+  return seq_level_idx == SEQ_LEVEL_MAX ||
+      (seq_level_idx < SEQ_LEVELS &&
+      // The following levels are currently undefined.
+      seq_level_idx != SEQ_LEVEL_2_2 &&
+      seq_level_idx != SEQ_LEVEL_2_3 &&
+      seq_level_idx != SEQ_LEVEL_3_2 &&
+      seq_level_idx != SEQ_LEVEL_3_3 &&
+      seq_level_idx != SEQ_LEVEL_4_2 &&
+      seq_level_idx != SEQ_LEVEL_4_3 &&
+      seq_level_idx != SEQ_LEVEL_7_0 &&
+      seq_level_idx != SEQ_LEVEL_7_1 &&
+      seq_level_idx != SEQ_LEVEL_7_2 &&
+      seq_level_idx != SEQ_LEVEL_7_3);
 }
 
 #ifdef __cplusplus
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 67f4ee9..f5d6527 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2600,6 +2600,50 @@
   av1_zero(*bufs);  // Set all pointers to NULL for safety.
 }
 
+static void config_target_level(AV1_COMP *const cpi, AV1_LEVEL target_level,
+                                int tier) {
+  aom_clear_system_state();
+
+  AV1EncoderConfig *const oxcf = &cpi->oxcf;
+  SequenceHeader *const seq_params = &cpi->common.seq_params;
+
+  // Adjust target bitrate to be no larger than 70% of level limit.
+  const BITSTREAM_PROFILE profile = seq_params->profile;
+  const double level_bitrate_limit =
+      av1_get_max_bitrate_for_level(target_level, tier, profile);
+  const int64_t max_bitrate = (int64_t)(level_bitrate_limit * 0.70);
+  oxcf->target_bandwidth = AOMMIN(oxcf->target_bandwidth, max_bitrate);
+  // Also need to update cpi->twopass.bits_left.
+  TWO_PASS *const twopass = &cpi->twopass;
+  FIRSTPASS_STATS *stats = &twopass->total_stats;
+  cpi->twopass.bits_left =
+      (int64_t)(stats->duration * cpi->oxcf.target_bandwidth / 10000000.0);
+
+  // Adjust max over-shoot percentage.
+  oxcf->over_shoot_pct = 0;
+
+  // Adjust max quantizer.
+  oxcf->worst_allowed_q = 255;
+
+  // Adjust number of tiles and tile columns to be under level limit.
+  int max_tiles, max_tile_cols;
+  av1_get_max_tiles_for_level(target_level, &max_tiles, &max_tile_cols);
+  while (oxcf->tile_columns > 0 && (1 << oxcf->tile_columns) > max_tile_cols) {
+    --oxcf->tile_columns;
+  }
+  const int tile_cols = (1 << oxcf->tile_columns);
+  while (oxcf->tile_rows > 0 &&
+         tile_cols * (1 << oxcf->tile_rows) > max_tiles) {
+    --oxcf->tile_rows;
+  }
+
+  // Adjust min compression ratio.
+  const int still_picture = seq_params->still_picture;
+  const double min_cr =
+      av1_get_min_cr_for_level(target_level, tier, still_picture);
+  oxcf->min_cr = AOMMAX(oxcf->min_cr, (unsigned int)(min_cr * 100));
+}
+
 void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf) {
   AV1_COMMON *const cm = &cpi->common;
   SequenceHeader *const seq_params = &cm->seq_params;
@@ -2619,19 +2663,6 @@
   assert(IMPLIES(seq_params->profile <= PROFILE_1,
                  seq_params->bit_depth <= AOM_BITS_10));
 
-  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 |= 1u << i;
-      if (!cpi->level_info[i]) {
-        CHECK_MEM_ERROR(cm, cpi->level_info[i],
-                        aom_calloc(1, sizeof(*cpi->level_info[i])));
-      }
-    }
-  }
-
   cm->timing_info_present = oxcf->timing_info_present;
   cm->timing_info.num_units_in_display_tick =
       oxcf->timing_info.num_units_in_display_tick;
@@ -2669,6 +2700,26 @@
   x->e_mbd.bd = (int)seq_params->bit_depth;
   x->e_mbd.global_motion = cm->global_motion;
 
+  memcpy(cpi->target_seq_level_idx, cpi->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 |= 1u << i;
+      if (!cpi->level_info[i]) {
+        CHECK_MEM_ERROR(cm, cpi->level_info[i],
+                        aom_calloc(1, sizeof(*cpi->level_info[i])));
+      }
+    }
+  }
+
+  // TODO(huisu@): level targeting currently only works for the 0th operating
+  // point, so scalable coding is not supported yet.
+  if (cpi->target_seq_level_idx[0] < SEQ_LEVELS) {
+    // Adjust encoder config in order to meet target level.
+    config_target_level(cpi, cpi->target_seq_level_idx[0], seq_params->tier[0]);
+  }
+
   if ((oxcf->pass == 0) && (oxcf->rc_mode == AOM_Q)) {
     rc->baseline_gf_interval = FIXED_GF_INTERVAL;
   } else {
diff --git a/av1/encoder/level.c b/av1/encoder/level.c
index 4b88631..6a6ceb2 100644
--- a/av1/encoder/level.c
+++ b/av1/encoder/level.c
@@ -276,6 +276,20 @@
   return bitrate_basis * bitrate_profile_factor;
 }
 
+double av1_get_max_bitrate_for_level(AV1_LEVEL level_index, int tier,
+                                     BITSTREAM_PROFILE profile) {
+  assert(is_valid_seq_level_idx(level_index));
+  return get_max_bitrate(&av1_level_defs[level_index], tier, profile);
+}
+
+void av1_get_max_tiles_for_level(AV1_LEVEL level_index, int *const max_tiles,
+                                 int *const max_tile_cols) {
+  assert(is_valid_seq_level_idx(level_index));
+  const AV1LevelSpec *const level_spec = &av1_level_defs[level_index];
+  *max_tiles = level_spec->max_tiles;
+  *max_tile_cols = level_spec->max_tile_cols;
+}
+
 // We assume time t to be valid if and only if t >= 0.0.
 // So INVALID_TIME can be defined as anything less than 0.
 #define INVALID_TIME (-1.0)
@@ -721,6 +735,14 @@
   return AOMMAX(min_cr_basis * speed_adj, 0.8);
 }
 
+double av1_get_min_cr_for_level(AV1_LEVEL level_index, int tier,
+                                int is_still_picture) {
+  assert(is_valid_seq_level_idx(level_index));
+  const AV1LevelSpec *const level_spec = &av1_level_defs[level_index];
+  return get_min_cr(level_spec, tier, is_still_picture,
+                    level_spec->max_decode_rate);
+}
+
 static void get_temporal_parallel_params(int scalability_mode_idc,
                                          int *temporal_parallel_num,
                                          int *temporal_parallel_denom) {
@@ -881,15 +903,6 @@
   return fail_id;
 }
 
-static INLINE int is_in_operating_point(int operating_point,
-                                        int temporal_layer_id,
-                                        int spatial_layer_id) {
-  if (!operating_point) return 1;
-
-  return ((operating_point >> temporal_layer_id) & 1) &&
-         ((operating_point >> (spatial_layer_id + 8)) & 1);
-}
-
 static void get_tile_stats(const AV1_COMP *const cpi, int *max_tile_size,
                            int *max_superres_tile_width,
                            int *min_cropped_tile_width,
@@ -1124,6 +1137,7 @@
     // Check whether target level is met.
     const AV1_LEVEL target_level = cpi->target_seq_level_idx[i];
     if (target_level < SEQ_LEVELS) {
+      assert(is_valid_seq_level_idx(target_level));
       const int tier = seq_params->tier[i];
       const TARGET_LEVEL_FAIL_ID fail_id = check_level_constraints(
           level_info, target_level, tier, is_still_picture, profile, 0);
@@ -1150,6 +1164,7 @@
     const AV1LevelInfo *const level_info = cpi->level_info[op];
     assert(level_info != NULL);
     for (int level = 0; level < SEQ_LEVELS; ++level) {
+      if (!is_valid_seq_level_idx(level)) continue;
       const TARGET_LEVEL_FAIL_ID fail_id = check_level_constraints(
           level_info, level, tier, is_still_picture, profile, 1);
       if (fail_id == TARGET_LEVEL_OK) {
diff --git a/av1/encoder/level.h b/av1/encoder/level.h
index e826554..72118ec 100644
--- a/av1/encoder/level.h
+++ b/av1/encoder/level.h
@@ -155,6 +155,15 @@
   DECODER_MODEL decoder_models[SEQ_LEVELS];
 } AV1LevelInfo;
 
+static INLINE int is_in_operating_point(int operating_point,
+                                        int temporal_layer_id,
+                                        int spatial_layer_id) {
+  if (!operating_point) return 1;
+
+  return ((operating_point >> temporal_layer_id) & 1) &&
+         ((operating_point >> (spatial_layer_id + 8)) & 1);
+}
+
 void av1_init_level_info(struct AV1_COMP *cpi);
 
 void av1_update_level_info(struct AV1_COMP *cpi, size_t size, int64_t ts_start,
@@ -174,4 +183,15 @@
                                      size_t coded_bits,
                                      DECODER_MODEL *const decoder_model);
 
+// Return max bitrate(bps) for given level.
+double av1_get_max_bitrate_for_level(AV1_LEVEL level_index, int tier,
+                                     BITSTREAM_PROFILE profile);
+
+// Get max number of tiles and tile columns for given level.
+void av1_get_max_tiles_for_level(AV1_LEVEL level_index, int *const max_tiles,
+                                 int *const max_tile_cols);
+
+// Return minimum compression ratio for given level.
+double av1_get_min_cr_for_level(AV1_LEVEL level_index, int tier,
+                                int is_still_picture);
 #endif  // AOM_AV1_ENCODER_LEVEL_H_
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index d8e5d81..82c9d1d 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -549,7 +549,7 @@
   return total_group_bits;
 }
 
-// Calculate the number bits extra to assign to boosted frames in a group.
+// Calculate the number of bits to assign to boosted frames in a group.
 static int calculate_boost_bits(int frame_count, int boost,
                                 int64_t total_group_bits) {
   int allocation_chunks;
@@ -571,6 +571,67 @@
                 0);
 }
 
+// Calculate the boost factor based on the number of bits assigned, i.e. the
+// inverse of calculate_boost_bits().
+static int calculate_boost_factor(int frame_count, int bits,
+                                  int64_t total_group_bits) {
+  aom_clear_system_state();
+  return (int)(100.0 * frame_count * bits / (total_group_bits - bits));
+}
+
+// Reduce the number of bits assigned to keyframe or arf if necessary, to
+// prevent bitrate spikes that may break level constraints.
+// frame_type: 0: keyframe; 1: arf.
+static int adjust_boost_bits_for_target_level(AV1_COMP *const cpi,
+                                              int bits_assigned, int group_bits,
+                                              int frame_type) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const SequenceHeader *const seq_params = &cm->seq_params;
+  const int temporal_layer_id = cm->temporal_layer_id;
+  const int spatial_layer_id = cm->spatial_layer_id;
+  for (int index = 0; index < seq_params->operating_points_cnt_minus_1 + 1;
+       ++index) {
+    if (!is_in_operating_point(seq_params->operating_point_idc[index],
+                               temporal_layer_id, spatial_layer_id)) {
+      continue;
+    }
+
+    const AV1_LEVEL target_level = cpi->target_seq_level_idx[index];
+    if (target_level >= SEQ_LEVELS) continue;
+
+    assert(is_valid_seq_level_idx(target_level));
+
+    const double level_bitrate_limit = av1_get_max_bitrate_for_level(
+        target_level, seq_params->tier[0], seq_params->profile);
+    const int target_bits_per_frame =
+        (int)(level_bitrate_limit / cpi->framerate);
+    RATE_CONTROL *const rc = &cpi->rc;
+    if (frame_type == 0) {
+      // Maximum bits for keyframe is 8 times the target_bits_per_frame.
+      const int level_enforced_max_kf_bits = target_bits_per_frame * 8;
+      if (bits_assigned > level_enforced_max_kf_bits) {
+        const int frames = rc->frames_to_key - 1;
+        rc->kf_boost = calculate_boost_factor(
+            frames, level_enforced_max_kf_bits, group_bits);
+        bits_assigned = calculate_boost_bits(frames, rc->kf_boost, group_bits);
+      }
+    } else if (frame_type == 1) {
+      // Maximum bits for arf is 4 times the target_bits_per_frame.
+      const int level_enforced_max_arf_bits = target_bits_per_frame * 4;
+      if (bits_assigned > level_enforced_max_arf_bits) {
+        rc->gfu_boost = calculate_boost_factor(
+            rc->baseline_gf_interval, level_enforced_max_arf_bits, group_bits);
+        bits_assigned = calculate_boost_bits(rc->baseline_gf_interval,
+                                             rc->gfu_boost, group_bits);
+      }
+    } else {
+      assert(0);
+    }
+  }
+
+  return bits_assigned;
+}
+
 static void allocate_gf_group_bits(
     GF_GROUP *gf_group, RATE_CONTROL *const rc, int64_t gf_group_bits,
     int gf_arf_bits, int max_bits,
@@ -1133,6 +1194,8 @@
   // Calculate the extra bits to be used for boosted frame(s)
   gf_arf_bits = calculate_boost_bits(rc->baseline_gf_interval, rc->gfu_boost,
                                      gf_group_bits);
+  gf_arf_bits =
+      adjust_boost_bits_for_target_level(cpi, gf_arf_bits, gf_group_bits, 1);
 
   // Adjust KF group bits and error remaining.
   twopass->kf_group_error_left -= (int64_t)gf_group_err;
@@ -1518,6 +1581,8 @@
                                  twopass->kf_group_bits);
   // printf("kf boost = %d kf_bits = %d kf_zeromotion_pct = %d\n", rc->kf_boost,
   //        kf_bits, twopass->kf_zeromotion_pct);
+  kf_bits = adjust_boost_bits_for_target_level(cpi, kf_bits,
+                                               twopass->kf_group_bits, 0);
 
   // Work out the fraction of the kf group bits reserved for the inter frames
   // within the group after discounting the bits for the kf itself.
diff --git a/test/invalid_file_test.cc b/test/invalid_file_test.cc
index adb9d4d..c21453c 100644
--- a/test/invalid_file_test.cc
+++ b/test/invalid_file_test.cc
@@ -128,9 +128,9 @@
   // { threads, filename, res_filename }
   { 1, "invalid-bug-1814.ivf", NULL },
   { 1, "invalid-chromium-906381.ivf", NULL },
-  { 4, "invalid-oss-fuzz-9463.ivf", NULL },
+  { 4, "invalid-oss-fuzz-9463.ivf", "invalid-oss-fuzz-9463.ivf.res.2" },
   { 1, "invalid-oss-fuzz-9720.ivf", NULL },
-  { 1, "invalid-oss-fuzz-10389.ivf", NULL },
+  { 1, "invalid-oss-fuzz-10389.ivf", "invalid-oss-fuzz-10389.ivf.res.2" },
   { 1, "invalid-oss-fuzz-11523.ivf", "invalid-oss-fuzz-11523.ivf.res.2" },
   { 4, "invalid-oss-fuzz-15363.ivf", NULL },
   { 1, "invalid-oss-fuzz-16437.ivf", NULL },
@@ -143,7 +143,7 @@
   { 1, "invalid-oss-fuzz-10227.ivf", NULL },
   { 4, "invalid-oss-fuzz-10555.ivf", NULL },
   { 1, "invalid-oss-fuzz-10705.ivf", NULL },
-  { 1, "invalid-oss-fuzz-10723.ivf", NULL },
+  { 1, "invalid-oss-fuzz-10723.ivf", "invalid-oss-fuzz-10723.ivf.res.2" },
   { 1, "invalid-oss-fuzz-10779.ivf", NULL },
   { 1, "invalid-oss-fuzz-11477.ivf", NULL },
   { 1, "invalid-oss-fuzz-11479.ivf", "invalid-oss-fuzz-11479.ivf.res.2" },
diff --git a/test/level_test.cc b/test/level_test.cc
index 03f5113..73e52e3 100644
--- a/test/level_test.cc
+++ b/test/level_test.cc
@@ -88,7 +88,10 @@
   for (int operating_point = 0; operating_point <= 32; ++operating_point) {
     for (int level = 0; level <= 32; ++level) {
       const int target_level = operating_point * 100 + level;
-      if ((level >= 0 && level <= 24) || level == 31 || operating_point > 31) {
+      if ((level <= 24 && level != 2 && level != 3 && level != 6 &&
+           level != 7 && level != 10 && level != 11 && level != 20 &&
+           level != 21 && level != 22 && level != 23) ||
+          level == 31 || operating_point > 31) {
         EXPECT_EQ(AOM_CODEC_OK,
                   aom_codec_control(&enc, AV1E_SET_TARGET_SEQ_LEVEL_IDX,
                                     target_level));
@@ -135,6 +138,19 @@
   }
 }
 
+TEST_P(LevelTest, TestTargetLevel0) {
+  // To save run time, we only test speed 4.
+  if (cpu_used_ == 4) {
+    libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
+                                       30, 1, 0, 50);
+    const int target_level = 0;
+    target_level_ = target_level;
+    cfg_.rc_target_bitrate = 4000;
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+    ASSERT_EQ(level_[0], target_level);
+  }
+}
+
 AV1_INSTANTIATE_TEST_CASE(LevelTest,
                           ::testing::Values(::libaom_test::kTwoPassGood),
                           ::testing::ValuesIn(kCpuUsedVectors));
diff --git a/test/test-data.sha1 b/test/test-data.sha1
index d162885..a3b99da 100644
--- a/test/test-data.sha1
+++ b/test/test-data.sha1
@@ -12,12 +12,14 @@
 b055f06b9a95aaa5697fa26497b592a47843a7c8 *invalid-oss-fuzz-10227.ivf.res
 b2d0a29a65879436bf483d04865faca7d11cc2ee *invalid-oss-fuzz-10389.ivf
 9655e6275888547ecd1f14e20e08ce4891372e76 *invalid-oss-fuzz-10389.ivf.res
+e5fe0e8984c42d53d4ff734c3fbfd57d5c5c25cf *invalid-oss-fuzz-10389.ivf.res.2
 11df8e9a068669c678097d460b63609d3da73828 *invalid-oss-fuzz-10555.ivf
 b055f06b9a95aaa5697fa26497b592a47843a7c8 *invalid-oss-fuzz-10555.ivf.res
 cf5945085fe85456a1f74bf4cc7998b88b3f4b62 *invalid-oss-fuzz-10705.ivf
 758671858368ffd2a2c0727898de5661f7cf7d68 *invalid-oss-fuzz-10705.ivf.res
 88e29851122cca3f336824f7fa4d9f757f91110c *invalid-oss-fuzz-10723.ivf
 1af486cd2cc83ebeddc76ca7a1c512cc0ec568d5 *invalid-oss-fuzz-10723.ivf.res
+64f8a208dec7f1580fbe0371aa15e62bb1262715 *invalid-oss-fuzz-10723.ivf.res.2
 0784acc8931090ec24eba752d6c27e359e68fe7d *invalid-oss-fuzz-10779.ivf
 5d9474c0309b7ca09a182d888f73b37a8fe1362c *invalid-oss-fuzz-10779.ivf.res
 7d37be9357f89a100ced694aee1ca5a6fad35ba9 *invalid-oss-fuzz-11477.ivf
@@ -36,6 +38,7 @@
 67c54283fe1a26ccf02cc991e4f9a1eea3ac5e78 *invalid-oss-fuzz-9288.ivf.res
 c0960f032484579f967881cc025b71cfd7a79ee1 *invalid-oss-fuzz-9463.ivf
 d3964f9dad9f60363c81b688324d95b4ec7c8038 *invalid-oss-fuzz-9463.ivf.res
+5d9474c0309b7ca09a182d888f73b37a8fe1362c *invalid-oss-fuzz-9463.ivf.res.2
 f448caf378e250b7eea4fa2d1c3cd7ef4a3211ce *invalid-oss-fuzz-9482.ivf
 b055f06b9a95aaa5697fa26497b592a47843a7c8 *invalid-oss-fuzz-9482.ivf.res
 a686989de79af89136f631fd630df639c7861851 *invalid-oss-fuzz-9720.ivf
diff --git a/test/test_data_util.cmake b/test/test_data_util.cmake
index 3206f59..d198517 100644
--- a/test/test_data_util.cmake
+++ b/test/test_data_util.cmake
@@ -529,12 +529,14 @@
               "invalid-oss-fuzz-10227.ivf.res"
               "invalid-oss-fuzz-10389.ivf"
               "invalid-oss-fuzz-10389.ivf.res"
+              "invalid-oss-fuzz-10389.ivf.res.2"
               "invalid-oss-fuzz-10555.ivf"
               "invalid-oss-fuzz-10555.ivf.res"
               "invalid-oss-fuzz-10705.ivf"
               "invalid-oss-fuzz-10705.ivf.res"
               "invalid-oss-fuzz-10723.ivf"
               "invalid-oss-fuzz-10723.ivf.res"
+              "invalid-oss-fuzz-10723.ivf.res.2"
               "invalid-oss-fuzz-10779.ivf"
               "invalid-oss-fuzz-10779.ivf.res"
               "invalid-oss-fuzz-11477.ivf"
@@ -553,6 +555,7 @@
               "invalid-oss-fuzz-9288.ivf.res"
               "invalid-oss-fuzz-9463.ivf"
               "invalid-oss-fuzz-9463.ivf.res"
+              "invalid-oss-fuzz-9463.ivf.res.2"
               "invalid-oss-fuzz-9482.ivf"
               "invalid-oss-fuzz-9482.ivf.res"
               "invalid-oss-fuzz-9720.ivf"