AV1 levels: add header, display and decode rate

BUG=aomedia:2332

Change-Id: Ic8fd9c447c9d210864bb18f006458d68dfc25bca
diff --git a/av1/encoder/level.c b/av1/encoder/level.c
index 4226a5c..911924f 100644
--- a/av1/encoder/level.c
+++ b/av1/encoder/level.c
@@ -227,6 +227,9 @@
   CROPPED_TILE_WIDTH_TOO_SMALL,
   CROPPED_TILE_HEIGHT_TOO_SMALL,
   TILE_WIDTH_INVALID,
+  FRAME_HEADER_RATE_TOO_HIGH,
+  DISPLAY_RATE_TOO_HIGH,
+  DECODE_RATE_TOO_HIGH,
 
   TARGET_LEVEL_FAIL_IDS
 } TARGET_LEVEL_FAIL_ID;
@@ -241,6 +244,9 @@
   "The cropped tile width is less than 8",
   "The cropped tile height is less than 8",
   "The tile width is invalid",
+  "The frame header rate is too high",
+  "The display luma sample rate is too high",
+  "The decoded luma sample rate is too high",
 };
 
 static void check_level_constraints(AV1_COMP *cpi, int operating_point_idx,
@@ -298,6 +304,21 @@
       fail_id = TILE_WIDTH_INVALID;
       break;
     }
+
+    if (level_spec->max_header_rate > target_level_spec->max_header_rate) {
+      fail_id = FRAME_HEADER_RATE_TOO_HIGH;
+      break;
+    }
+
+    if (level_spec->max_display_rate > target_level_spec->max_display_rate) {
+      fail_id = DISPLAY_RATE_TOO_HIGH;
+      break;
+    }
+
+    if (level_spec->max_decode_rate > target_level_spec->max_decode_rate) {
+      fail_id = DECODE_RATE_TOO_HIGH;
+      break;
+    }
   } while (0);
 
   if (fail_id != TARGET_LEVEL_FAIL_IDS) {
@@ -408,6 +429,35 @@
   return num_frames;
 }
 
+// Scan previously encoded frames and update level metrics accordingly.
+static void scan_past_frames(const FrameWindowBuffer *const buffer,
+                             int num_frames_to_scan,
+                             AV1LevelSpec *const level_spec) {
+  const int num_frames_in_buffer = buffer->num;
+  int index = (buffer->start + num_frames_in_buffer - 1) % FRAME_WINDOW_SIZE;
+  int frame_headers = 0;
+  int64_t display_samples = 0;
+  int64_t decoded_samples = 0;
+  for (int i = 0; i < AOMMIN(num_frames_in_buffer, num_frames_to_scan); ++i) {
+    const FrameRecord *const record = &buffer->buf[index];
+    if (!record->show_existing_frame) {
+      frame_headers += record->frame_header_count;
+      decoded_samples += record->pic_size;
+    }
+    if (record->show_frame) {
+      display_samples += record->pic_size;
+    }
+    --index;
+    if (index < 0) index = FRAME_WINDOW_SIZE - 1;
+  }
+  level_spec->max_header_rate =
+      AOMMAX(level_spec->max_header_rate, frame_headers);
+  level_spec->max_display_rate =
+      AOMMAX(level_spec->max_display_rate, display_samples);
+  level_spec->max_decode_rate =
+      AOMMAX(level_spec->max_decode_rate, decoded_samples);
+}
+
 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;
@@ -428,9 +478,6 @@
   // Count the number of frames encoded in the past 1 second.
   const int encoded_frames_in_last_second =
       show_frame ? count_frames(buffer, TICKS_PER_SEC) : 0;
-  // TODO(huisu@): calculate stats using the info. of the
-  // encoded_frames_in_last_sescond frames in frame_window_buffer.
-  (void)encoded_frames_in_last_second;
 
   int max_tile_size;
   int min_cropped_tile_width;
@@ -489,6 +536,10 @@
         AOMMIN(level_spec->min_cropped_tile_height, min_cropped_tile_height);
     level_spec->tile_width_is_valid &= tile_width_is_valid;
 
+    if (show_frame) {
+      scan_past_frames(buffer, encoded_frames_in_last_second, level_spec);
+    }
+
     // TODO(kyslov@) These are needed for further level stat calculations
     (void)compression_ratio;
     (void)ts_start;