Clean up Stats class used in VideoProcessor tests.

* Reorder and rename members of FrameStatistic.
* Rename corresponding temp variables used in the summary calculations.
* Make Stats class less structy.
* Try to enforce consistent frame numbers at all times.
* Remove VideoProcessor::FrameInfo struct and use the Stats class
  for keeping track of encode/decode start times, etc.

BUG=webrtc:6634

Review-Url: https://codereview.webrtc.org/3011923002
Cr-Original-Commit-Position: refs/heads/master@{#19703}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 8935d97f169d0c85ea04d2f370964f283783cd79
diff --git a/modules/video_coding/codecs/test/stats.cc b/modules/video_coding/codecs/test/stats.cc
index 649c0d5..b2950ae 100644
--- a/modules/video_coding/codecs/test/stats.cc
+++ b/modules/video_coding/codecs/test/stats.cc
@@ -12,88 +12,93 @@
 
 #include <stdio.h>
 
-#include <algorithm>  // min_element, max_element
+#include <algorithm>
 
 #include "webrtc/rtc_base/checks.h"
 #include "webrtc/rtc_base/format_macros.h"
 
 namespace webrtc {
 namespace test {
+
 namespace {
+
 bool LessForEncodeTime(const FrameStatistic& s1, const FrameStatistic& s2) {
-  return s1.encode_time_in_us < s2.encode_time_in_us;
+  RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
+  return s1.encode_time_us < s2.encode_time_us;
 }
 
 bool LessForDecodeTime(const FrameStatistic& s1, const FrameStatistic& s2) {
-  return s1.decode_time_in_us < s2.decode_time_in_us;
+  RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
+  return s1.decode_time_us < s2.decode_time_us;
 }
 
 bool LessForEncodedSize(const FrameStatistic& s1, const FrameStatistic& s2) {
-  return s1.encoded_frame_length_in_bytes < s2.encoded_frame_length_in_bytes;
+  RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
+  return s1.encoded_frame_size_bytes < s2.encoded_frame_size_bytes;
 }
 
 bool LessForBitRate(const FrameStatistic& s1, const FrameStatistic& s2) {
-  return s1.bit_rate_in_kbps < s2.bit_rate_in_kbps;
+  RTC_DCHECK_NE(s1.frame_number, s2.frame_number);
+  return s1.bitrate_kbps < s2.bitrate_kbps;
 }
+
 }  // namespace
 
-Stats::Stats() {}
-
-Stats::~Stats() {}
-
-FrameStatistic& Stats::NewFrame(int frame_number) {
-  RTC_DCHECK_GE(frame_number, 0);
-  FrameStatistic stat;
-  stat.frame_number = frame_number;
-  stats_.push_back(stat);
-  return stats_[frame_number];
+FrameStatistic* Stats::AddFrame() {
+  // We don't expect more frames than what can be stored in an int.
+  stats_.emplace_back(static_cast<int>(stats_.size()));
+  return &stats_.back();
 }
 
-void Stats::PrintSummary() {
+FrameStatistic* Stats::GetFrame(int frame_number) {
+  RTC_CHECK_GE(frame_number, 0);
+  RTC_CHECK_LT(frame_number, stats_.size());
+  return &stats_[frame_number];
+}
+
+size_t Stats::size() const {
+  return stats_.size();
+}
+
+void Stats::PrintSummary() const {
   if (stats_.empty()) {
     printf("No frame statistics have been logged yet.\n");
     return;
   }
 
   // Calculate min, max, average and total encoding time.
-  int total_encoding_time_in_us = 0;
-  int total_decoding_time_in_us = 0;
-  int total_qp = 0;
-  int total_qp_count = 0;
-  size_t total_encoded_frames_lengths = 0;
-  size_t total_encoded_key_frames_lengths = 0;
-  size_t total_encoded_nonkey_frames_lengths = 0;
-  size_t num_keyframes = 0;
-  size_t num_nonkeyframes = 0;
+  int total_encoding_time_us = 0;
+  int total_decoding_time_us = 0;
+  size_t total_encoded_frame_size_bytes = 0;
+  size_t total_encoded_key_frame_size_bytes = 0;
+  size_t total_encoded_delta_frame_size_bytes = 0;
+  size_t num_key_frames = 0;
+  size_t num_delta_frames = 0;
 
   for (const FrameStatistic& stat : stats_) {
-    total_encoding_time_in_us += stat.encode_time_in_us;
-    total_decoding_time_in_us += stat.decode_time_in_us;
-    total_encoded_frames_lengths += stat.encoded_frame_length_in_bytes;
+    total_encoding_time_us += stat.encode_time_us;
+    total_decoding_time_us += stat.decode_time_us;
+    total_encoded_frame_size_bytes += stat.encoded_frame_size_bytes;
     if (stat.frame_type == webrtc::kVideoFrameKey) {
-      total_encoded_key_frames_lengths += stat.encoded_frame_length_in_bytes;
-      ++num_keyframes;
+      total_encoded_key_frame_size_bytes += stat.encoded_frame_size_bytes;
+      ++num_key_frames;
     } else {
-      total_encoded_nonkey_frames_lengths += stat.encoded_frame_length_in_bytes;
-      ++num_nonkeyframes;
-    }
-    if (stat.qp >= 0) {
-      total_qp += stat.qp;
-      ++total_qp_count;
+      total_encoded_delta_frame_size_bytes += stat.encoded_frame_size_bytes;
+      ++num_delta_frames;
     }
   }
 
   // Encoding stats.
   printf("Encoding time:\n");
-  FrameStatisticsIterator frame;
-  frame = std::min_element(stats_.begin(), stats_.end(), LessForEncodeTime);
-  printf("  Min     : %7d us (frame %d)\n", frame->encode_time_in_us,
-         frame->frame_number);
-  frame = std::max_element(stats_.begin(), stats_.end(), LessForEncodeTime);
-  printf("  Max     : %7d us (frame %d)\n", frame->encode_time_in_us,
-         frame->frame_number);
+  auto frame_it =
+      std::min_element(stats_.begin(), stats_.end(), LessForEncodeTime);
+  printf("  Min     : %7d us (frame %d)\n", frame_it->encode_time_us,
+         frame_it->frame_number);
+  frame_it = std::max_element(stats_.begin(), stats_.end(), LessForEncodeTime);
+  printf("  Max     : %7d us (frame %d)\n", frame_it->encode_time_us,
+         frame_it->frame_number);
   printf("  Average : %7d us\n",
-         static_cast<int>(total_encoding_time_in_us / stats_.size()));
+         static_cast<int>(total_encoding_time_us / stats_.size()));
 
   // Decoding stats.
   printf("Decoding time:\n");
@@ -107,57 +112,66 @@
   if (decoded_frames.empty()) {
     printf("No successfully decoded frames exist in this statistics.\n");
   } else {
-    frame = std::min_element(decoded_frames.begin(), decoded_frames.end(),
-                             LessForDecodeTime);
-    printf("  Min     : %7d us (frame %d)\n", frame->decode_time_in_us,
-           frame->frame_number);
-    frame = std::max_element(decoded_frames.begin(), decoded_frames.end(),
-                             LessForDecodeTime);
-    printf("  Max     : %7d us (frame %d)\n", frame->decode_time_in_us,
-           frame->frame_number);
+    frame_it = std::min_element(decoded_frames.begin(), decoded_frames.end(),
+                                LessForDecodeTime);
+    printf("  Min     : %7d us (frame %d)\n", frame_it->decode_time_us,
+           frame_it->frame_number);
+    frame_it = std::max_element(decoded_frames.begin(), decoded_frames.end(),
+                                LessForDecodeTime);
+    printf("  Max     : %7d us (frame %d)\n", frame_it->decode_time_us,
+           frame_it->frame_number);
     printf("  Average : %7d us\n",
-           static_cast<int>(total_decoding_time_in_us / decoded_frames.size()));
+           static_cast<int>(total_decoding_time_us / decoded_frames.size()));
     printf("  Failures: %d frames failed to decode.\n",
            static_cast<int>(stats_.size() - decoded_frames.size()));
   }
 
   // Frame size stats.
   printf("Frame sizes:\n");
-  frame = std::min_element(stats_.begin(), stats_.end(), LessForEncodedSize);
+  frame_it = std::min_element(stats_.begin(), stats_.end(), LessForEncodedSize);
   printf("  Min     : %7" PRIuS " bytes (frame %d)\n",
-         frame->encoded_frame_length_in_bytes, frame->frame_number);
-  frame = std::max_element(stats_.begin(), stats_.end(), LessForEncodedSize);
+         frame_it->encoded_frame_size_bytes, frame_it->frame_number);
+  frame_it = std::max_element(stats_.begin(), stats_.end(), LessForEncodedSize);
   printf("  Max     : %7" PRIuS " bytes (frame %d)\n",
-         frame->encoded_frame_length_in_bytes, frame->frame_number);
+         frame_it->encoded_frame_size_bytes, frame_it->frame_number);
   printf("  Average : %7" PRIuS " bytes\n",
-         total_encoded_frames_lengths / stats_.size());
-  if (num_keyframes > 0) {
+         total_encoded_frame_size_bytes / stats_.size());
+  if (num_key_frames > 0) {
     printf("  Average key frame size    : %7" PRIuS " bytes (%" PRIuS
            " keyframes)\n",
-           total_encoded_key_frames_lengths / num_keyframes, num_keyframes);
+           total_encoded_key_frame_size_bytes / num_key_frames, num_key_frames);
   }
-  if (num_nonkeyframes > 0) {
+  if (num_delta_frames > 0) {
     printf("  Average non-key frame size: %7" PRIuS " bytes (%" PRIuS
            " frames)\n",
-           total_encoded_nonkey_frames_lengths / num_nonkeyframes,
-           num_nonkeyframes);
+           total_encoded_delta_frame_size_bytes / num_delta_frames,
+           num_delta_frames);
   }
 
   // Bitrate stats.
   printf("Bitrates:\n");
-  frame = std::min_element(stats_.begin(), stats_.end(), LessForBitRate);
-  printf("  Min bitrate: %7d kbps (frame %d)\n", frame->bit_rate_in_kbps,
-         frame->frame_number);
-  frame = std::max_element(stats_.begin(), stats_.end(), LessForBitRate);
-  printf("  Max bitrate: %7d kbps (frame %d)\n", frame->bit_rate_in_kbps,
-         frame->frame_number);
+  frame_it = std::min_element(stats_.begin(), stats_.end(), LessForBitRate);
+  printf("  Min bitrate: %7d kbps (frame %d)\n", frame_it->bitrate_kbps,
+         frame_it->frame_number);
+  frame_it = std::max_element(stats_.begin(), stats_.end(), LessForBitRate);
+  printf("  Max bitrate: %7d kbps (frame %d)\n", frame_it->bitrate_kbps,
+         frame_it->frame_number);
 
   printf("\n");
-  printf("Total encoding time  : %7d ms.\n", total_encoding_time_in_us / 1000);
-  printf("Total decoding time  : %7d ms.\n", total_decoding_time_in_us / 1000);
+  printf("Total encoding time  : %7d ms.\n", total_encoding_time_us / 1000);
+  printf("Total decoding time  : %7d ms.\n", total_decoding_time_us / 1000);
   printf("Total processing time: %7d ms.\n",
-         (total_encoding_time_in_us + total_decoding_time_in_us) / 1000);
+         (total_encoding_time_us + total_decoding_time_us) / 1000);
 
+  // QP stats.
+  int total_qp = 0;
+  int total_qp_count = 0;
+  for (const FrameStatistic& stat : stats_) {
+    if (stat.qp >= 0) {
+      total_qp += stat.qp;
+      ++total_qp_count;
+    }
+  }
   int avg_qp = (total_qp_count > 0) ? (total_qp / total_qp_count) : -1;
   printf("Average QP: %d\n", avg_qp);
 }
diff --git a/modules/video_coding/codecs/test/stats.h b/modules/video_coding/codecs/test/stats.h
index 1a9b187..a0bd112 100644
--- a/modules/video_coding/codecs/test/stats.h
+++ b/modules/video_coding/codecs/test/stats.h
@@ -18,48 +18,55 @@
 namespace webrtc {
 namespace test {
 
-// Contains statistics of a single processed frame.
+// Statistics for one processed frame.
 struct FrameStatistic {
-  bool encoding_successful = false;
-  bool decoding_successful = false;
+  explicit FrameStatistic(int frame_number) : frame_number(frame_number) {}
+  const int frame_number = 0;
+
+  // Encoding.
+  int64_t encode_start_ns = 0;
   int encode_return_code = 0;
+  bool encoding_successful = false;
+  int encode_time_us = 0;
+  int bitrate_kbps = 0;
+  size_t encoded_frame_size_bytes = 0;
+  webrtc::FrameType frame_type = kVideoFrameDelta;
+
+  // Decoding.
+  int64_t decode_start_ns = 0;
   int decode_return_code = 0;
-  int encode_time_in_us = 0;
-  int decode_time_in_us = 0;
+  bool decoding_successful = false;
+  int decode_time_us = 0;
+  int decoded_width = 0;
+  int decoded_height = 0;
+
+  // Quantization.
   int qp = -1;
-  int frame_number = 0;
+
   // How many packets were discarded of the encoded frame data (if any).
   int packets_dropped = 0;
   size_t total_packets = 0;
-
-  // Current bit rate. Calculated out of the size divided with the time
-  // interval per frame.
-  int bit_rate_in_kbps = 0;
-
-  // Copied from EncodedImage.
-  size_t encoded_frame_length_in_bytes = 0;
-  webrtc::FrameType frame_type = kVideoFrameDelta;
+  size_t manipulated_length = 0;
 };
 
-// Handles statistics from a single video processing run.
-// Contains calculation methods for interesting metrics from these stats.
+// Statistics for a sequence of processed frames. This class is not thread safe.
 class Stats {
  public:
-  typedef std::vector<FrameStatistic>::iterator FrameStatisticsIterator;
+  Stats() = default;
+  ~Stats() = default;
 
-  Stats();
-  virtual ~Stats();
+  // Creates a FrameStatistic for the next frame to be processed.
+  FrameStatistic* AddFrame();
 
-  // Add a new statistic data object.
-  // The |frame_number| must be incrementing and start at zero in order to use
-  // it as an index for the FrameStatistic vector.
-  // Returns the newly created statistic object.
-  FrameStatistic& NewFrame(int frame_number);
+  // Returns the FrameStatistic corresponding to |frame_number|.
+  FrameStatistic* GetFrame(int frame_number);
 
-  // Prints a summary of all the statistics that have been gathered during the
-  // processing.
-  void PrintSummary();
+  size_t size() const;
 
+  // TODO(brandtr): Add output as CSV.
+  void PrintSummary() const;
+
+ private:
   std::vector<FrameStatistic> stats_;
 };
 
diff --git a/modules/video_coding/codecs/test/stats_unittest.cc b/modules/video_coding/codecs/test/stats_unittest.cc
index 55bdfaf..24fe265 100644
--- a/modules/video_coding/codecs/test/stats_unittest.cc
+++ b/modules/video_coding/codecs/test/stats_unittest.cc
@@ -11,36 +11,32 @@
 #include "webrtc/modules/video_coding/codecs/test/stats.h"
 
 #include "webrtc/test/gtest.h"
-#include "webrtc/typedefs.h"
 
 namespace webrtc {
 namespace test {
 
 TEST(StatsTest, TestEmptyObject) {
   Stats stats;
-  EXPECT_EQ(0u, stats.stats_.size());
-  stats.PrintSummary();  // should not crash
+  stats.PrintSummary();  // Should not crash.
 }
 
 TEST(StatsTest, AddSingleFrame) {
-  const int kFrameNumber = 0;
   Stats stats;
-  stats.NewFrame(kFrameNumber);
-  EXPECT_EQ(1u, stats.stats_.size());
-  FrameStatistic* frame_stat = &stats.stats_[0];
-  EXPECT_EQ(kFrameNumber, frame_stat->frame_number);
+  FrameStatistic* frame_stat = stats.AddFrame();
+  EXPECT_EQ(0, frame_stat->frame_number);
+  EXPECT_EQ(1u, stats.size());
 }
 
 TEST(StatsTest, AddMultipleFrames) {
   Stats stats;
   const int kNumFrames = 1000;
   for (int i = 0; i < kNumFrames; ++i) {
-    FrameStatistic& frame_stat = stats.NewFrame(i);
-    EXPECT_EQ(i, frame_stat.frame_number);
+    FrameStatistic* frame_stat = stats.AddFrame();
+    EXPECT_EQ(i, frame_stat->frame_number);
   }
-  EXPECT_EQ(kNumFrames, static_cast<int>(stats.stats_.size()));
+  EXPECT_EQ(kNumFrames, static_cast<int>(stats.size()));
 
-  stats.PrintSummary();  // should not crash
+  stats.PrintSummary();  // Should not crash.
 }
 
 }  // namespace test
diff --git a/modules/video_coding/codecs/test/videoprocessor.cc b/modules/video_coding/codecs/test/videoprocessor.cc
index fab707f..ed19ae6 100644
--- a/modules/video_coding/codecs/test/videoprocessor.cc
+++ b/modules/video_coding/codecs/test/videoprocessor.cc
@@ -150,6 +150,7 @@
       analysis_frame_writer_(analysis_frame_writer),
       encoded_frame_writer_(encoded_frame_writer),
       decoded_frame_writer_(decoded_frame_writer),
+      last_inputed_frame_num_(-1),
       last_encoded_frame_num_(-1),
       last_decoded_frame_num_(-1),
       first_key_frame_has_been_excluded_(false),
@@ -162,7 +163,6 @@
   RTC_DCHECK(analysis_frame_reader);
   RTC_DCHECK(analysis_frame_writer);
   RTC_DCHECK(stats);
-  frame_infos_.reserve(analysis_frame_reader->NumberOfFrames());
 }
 
 VideoProcessor::~VideoProcessor() = default;
@@ -225,11 +225,10 @@
   initialized_ = false;
 }
 
-void VideoProcessor::ProcessFrame(int frame_number) {
+void VideoProcessor::ProcessFrame() {
   RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
-  RTC_DCHECK_EQ(frame_number, frame_infos_.size())
-      << "Must process frames in sequence.";
   RTC_DCHECK(initialized_) << "VideoProcessor not initialized.";
+  ++last_inputed_frame_num_;
 
   // Get frame from file.
   rtc::scoped_refptr<I420BufferInterface> buffer(
@@ -238,9 +237,10 @@
   // Use the frame number as the basis for timestamp to identify frames. Let the
   // first timestamp be non-zero, to not make the IvfFileWriter believe that we
   // want to use capture timestamps in the IVF files.
-  const uint32_t rtp_timestamp = (frame_number + 1) * kRtpClockRateHz /
+  const uint32_t rtp_timestamp = (last_inputed_frame_num_ + 1) *
+                                 kRtpClockRateHz /
                                  config_.codec_settings.maxFramerate;
-  rtp_timestamp_to_frame_num_[rtp_timestamp] = frame_number;
+  rtp_timestamp_to_frame_num_[rtp_timestamp] = last_inputed_frame_num_;
   const int64_t kNoRenderTime = 0;
   VideoFrame source_frame(buffer, rtp_timestamp, kNoRenderTime,
                           webrtc::kVideoRotation_0);
@@ -248,25 +248,21 @@
   // Decide if we are going to force a keyframe.
   std::vector<FrameType> frame_types(1, kVideoFrameDelta);
   if (config_.keyframe_interval > 0 &&
-      frame_number % config_.keyframe_interval == 0) {
+      last_inputed_frame_num_ % config_.keyframe_interval == 0) {
     frame_types[0] = kVideoFrameKey;
   }
 
-  // Store frame information during the different stages of encode and decode.
-  frame_infos_.emplace_back();
-  FrameInfo* frame_info = &frame_infos_.back();
-
   // Create frame statistics object used for aggregation at end of test run.
-  FrameStatistic* frame_stat = &stats_->NewFrame(frame_number);
+  FrameStatistic* frame_stat = stats_->AddFrame();
 
   // For the highest measurement accuracy of the encode time, the start/stop
   // time recordings should wrap the Encode call as tightly as possible.
-  frame_info->encode_start_ns = rtc::TimeNanos();
+  frame_stat->encode_start_ns = rtc::TimeNanos();
   frame_stat->encode_return_code =
       encoder_->Encode(source_frame, nullptr, &frame_types);
 
   if (frame_stat->encode_return_code != WEBRTC_VIDEO_CODEC_OK) {
-    LOG(LS_WARNING) << "Failed to encode frame " << frame_number
+    LOG(LS_WARNING) << "Failed to encode frame " << last_inputed_frame_num_
                     << ", return code: " << frame_stat->encode_return_code
                     << ".";
   }
@@ -303,9 +299,8 @@
   // time recordings should wrap the Encode call as tightly as possible.
   int64_t encode_stop_ns = rtc::TimeNanos();
 
-  if (encoded_frame_writer_) {
-    RTC_CHECK(encoded_frame_writer_->WriteFrame(encoded_image, codec));
-  }
+  // Take the opportunity to verify the QP bitstream parser.
+  VerifyQpParser(encoded_image, config_);
 
   // Check for dropped frames.
   const int frame_number =
@@ -334,26 +329,23 @@
         }
       }
     }
-    last_frame_missing =
-        (frame_infos_[last_encoded_frame_num_].manipulated_length == 0);
+    const FrameStatistic* last_encoded_frame_stat =
+        stats_->GetFrame(last_encoded_frame_num_);
+    last_frame_missing = (last_encoded_frame_stat->manipulated_length == 0);
   }
   // Ensure strict monotonicity.
   RTC_CHECK_GT(frame_number, last_encoded_frame_num_);
   last_encoded_frame_num_ = frame_number;
 
-  // Update frame information and statistics.
-  VerifyQpParser(encoded_image, config_);
-  RTC_CHECK_LT(frame_number, frame_infos_.size());
-  FrameInfo* frame_info = &frame_infos_[frame_number];
-  FrameStatistic* frame_stat = &stats_->stats_[frame_number];
-  frame_stat->encode_time_in_us =
-      GetElapsedTimeMicroseconds(frame_info->encode_start_ns, encode_stop_ns);
+  // Update frame statistics.
+  FrameStatistic* frame_stat = stats_->GetFrame(frame_number);
+  frame_stat->encode_time_us =
+      GetElapsedTimeMicroseconds(frame_stat->encode_start_ns, encode_stop_ns);
   frame_stat->encoding_successful = true;
-  frame_stat->encoded_frame_length_in_bytes = encoded_image._length;
-  frame_stat->frame_number = frame_number;
+  frame_stat->encoded_frame_size_bytes = encoded_image._length;
   frame_stat->frame_type = encoded_image._frameType;
   frame_stat->qp = encoded_image.qp_;
-  frame_stat->bit_rate_in_kbps = static_cast<int>(
+  frame_stat->bitrate_kbps = static_cast<int>(
       encoded_image._length * config_.codec_settings.maxFramerate * 8 / 1000);
   frame_stat->total_packets =
       encoded_image._length / config_.networking_config.packet_size_in_bytes +
@@ -393,11 +385,11 @@
     frame_stat->packets_dropped =
         packet_manipulator_->ManipulatePackets(&copied_image);
   }
-  frame_info->manipulated_length = copied_image._length;
+  frame_stat->manipulated_length = copied_image._length;
 
   // For the highest measurement accuracy of the decode time, the start/stop
   // time recordings should wrap the Decode call as tightly as possible.
-  frame_info->decode_start_ns = rtc::TimeNanos();
+  frame_stat->decode_start_ns = rtc::TimeNanos();
   frame_stat->decode_return_code =
       decoder_->Decode(copied_image, last_frame_missing, nullptr);
 
@@ -415,6 +407,10 @@
           decoded_frame_writer_->WriteFrame(last_decoded_frame_buffer_.data()));
     }
   }
+
+  if (encoded_frame_writer_) {
+    RTC_CHECK(encoded_frame_writer_->WriteFrame(encoded_image, codec));
+  }
 }
 
 void VideoProcessor::FrameDecoded(const VideoFrame& image) {
@@ -424,26 +420,24 @@
   // time recordings should wrap the Decode call as tightly as possible.
   int64_t decode_stop_ns = rtc::TimeNanos();
 
-  // Update frame information and statistics.
+  // Update frame statistics.
   const int frame_number = rtp_timestamp_to_frame_num_[image.timestamp()];
-  RTC_CHECK_LT(frame_number, frame_infos_.size());
-  FrameInfo* frame_info = &frame_infos_[frame_number];
-  frame_info->decoded_width = image.width();
-  frame_info->decoded_height = image.height();
-  FrameStatistic* frame_stat = &stats_->stats_[frame_number];
-  frame_stat->decode_time_in_us =
-      GetElapsedTimeMicroseconds(frame_info->decode_start_ns, decode_stop_ns);
+  FrameStatistic* frame_stat = stats_->GetFrame(frame_number);
+  frame_stat->decoded_width = image.width();
+  frame_stat->decoded_height = image.height();
+  frame_stat->decode_time_us =
+      GetElapsedTimeMicroseconds(frame_stat->decode_start_ns, decode_stop_ns);
   frame_stat->decoding_successful = true;
 
   // Check if the codecs have resized the frame since previously decoded frame.
   if (frame_number > 0) {
     RTC_CHECK_GE(last_decoded_frame_num_, 0);
-    const FrameInfo& last_decoded_frame_info =
-        frame_infos_[last_decoded_frame_num_];
+    const FrameStatistic* last_decoded_frame_stat =
+        stats_->GetFrame(last_decoded_frame_num_);
     if (static_cast<int>(image.width()) !=
-            last_decoded_frame_info.decoded_width ||
+            last_decoded_frame_stat->decoded_width ||
         static_cast<int>(image.height()) !=
-            last_decoded_frame_info.decoded_height) {
+            last_decoded_frame_stat->decoded_height) {
       RTC_CHECK_GE(rate_update_index_, 0);
       ++num_spatial_resizes_[rate_update_index_];
     }
@@ -452,7 +446,7 @@
   RTC_CHECK_GT(frame_number, last_decoded_frame_num_);
   last_decoded_frame_num_ = frame_number;
 
-  // Check if codec size is different from the original size, and if so,
+  // Check if frame size is different from the original size, and if so,
   // scale back to original size. This is needed for the PSNR and SSIM
   // calculations.
   size_t extracted_length;
diff --git a/modules/video_coding/codecs/test/videoprocessor.h b/modules/video_coding/codecs/test/videoprocessor.h
index e50f0d9..3ae3dc5 100644
--- a/modules/video_coding/codecs/test/videoprocessor.h
+++ b/modules/video_coding/codecs/test/videoprocessor.h
@@ -134,9 +134,11 @@
   // Tears down callbacks and releases the encoder and decoder.
   void Release();
 
-  // Processes a single frame. The frames must be processed in order, and the
-  // VideoProcessor must be initialized first.
-  void ProcessFrame(int frame_number);
+  // Reads a frame from the analysis frame reader and sends it to the encoder.
+  // When the encode callback is received, the encoded frame is sent to the
+  // decoder. The decoded frame is written to disk by the analysis frame writer.
+  // Objective video quality metrics can thus be calculated after the fact.
+  void ProcessFrame();
 
   // Updates the encoder with target rates. Must be called at least once.
   void SetRates(int bitrate_kbps, int framerate_fps);
@@ -148,18 +150,6 @@
   std::vector<int> NumberSpatialResizesPerRateUpdate() const;
 
  private:
-  // Container that holds per-frame information that needs to be stored between
-  // calls to Encode and Decode, as well as the corresponding callbacks. It is
-  // not directly used for statistics -- for that, test::FrameStatistic is used.
-  // TODO(brandtr): Get rid of this struct and use the Stats class instead.
-  struct FrameInfo {
-    int64_t encode_start_ns = 0;
-    int64_t decode_start_ns = 0;
-    int decoded_width = 0;
-    int decoded_height = 0;
-    size_t manipulated_length = 0;
-  };
-
   class VideoProcessorEncodeCompleteCallback
       : public webrtc::EncodedImageCallback {
    public:
@@ -283,10 +273,8 @@
   IvfFileWriter* const encoded_frame_writer_;
   FrameWriter* const decoded_frame_writer_;
 
-  // Frame metadata for all frames that have been added through a call to
-  // ProcessFrames(). We need to store this metadata over the course of the
-  // test run, to support pipelining HW codecs.
-  std::vector<FrameInfo> frame_infos_ GUARDED_BY(sequence_checker_);
+  // Keep track of inputed/encoded/decoded frames, so we can detect frame drops.
+  int last_inputed_frame_num_ GUARDED_BY(sequence_checker_);
   int last_encoded_frame_num_ GUARDED_BY(sequence_checker_);
   int last_decoded_frame_num_ GUARDED_BY(sequence_checker_);
 
diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc
index 7fd5dc2..68c2a68 100644
--- a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc
+++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc
@@ -191,8 +191,7 @@
               rate_profile.input_frame_rate[rate_update_index]);
     }
 
-    task_queue.PostTask(
-        [this, frame_number] { processor_->ProcessFrame(frame_number); });
+    task_queue.PostTask([this] { processor_->ProcessFrame(); });
     ++frame_number;
 
     if (frame_number ==
@@ -245,7 +244,7 @@
                                         num_dropped_frames, num_resize_actions);
 
   // Calculate and print other statistics.
-  EXPECT_EQ(num_frames, static_cast<int>(stats_.stats_.size()));
+  EXPECT_EQ(num_frames, static_cast<int>(stats_.size()));
   stats_.PrintSummary();
 
   // Calculate and print image quality statistics.
@@ -414,10 +413,10 @@
   ++num_frames_per_update_[tl_idx];
   ++num_frames_total_;
 
-  FrameType frame_type = stats_.stats_[frame_number].frame_type;
+  const FrameStatistic* frame_stat = stats_.GetFrame(frame_number);
+  FrameType frame_type = frame_stat->frame_type;
   float encoded_size_kbits =
-      stats_.stats_[frame_number].encoded_frame_length_in_bytes * 8.0f /
-      1000.0f;
+      frame_stat->encoded_frame_size_bytes * 8.0f / 1000.0f;
 
   // Update layer data.
   // Update rate mismatch relative to per-frame bandwidth for delta frames.
diff --git a/modules/video_coding/codecs/test/videoprocessor_unittest.cc b/modules/video_coding/codecs/test/videoprocessor_unittest.cc
index 5f71a68..9474760 100644
--- a/modules/video_coding/codecs/test/videoprocessor_unittest.cc
+++ b/modules/video_coding/codecs/test/videoprocessor_unittest.cc
@@ -109,13 +109,13 @@
       encoder_mock_,
       Encode(Property(&VideoFrame::timestamp, 1 * 90000 / kFramerateFps), _, _))
       .Times(1);
-  video_processor_->ProcessFrame(0);
+  video_processor_->ProcessFrame();
 
   EXPECT_CALL(
       encoder_mock_,
       Encode(Property(&VideoFrame::timestamp, 2 * 90000 / kFramerateFps), _, _))
       .Times(1);
-  video_processor_->ProcessFrame(1);
+  video_processor_->ProcessFrame();
 
   ExpectRelease();
   video_processor_->Release();
@@ -135,7 +135,7 @@
                                              1 * 90000 / kStartFramerateFps),
                                     _, _))
       .Times(1);
-  video_processor_->ProcessFrame(0);
+  video_processor_->ProcessFrame();
 
   const int kNewFramerateFps = 13;
   video_processor_->SetRates(kBitrateKbps, kNewFramerateFps);
@@ -144,7 +144,7 @@
                                              2 * 90000 / kNewFramerateFps),
                                     _, _))
       .Times(1);
-  video_processor_->ProcessFrame(1);
+  video_processor_->ProcessFrame();
 
   ExpectRelease();
   video_processor_->Release();