Add infrastructure to calculate level statistics

In preparation to calcualte and enforce level constraints add
infrastructure for this. Basic statistics and function where this
statistics will be calculated and enforced

BUG=aomedia:2332

Change-Id: I78b76054ae02d383dcabbad7fee792c47cc86f87
diff --git a/av1/av1.cmake b/av1/av1.cmake
index ca40d1e..1c9794b 100644
--- a/av1/av1.cmake
+++ b/av1/av1.cmake
@@ -160,6 +160,7 @@
             "${AOM_ROOT}/av1/encoder/hash_motion.h"
             "${AOM_ROOT}/av1/encoder/hybrid_fwd_txfm.c"
             "${AOM_ROOT}/av1/encoder/hybrid_fwd_txfm.h"
+            "${AOM_ROOT}/av1/encoder/level.c"
             "${AOM_ROOT}/av1/encoder/level.h"
             "${AOM_ROOT}/av1/encoder/lookahead.c"
             "${AOM_ROOT}/av1/encoder/lookahead.h"
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 6c68537..b013b6a 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2413,6 +2413,9 @@
   assert(IMPLIES(seq_params->profile <= PROFILE_1,
                  seq_params->bit_depth <= AOM_BITS_10));
 
+  cpi->target_seq_level_idx = (AV1_LEVEL)oxcf->target_seq_level_idx;
+  cpi->keep_level_stats = cpi->target_seq_level_idx != LEVEL_DISABLED;
+
   cm->timing_info_present = oxcf->timing_info_present;
   cm->timing_info.num_units_in_display_tick =
       oxcf->timing_info.num_units_in_display_tick;
@@ -5438,7 +5441,6 @@
   }
 }
 #endif  // CONFIG_INTERNAL_STATS
-
 int av1_get_compressed_data(AV1_COMP *cpi, unsigned int *frame_flags,
                             size_t *size, uint8_t *dest, int64_t *time_stamp,
                             int64_t *time_end, int flush,
@@ -5495,6 +5497,8 @@
       generate_psnr_packet(cpi);
     }
   }
+  if (cpi->keep_level_stats && oxcf->pass != 1 && !cm->show_existing_frame)
+    av1_update_level_info(cpi, *size, *time_stamp, *time_end);
 
 #if CONFIG_INTERNAL_STATS
   if (oxcf->pass != 1) {
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 6fa2433..2c84827 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -998,6 +998,9 @@
   // frame_component_time[] are initialized to zero at beginning of each frame.
   uint64_t frame_component_time[kTimingComponents];
 #endif
+  // level info and flags
+  AV1_LEVEL target_seq_level_idx;
+  int keep_level_stats;
   AV1LevelInfo level_info;
 } AV1_COMP;
 
diff --git a/av1/encoder/level.c b/av1/encoder/level.c
new file mode 100644
index 0000000..0f18d44
--- /dev/null
+++ b/av1/encoder/level.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2019, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <limits.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "config/aom_config.h"
+#include "config/aom_dsp_rtcd.h"
+#include "config/av1_rtcd.h"
+
+#include "av1/encoder/encoder.h"
+#include "av1/encoder/level.h"
+
+typedef enum {
+  LUMA_PIC_SIZE_TOO_LARGE,
+  LUMA_PIC_H_SIZE_TOO_LARGE,
+  LUMA_PIC_V_SIZE_TOO_LARGE,
+  TOO_MANY_TILE_COLUMNS,
+  TOO_MANY_TILES,
+
+  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.",
+  "Too many tiles are used.",
+};
+
+static void check_level_constraints(AV1_COMP *cpi,
+                                    const AV1LevelSpec *level_spec) {
+  const AV1_LEVEL target_seq_level_idx = cpi->target_seq_level_idx;
+  if (target_seq_level_idx > LEVEL_DISABLED &&
+      target_seq_level_idx < LEVEL_END) {
+    // Check level conformance
+    // TODO(kyslov@) implement all constraints
+    AV1_COMMON *const cm = &cpi->common;
+    const AV1LevelSpec *const target_level_spec =
+        av1_level_defs + target_seq_level_idx;
+    const int target_level_major = 2 + (target_seq_level_idx >> 2);
+    const int target_level_minor = target_seq_level_idx & 3;
+
+    if (level_spec->max_picture_size > target_level_spec->max_picture_size) {
+      aom_internal_error(&cm->error, AOM_CODEC_ERROR,
+                         "Failed to encode to the target level %d_%d. %s",
+                         target_level_major, target_level_minor,
+                         level_fail_messages[LUMA_PIC_SIZE_TOO_LARGE]);
+    }
+
+    if (level_spec->max_h_size > target_level_spec->max_h_size) {
+      aom_internal_error(&cm->error, AOM_CODEC_ERROR,
+                         "Failed to encode to the target level %d_%d. %s",
+                         target_level_major, target_level_minor,
+                         level_fail_messages[LUMA_PIC_H_SIZE_TOO_LARGE]);
+    }
+
+    if (level_spec->max_v_size > target_level_spec->max_v_size) {
+      aom_internal_error(&cm->error, AOM_CODEC_ERROR,
+                         "Failed to encode to the target level %d_%d. %s",
+                         target_level_major, target_level_minor,
+                         level_fail_messages[LUMA_PIC_V_SIZE_TOO_LARGE]);
+    }
+
+    if (level_spec->max_tile_cols > target_level_spec->max_tile_cols) {
+      aom_internal_error(&cm->error, AOM_CODEC_ERROR,
+                         "Failed to encode to the target level %d_%d. %s",
+                         target_level_major, target_level_minor,
+                         level_fail_messages[TOO_MANY_TILE_COLUMNS]);
+    }
+
+    if (level_spec->max_tiles > target_level_spec->max_tiles) {
+      aom_internal_error(&cm->error, AOM_CODEC_ERROR,
+                         "Failed to encode to the target level %d_%d. %s",
+                         target_level_major, target_level_minor,
+                         level_fail_messages[TOO_MANY_TILES]);
+    }
+  }
+}
+
+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;
+  AV1LevelInfo *const level_info = &cpi->level_info;
+  AV1LevelSpec *const level_spec = &level_info->level_spec;
+  AV1LevelStats *const level_stats = &level_info->level_stats;
+
+  const uint32_t luma_pic_size = cm->superres_upscaled_width * cm->height;
+  const uint32_t 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;
+  const double compression_ratio =
+      frame_uncompressed_size / (double)frame_compressed_size;
+
+  // update level_stats
+  // TODO(kyslov@) fix the implementation according to buffer model
+  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;
+  }
+
+  // update level_spec
+  // TODO(kyslov@) update all spec fields
+  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);
+  }
+
+  // TODO(kyslov@) These are needed for further level stat calculations
+  (void)compression_ratio;
+  (void)ts_start;
+  (void)ts_end;
+
+  check_level_constraints(cpi, level_spec);
+}
diff --git a/av1/encoder/level.h b/av1/encoder/level.h
index ce08236..c5b7c84 100644
--- a/av1/encoder/level.h
+++ b/av1/encoder/level.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2019, Alliance for Open Media. All rights reserved
  *
@@ -14,6 +13,8 @@
 #define AOM_AV1_ENCODER_LEVEL_H_
 #include "av1/common/enums.h"
 
+struct AV1_COMP;
+
 #define UNDEFINED_LEVEL                                                 \
   {                                                                     \
     .level = LEVEL_DISABLED, .max_picture_size = 0, .max_h_size = 0,    \
@@ -74,8 +75,8 @@
 
 // Used to keep track of AV1 Level Stats. Currently unimplemented.
 typedef struct {
-  // Unimplemented
-  uint32_t dummy;
+  uint64_t total_compressed_size;
+  double total_time_encoded;
 } AV1LevelStats;
 
 typedef struct {
@@ -278,4 +279,7 @@
   UNDEFINED_LEVEL,
 };
 
+void av1_update_level_info(struct AV1_COMP *cpi, size_t size, int64_t ts_start,
+                           int64_t ts_end);
+
 #endif  // AOM_AV1_ENCODER_LEVEL_H_