AV1 levels: add tile related metrics

Added metrics:
max_tile_size
min_cropped_tile_width
min_cropped_tile_height
tile_width_is_valid

BUG=aomedia:2332

Change-Id: Ie1c92af5b1690a9df673fb4bf3d5d1657f32715c
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index c0a33df..d138cd6 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2550,8 +2550,13 @@
 
 static void init_level_info(AV1LevelInfo *level_info) {
   memset(level_info, 0, MAX_NUM_OPERATING_POINTS * sizeof(*level_info));
-  for (int i = 0; i < MAX_NUM_OPERATING_POINTS; ++i)
-    level_info[i].level_spec.level = SEQ_LEVEL_MAX;
+  for (int i = 0; i < MAX_NUM_OPERATING_POINTS; ++i) {
+    AV1LevelSpec *level_spec = &level_info[i].level_spec;
+    level_spec->level = SEQ_LEVEL_MAX;
+    level_spec->min_cropped_tile_width = INT_MAX;
+    level_spec->min_cropped_tile_height = INT_MAX;
+    level_spec->tile_width_is_valid = 1;
+  }
 }
 
 AV1_COMP *av1_create_compressor(AV1EncoderConfig *oxcf,
diff --git a/av1/encoder/level.c b/av1/encoder/level.c
index 0e8b34b..02bc112 100644
--- a/av1/encoder/level.c
+++ b/av1/encoder/level.c
@@ -9,6 +9,8 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 
+#include "aom_ports/system_state.h"
+
 #include "av1/encoder/encoder.h"
 #include "av1/encoder/level.h"
 
@@ -221,14 +223,24 @@
   LUMA_PIC_V_SIZE_TOO_LARGE,
   TOO_MANY_TILE_COLUMNS,
   TOO_MANY_TILES,
+  TILE_TOO_LARGE,
+  CROPPED_TILE_WIDTH_TOO_SMALL,
+  CROPPED_TILE_HEIGHT_TOO_SMALL,
+  TILE_WIDTH_INVALID,
 
   TARGET_LEVEL_FAIL_IDS
 } TARGET_LEVEL_FAIL_ID;
 
 static const char *level_fail_messages[TARGET_LEVEL_FAIL_IDS] = {
-  "The picture size is too large.",   "The picture width is too large.",
-  "The picture height is too large.", "Too many tile columns are used.",
+  "The picture size is too large.",
+  "The picture width is too large.",
+  "The picture height is too large.",
+  "Too many tile columns are used.",
   "Too many tiles are used.",
+  "The tile size is too large.",
+  "The cropped tile width is less than 8",
+  "The cropped tile height is less than 8",
+  "The tile width is invalid",
 };
 
 static void check_level_constraints(AV1_COMP *cpi, int operating_point_idx,
@@ -266,6 +278,26 @@
       fail_id = TOO_MANY_TILES;
       break;
     }
+
+    if (level_spec->max_tile_size > 4096 * 2304) {
+      fail_id = TILE_TOO_LARGE;
+      break;
+    }
+
+    if (level_spec->min_cropped_tile_width < 8) {
+      fail_id = CROPPED_TILE_WIDTH_TOO_SMALL;
+      break;
+    }
+
+    if (level_spec->min_cropped_tile_height < 8) {
+      fail_id = CROPPED_TILE_HEIGHT_TOO_SMALL;
+      break;
+    }
+
+    if (!level_spec->tile_width_is_valid) {
+      fail_id = TILE_WIDTH_INVALID;
+      break;
+    }
   } while (0);
 
   if (fail_id != TARGET_LEVEL_FAIL_IDS) {
@@ -288,23 +320,86 @@
          ((operating_point >> (spatial_layer_id + 8)) & 1);
 }
 
+static void get_tile_stats(const AV1_COMP *const cpi, int *max_tile_size,
+                           int *min_cropped_tile_width,
+                           int *min_cropped_tile_height,
+                           int *tile_width_valid) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int tile_cols = cm->tile_cols;
+  const int tile_rows = cm->tile_rows;
+
+  *max_tile_size = 0;
+  *min_cropped_tile_width = INT_MAX;
+  *min_cropped_tile_height = INT_MAX;
+  *tile_width_valid = 1;
+
+  for (int tile_row = 0; tile_row < tile_rows; ++tile_row) {
+    for (int tile_col = 0; tile_col < tile_cols; ++tile_col) {
+      const TileInfo *const tile_info =
+          &cpi->tile_data[tile_row * cm->tile_cols + tile_col].tile_info;
+      const int tile_width =
+          (tile_info->mi_col_end - tile_info->mi_col_start) * MI_SIZE;
+      const int tile_height =
+          (tile_info->mi_row_end - tile_info->mi_row_start) * MI_SIZE;
+      const int tile_size = tile_width * tile_height;
+      *max_tile_size = AOMMAX(*max_tile_size, tile_size);
+
+      const int cropped_tile_width =
+          cm->width - tile_info->mi_col_start * MI_SIZE;
+      const int cropped_tile_height =
+          cm->height - tile_info->mi_row_start * MI_SIZE;
+      *min_cropped_tile_width =
+          AOMMIN(*min_cropped_tile_width, cropped_tile_width);
+      *min_cropped_tile_height =
+          AOMMIN(*min_cropped_tile_height, cropped_tile_height);
+
+      const int is_right_most_tile = tile_info->mi_col_end == cm->mi_cols;
+      if (!is_right_most_tile) {
+        if (av1_superres_scaled(cm))
+          *tile_width_valid &= tile_width >= 128;
+        else
+          *tile_width_valid &= tile_width >= 64;
+      }
+    }
+  }
+}
+
 void av1_update_level_info(AV1_COMP *cpi, size_t size, int64_t ts_start,
                            int64_t ts_end) {
   const AV1_COMMON *const cm = &cpi->common;
-  const uint32_t luma_pic_size = cm->superres_upscaled_width * cm->height;
-  const uint32_t pic_size_profile_factor =
+  const int upscaled_width = cm->superres_upscaled_width;
+  const int height = cm->height;
+  const int tile_cols = cm->tile_cols;
+  const int tile_rows = cm->tile_rows;
+  const int tiles = tile_cols * tile_rows;
+  const int luma_pic_size = upscaled_width * height;
+
+  int max_tile_size;
+  int min_cropped_tile_width;
+  int min_cropped_tile_height;
+  int tile_width_is_valid;
+  get_tile_stats(cpi, &max_tile_size, &min_cropped_tile_width,
+                 &min_cropped_tile_height, &tile_width_is_valid);
+
+  const int pic_size_profile_factor =
       cm->seq_params.profile == PROFILE_0
           ? 15
           : (cm->seq_params.profile == PROFILE_1 ? 30 : 36);
   const size_t frame_compressed_size = (size > 129 ? size - 128 : 1);
   const size_t frame_uncompressed_size =
       (luma_pic_size * pic_size_profile_factor) >> 3;
+
+  aom_clear_system_state();
   const double compression_ratio =
       frame_uncompressed_size / (double)frame_compressed_size;
+  const double total_time_encoded =
+      (cpi->last_end_time_stamp_seen - cpi->first_time_stamp_ever) /
+      (double)TICKS_PER_SEC;
 
   const SequenceHeader *const seq = &cm->seq_params;
   const int temporal_layer_id = cm->temporal_layer_id;
   const int spatial_layer_id = cm->spatial_layer_id;
+
   // update level_stats
   // TODO(kyslov@) fix the implementation according to buffer model
   for (int i = 0; i < seq->operating_points_cnt_minus_1 + 1; ++i) {
@@ -315,37 +410,27 @@
 
     AV1LevelInfo *const level_info = &cpi->level_info[i];
     AV1LevelStats *const level_stats = &level_info->level_stats;
+
     level_stats->total_compressed_size += frame_compressed_size;
-    if (cm->show_frame) {
-      level_stats->total_time_encoded =
-          (cpi->last_end_time_stamp_seen - cpi->first_time_stamp_ever) /
-          (double)TICKS_PER_SEC;
-    }
+    if (cm->show_frame) level_stats->total_time_encoded = total_time_encoded;
 
     // update level_spec
     // TODO(kyslov@) update all spec fields
     AV1LevelSpec *const level_spec = &level_info->level_spec;
-    if (luma_pic_size > level_spec->max_picture_size) {
-      level_spec->max_picture_size = luma_pic_size;
-    }
-
-    if (cm->superres_upscaled_width > (int)level_spec->max_h_size) {
-      level_spec->max_h_size = cm->superres_upscaled_width;
-    }
-
-    if (cm->height > (int)level_spec->max_v_size) {
-      level_spec->max_v_size = cm->height;
-    }
-
-    if (level_spec->max_tile_cols < (1 << cm->log2_tile_cols)) {
-      level_spec->max_tile_cols = (1 << cm->log2_tile_cols);
-    }
-
-    if (level_spec->max_tiles <
-        (1 << cm->log2_tile_cols) * (1 << cm->log2_tile_rows)) {
-      level_spec->max_tiles =
-          (1 << cm->log2_tile_cols) * (1 << cm->log2_tile_rows);
-    }
+    level_spec->max_picture_size =
+        AOMMAX(level_spec->max_picture_size, luma_pic_size);
+    level_spec->max_h_size =
+        AOMMAX(level_spec->max_h_size, cm->superres_upscaled_width);
+    level_spec->max_v_size = AOMMAX(level_spec->max_v_size, height);
+    level_spec->max_tile_cols = AOMMAX(level_spec->max_tile_cols, tile_cols);
+    level_spec->max_tiles = AOMMAX(level_spec->max_tiles, tiles);
+    level_spec->max_tile_size =
+        AOMMAX(level_spec->max_tile_size, max_tile_size);
+    level_spec->min_cropped_tile_width =
+        AOMMIN(level_spec->min_cropped_tile_width, min_cropped_tile_width);
+    level_spec->min_cropped_tile_height =
+        AOMMIN(level_spec->min_cropped_tile_height, min_cropped_tile_height);
+    level_spec->tile_width_is_valid &= tile_width_is_valid;
 
     // TODO(kyslov@) These are needed for further level stat calculations
     (void)compression_ratio;
diff --git a/av1/encoder/level.h b/av1/encoder/level.h
index 32c7088..18f0de5 100644
--- a/av1/encoder/level.h
+++ b/av1/encoder/level.h
@@ -19,18 +19,22 @@
 // AV1 Level Specifications
 typedef struct {
   AV1_LEVEL level;
-  uint32_t max_picture_size;
-  uint32_t max_h_size;
-  uint32_t max_v_size;
-  uint64_t max_display_rate;
-  uint64_t max_decode_rate;
-  uint32_t max_header_rate;
+  int max_picture_size;
+  int max_h_size;
+  int max_v_size;
+  int max_header_rate;
+  int max_tiles;
+  int max_tile_cols;
+  int max_tile_size;
+  int min_cropped_tile_width;
+  int min_cropped_tile_height;
+  int tile_width_is_valid;
+  int64_t max_display_rate;
+  int64_t max_decode_rate;
   double main_mbps;
   double high_mbps;
   double main_cr;
   double high_cr;
-  int32_t max_tiles;
-  int32_t max_tile_cols;
 } AV1LevelSpec;
 
 // Used to keep track of AV1 Level Stats. Currently unimplemented.