[DVQA] Extract FrameInFlight into separate file

Also add ability to remove expected receivers for the frame.

Bug: b/231397778
Change-Id: Id1fb2df05a69e0dca4f05eaa995521b6be2ac52a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265396
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37191}
diff --git a/test/pc/e2e/BUILD.gn b/test/pc/e2e/BUILD.gn
index e88d53d..43bd800 100644
--- a/test/pc/e2e/BUILD.gn
+++ b/test/pc/e2e/BUILD.gn
@@ -734,6 +734,8 @@
     sources = [
       "analyzer/video/default_video_quality_analyzer_cpu_measurer.cc",
       "analyzer/video/default_video_quality_analyzer_cpu_measurer.h",
+      "analyzer/video/default_video_quality_analyzer_frame_in_flight.cc",
+      "analyzer/video/default_video_quality_analyzer_frame_in_flight.h",
       "analyzer/video/default_video_quality_analyzer_frames_comparator.cc",
       "analyzer/video/default_video_quality_analyzer_frames_comparator.h",
       "analyzer/video/default_video_quality_analyzer_internal_shared_objects.cc",
diff --git a/test/pc/e2e/analyzer/video/default_video_quality_analyzer.cc b/test/pc/e2e/analyzer/video/default_video_quality_analyzer.cc
index ebebe92..5922c44 100644
--- a/test/pc/e2e/analyzer/video/default_video_quality_analyzer.cc
+++ b/test/pc/e2e/analyzer/video/default_video_quality_analyzer.cc
@@ -12,7 +12,9 @@
 
 #include <algorithm>
 #include <memory>
+#include <set>
 #include <utility>
+#include <vector>
 
 #include "api/array_view.h"
 #include "api/numerics/samples_stats_counter.h"
@@ -25,6 +27,7 @@
 #include "rtc_base/strings/string_builder.h"
 #include "rtc_base/time_utils.h"
 #include "rtc_tools/frame_analyzer/video_geometry_aligner.h"
+#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.h"
 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator.h"
 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_internal_shared_objects.h"
 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_shared_objects.h"
@@ -227,10 +230,13 @@
 
       captured_frames_in_flight_.erase(it);
     }
+    std::set<size_t> frame_receivers_indexes = peers_->GetPresentIndexes();
+    if (!options_.enable_receive_own_stream) {
+      frame_receivers_indexes.erase(peer_index);
+    }
     captured_frames_in_flight_.emplace(
-        frame_id,
-        FrameInFlight(stream_index, frame, captured_time, peer_index,
-                      peers_->size(), options_.enable_receive_own_stream));
+        frame_id, FrameInFlight(stream_index, frame, captured_time,
+                                std::move(frame_receivers_indexes)));
     // Set frame id on local copy of the frame
     captured_frames_in_flight_.at(frame_id).SetFrameId(frame_id);
 
@@ -593,7 +599,7 @@
   // frame, the frame will be removed by OnFrameRendered after next frame comes
   // for the new peer. It is important because FrameInFlight is a large object.
   for (auto& key_val : captured_frames_in_flight_) {
-    key_val.second.AddPeer();
+    key_val.second.AddExpectedReceiver(new_peer_index);
   }
 }
 
@@ -988,170 +994,4 @@
   return out;
 }
 
-bool DefaultVideoQualityAnalyzer::FrameInFlight::RemoveFrame() {
-  if (!frame_) {
-    return false;
-  }
-  frame_ = absl::nullopt;
-  return true;
-}
-
-void DefaultVideoQualityAnalyzer::FrameInFlight::SetFrameId(uint16_t id) {
-  if (frame_) {
-    frame_->set_id(id);
-  }
-}
-
-std::vector<size_t>
-DefaultVideoQualityAnalyzer::FrameInFlight::GetPeersWhichDidntReceive() const {
-  std::vector<size_t> out;
-  for (size_t i = 0; i < peers_count_; ++i) {
-    auto it = receiver_stats_.find(i);
-    bool should_current_peer_receive =
-        i != owner_ || enable_receive_own_stream_;
-    if (should_current_peer_receive &&
-        (it == receiver_stats_.end() ||
-         it->second.rendered_time.IsInfinite())) {
-      out.push_back(i);
-    }
-  }
-  return out;
-}
-
-bool DefaultVideoQualityAnalyzer::FrameInFlight::HaveAllPeersReceived() const {
-  for (size_t i = 0; i < peers_count_; ++i) {
-    // Skip `owner_` only if peer can't receive its own stream.
-    if (i == owner_ && !enable_receive_own_stream_) {
-      continue;
-    }
-
-    auto it = receiver_stats_.find(i);
-    if (it == receiver_stats_.end()) {
-      return false;
-    }
-
-    if (!it->second.dropped && it->second.rendered_time.IsInfinite()) {
-      return false;
-    }
-  }
-  return true;
-}
-
-void DefaultVideoQualityAnalyzer::FrameInFlight::OnFrameEncoded(
-    webrtc::Timestamp time,
-    VideoFrameType frame_type,
-    DataSize encoded_image_size,
-    uint32_t target_encode_bitrate,
-    StreamCodecInfo used_encoder) {
-  encoded_time_ = time;
-  frame_type_ = frame_type;
-  encoded_image_size_ = encoded_image_size;
-  target_encode_bitrate_ += target_encode_bitrate;
-  // Update used encoder info. If simulcast/SVC is used, this method can
-  // be called multiple times, in such case we should preserve the value
-  // of `used_encoder_.switched_on_at` from the first invocation as the
-  // smallest one.
-  Timestamp encoder_switched_on_at = used_encoder_.has_value()
-                                         ? used_encoder_->switched_on_at
-                                         : Timestamp::PlusInfinity();
-  RTC_DCHECK(used_encoder.switched_on_at.IsFinite());
-  RTC_DCHECK(used_encoder.switched_from_at.IsFinite());
-  used_encoder_ = used_encoder;
-  if (encoder_switched_on_at < used_encoder_->switched_on_at) {
-    used_encoder_->switched_on_at = encoder_switched_on_at;
-  }
-}
-
-void DefaultVideoQualityAnalyzer::FrameInFlight::OnFramePreDecode(
-    size_t peer,
-    webrtc::Timestamp received_time,
-    webrtc::Timestamp decode_start_time,
-    VideoFrameType frame_type,
-    DataSize encoded_image_size) {
-  receiver_stats_[peer].received_time = received_time;
-  receiver_stats_[peer].decode_start_time = decode_start_time;
-  receiver_stats_[peer].frame_type = frame_type;
-  receiver_stats_[peer].encoded_image_size = encoded_image_size;
-}
-
-bool DefaultVideoQualityAnalyzer::FrameInFlight::HasReceivedTime(
-    size_t peer) const {
-  auto it = receiver_stats_.find(peer);
-  if (it == receiver_stats_.end()) {
-    return false;
-  }
-  return it->second.received_time.IsFinite();
-}
-
-void DefaultVideoQualityAnalyzer::FrameInFlight::OnFrameDecoded(
-    size_t peer,
-    webrtc::Timestamp time,
-    StreamCodecInfo used_decoder) {
-  receiver_stats_[peer].decode_end_time = time;
-  receiver_stats_[peer].used_decoder = used_decoder;
-}
-
-bool DefaultVideoQualityAnalyzer::FrameInFlight::HasDecodeEndTime(
-    size_t peer) const {
-  auto it = receiver_stats_.find(peer);
-  if (it == receiver_stats_.end()) {
-    return false;
-  }
-  return it->second.decode_end_time.IsFinite();
-}
-
-void DefaultVideoQualityAnalyzer::FrameInFlight::OnFrameRendered(
-    size_t peer,
-    webrtc::Timestamp time,
-    int width,
-    int height) {
-  receiver_stats_[peer].rendered_time = time;
-  receiver_stats_[peer].rendered_frame_width = width;
-  receiver_stats_[peer].rendered_frame_height = height;
-}
-
-bool DefaultVideoQualityAnalyzer::FrameInFlight::HasRenderedTime(
-    size_t peer) const {
-  auto it = receiver_stats_.find(peer);
-  if (it == receiver_stats_.end()) {
-    return false;
-  }
-  return it->second.rendered_time.IsFinite();
-}
-
-bool DefaultVideoQualityAnalyzer::FrameInFlight::IsDropped(size_t peer) const {
-  auto it = receiver_stats_.find(peer);
-  if (it == receiver_stats_.end()) {
-    return false;
-  }
-  return it->second.dropped;
-}
-
-FrameStats DefaultVideoQualityAnalyzer::FrameInFlight::GetStatsForPeer(
-    size_t peer) const {
-  FrameStats stats(captured_time_);
-  stats.pre_encode_time = pre_encode_time_;
-  stats.encoded_time = encoded_time_;
-  stats.target_encode_bitrate = target_encode_bitrate_;
-  stats.encoded_frame_type = frame_type_;
-  stats.encoded_image_size = encoded_image_size_;
-  stats.used_encoder = used_encoder_;
-
-  absl::optional<ReceiverFrameStats> receiver_stats =
-      MaybeGetValue<ReceiverFrameStats>(receiver_stats_, peer);
-  if (receiver_stats.has_value()) {
-    stats.received_time = receiver_stats->received_time;
-    stats.decode_start_time = receiver_stats->decode_start_time;
-    stats.decode_end_time = receiver_stats->decode_end_time;
-    stats.rendered_time = receiver_stats->rendered_time;
-    stats.prev_frame_rendered_time = receiver_stats->prev_frame_rendered_time;
-    stats.rendered_frame_width = receiver_stats->rendered_frame_width;
-    stats.rendered_frame_height = receiver_stats->rendered_frame_height;
-    stats.used_decoder = receiver_stats->used_decoder;
-    stats.pre_decoded_frame_type = receiver_stats->frame_type;
-    stats.pre_decoded_image_size = receiver_stats->encoded_image_size;
-  }
-  return stats;
-}
-
 }  // namespace webrtc
diff --git a/test/pc/e2e/analyzer/video/default_video_quality_analyzer.h b/test/pc/e2e/analyzer/video/default_video_quality_analyzer.h
index b9d8e3e..6d4818f 100644
--- a/test/pc/e2e/analyzer/video/default_video_quality_analyzer.h
+++ b/test/pc/e2e/analyzer/video/default_video_quality_analyzer.h
@@ -34,6 +34,7 @@
 #include "rtc_base/thread_annotations.h"
 #include "system_wrappers/include/clock.h"
 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_cpu_measurer.h"
+#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.h"
 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_frames_comparator.h"
 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_internal_shared_objects.h"
 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_shared_objects.h"
@@ -108,118 +109,6 @@
  private:
   enum State { kNew, kActive, kStopped };
 
-  struct ReceiverFrameStats {
-    // Time when last packet of a frame was received.
-    Timestamp received_time = Timestamp::MinusInfinity();
-    Timestamp decode_start_time = Timestamp::MinusInfinity();
-    Timestamp decode_end_time = Timestamp::MinusInfinity();
-    Timestamp rendered_time = Timestamp::MinusInfinity();
-    Timestamp prev_frame_rendered_time = Timestamp::MinusInfinity();
-
-    // Type and encoded size of received frame.
-    VideoFrameType frame_type = VideoFrameType::kEmptyFrame;
-    DataSize encoded_image_size = DataSize::Bytes(0);
-
-    absl::optional<int> rendered_frame_width = absl::nullopt;
-    absl::optional<int> rendered_frame_height = absl::nullopt;
-
-    // Can be not set if frame was dropped in the network.
-    absl::optional<StreamCodecInfo> used_decoder = absl::nullopt;
-
-    bool dropped = false;
-  };
-
-  class FrameInFlight {
-   public:
-    FrameInFlight(size_t stream,
-                  VideoFrame frame,
-                  Timestamp captured_time,
-                  size_t owner,
-                  size_t peers_count,
-                  bool enable_receive_own_stream)
-        : stream_(stream),
-          owner_(owner),
-          peers_count_(peers_count),
-          enable_receive_own_stream_(enable_receive_own_stream),
-          frame_(std::move(frame)),
-          captured_time_(captured_time) {}
-
-    size_t stream() const { return stream_; }
-    const absl::optional<VideoFrame>& frame() const { return frame_; }
-    // Returns was frame removed or not.
-    bool RemoveFrame();
-    void SetFrameId(uint16_t id);
-
-    void AddPeer() { ++peers_count_; }
-
-    std::vector<size_t> GetPeersWhichDidntReceive() const;
-    bool HaveAllPeersReceived() const;
-
-    void SetPreEncodeTime(webrtc::Timestamp time) { pre_encode_time_ = time; }
-
-    void OnFrameEncoded(webrtc::Timestamp time,
-                        VideoFrameType frame_type,
-                        DataSize encoded_image_size,
-                        uint32_t target_encode_bitrate,
-                        StreamCodecInfo used_encoder);
-
-    bool HasEncodedTime() const { return encoded_time_.IsFinite(); }
-
-    void OnFramePreDecode(size_t peer,
-                          webrtc::Timestamp received_time,
-                          webrtc::Timestamp decode_start_time,
-                          VideoFrameType frame_type,
-                          DataSize encoded_image_size);
-
-    bool HasReceivedTime(size_t peer) const;
-
-    void OnFrameDecoded(size_t peer,
-                        webrtc::Timestamp time,
-                        StreamCodecInfo used_decoder);
-
-    bool HasDecodeEndTime(size_t peer) const;
-
-    void OnFrameRendered(size_t peer,
-                         webrtc::Timestamp time,
-                         int width,
-                         int height);
-
-    bool HasRenderedTime(size_t peer) const;
-
-    // Crash if rendered time is not set for specified `peer`.
-    webrtc::Timestamp rendered_time(size_t peer) const {
-      return receiver_stats_.at(peer).rendered_time;
-    }
-
-    void MarkDropped(size_t peer) { receiver_stats_[peer].dropped = true; }
-    bool IsDropped(size_t peer) const;
-
-    void SetPrevFrameRenderedTime(size_t peer, webrtc::Timestamp time) {
-      receiver_stats_[peer].prev_frame_rendered_time = time;
-    }
-
-    FrameStats GetStatsForPeer(size_t peer) const;
-
-   private:
-    const size_t stream_;
-    const size_t owner_;
-    size_t peers_count_;
-    const bool enable_receive_own_stream_;
-    absl::optional<VideoFrame> frame_;
-
-    // Frame events timestamp.
-    Timestamp captured_time_;
-    Timestamp pre_encode_time_ = Timestamp::MinusInfinity();
-    Timestamp encoded_time_ = Timestamp::MinusInfinity();
-    // Type and encoded size of sent frame.
-    VideoFrameType frame_type_ = VideoFrameType::kEmptyFrame;
-    DataSize encoded_image_size_ = DataSize::Bytes(0);
-    uint32_t target_encode_bitrate_ = 0;
-    // Can be not set if frame was dropped by encoder.
-    absl::optional<StreamCodecInfo> used_encoder_ = absl::nullopt;
-    std::map<size_t, ReceiverFrameStats> receiver_stats_;
-  };
-
   // Returns next frame id to use. Frame ID can't be `VideoFrame::kNotSetId`,
   // because this value is reserved by `VideoFrame` as "ID not set".
   uint16_t GetNextFrameId() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
diff --git a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.cc b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.cc
new file mode 100644
index 0000000..13e77b4
--- /dev/null
+++ b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.cc
@@ -0,0 +1,194 @@
+/*
+ *  Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.h"
+
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/units/data_size.h"
+#include "api/units/timestamp.h"
+#include "api/video/video_frame_type.h"
+#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_internal_shared_objects.h"
+
+namespace webrtc {
+namespace {
+
+template <typename T>
+absl::optional<T> MaybeGetValue(const std::map<size_t, T>& map, size_t key) {
+  auto it = map.find(key);
+  if (it == map.end()) {
+    return absl::nullopt;
+  }
+  return it->second;
+}
+
+}  // namespace
+
+FrameInFlight::FrameInFlight(size_t stream,
+                             VideoFrame frame,
+                             Timestamp captured_time,
+                             std::set<size_t> expected_receivers)
+    : stream_(stream),
+      expected_receivers_(std::move(expected_receivers)),
+      frame_(std::move(frame)),
+      captured_time_(captured_time) {}
+
+bool FrameInFlight::RemoveFrame() {
+  if (!frame_) {
+    return false;
+  }
+  frame_ = absl::nullopt;
+  return true;
+}
+
+void FrameInFlight::SetFrameId(uint16_t id) {
+  if (frame_) {
+    frame_->set_id(id);
+  }
+}
+
+std::vector<size_t> FrameInFlight::GetPeersWhichDidntReceive() const {
+  std::vector<size_t> out;
+  for (size_t peer : expected_receivers_) {
+    auto it = receiver_stats_.find(peer);
+    if (it == receiver_stats_.end() ||
+        (!it->second.dropped && it->second.rendered_time.IsInfinite())) {
+      out.push_back(peer);
+    }
+  }
+  return out;
+}
+
+bool FrameInFlight::HaveAllPeersReceived() const {
+  for (size_t peer : expected_receivers_) {
+    auto it = receiver_stats_.find(peer);
+    if (it == receiver_stats_.end()) {
+      return false;
+    }
+
+    if (!it->second.dropped && it->second.rendered_time.IsInfinite()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void FrameInFlight::OnFrameEncoded(webrtc::Timestamp time,
+                                   VideoFrameType frame_type,
+                                   DataSize encoded_image_size,
+                                   uint32_t target_encode_bitrate,
+                                   StreamCodecInfo used_encoder) {
+  encoded_time_ = time;
+  frame_type_ = frame_type;
+  encoded_image_size_ = encoded_image_size;
+  target_encode_bitrate_ += target_encode_bitrate;
+  // Update used encoder info. If simulcast/SVC is used, this method can
+  // be called multiple times, in such case we should preserve the value
+  // of `used_encoder_.switched_on_at` from the first invocation as the
+  // smallest one.
+  Timestamp encoder_switched_on_at = used_encoder_.has_value()
+                                         ? used_encoder_->switched_on_at
+                                         : Timestamp::PlusInfinity();
+  RTC_DCHECK(used_encoder.switched_on_at.IsFinite());
+  RTC_DCHECK(used_encoder.switched_from_at.IsFinite());
+  used_encoder_ = used_encoder;
+  if (encoder_switched_on_at < used_encoder_->switched_on_at) {
+    used_encoder_->switched_on_at = encoder_switched_on_at;
+  }
+}
+
+void FrameInFlight::OnFramePreDecode(size_t peer,
+                                     webrtc::Timestamp received_time,
+                                     webrtc::Timestamp decode_start_time,
+                                     VideoFrameType frame_type,
+                                     DataSize encoded_image_size) {
+  receiver_stats_[peer].received_time = received_time;
+  receiver_stats_[peer].decode_start_time = decode_start_time;
+  receiver_stats_[peer].frame_type = frame_type;
+  receiver_stats_[peer].encoded_image_size = encoded_image_size;
+}
+
+bool FrameInFlight::HasReceivedTime(size_t peer) const {
+  auto it = receiver_stats_.find(peer);
+  if (it == receiver_stats_.end()) {
+    return false;
+  }
+  return it->second.received_time.IsFinite();
+}
+
+void FrameInFlight::OnFrameDecoded(size_t peer,
+                                   webrtc::Timestamp time,
+                                   StreamCodecInfo used_decoder) {
+  receiver_stats_[peer].decode_end_time = time;
+  receiver_stats_[peer].used_decoder = used_decoder;
+}
+
+bool FrameInFlight::HasDecodeEndTime(size_t peer) const {
+  auto it = receiver_stats_.find(peer);
+  if (it == receiver_stats_.end()) {
+    return false;
+  }
+  return it->second.decode_end_time.IsFinite();
+}
+
+void FrameInFlight::OnFrameRendered(size_t peer,
+                                    webrtc::Timestamp time,
+                                    int width,
+                                    int height) {
+  receiver_stats_[peer].rendered_time = time;
+  receiver_stats_[peer].rendered_frame_width = width;
+  receiver_stats_[peer].rendered_frame_height = height;
+}
+
+bool FrameInFlight::HasRenderedTime(size_t peer) const {
+  auto it = receiver_stats_.find(peer);
+  if (it == receiver_stats_.end()) {
+    return false;
+  }
+  return it->second.rendered_time.IsFinite();
+}
+
+bool FrameInFlight::IsDropped(size_t peer) const {
+  auto it = receiver_stats_.find(peer);
+  if (it == receiver_stats_.end()) {
+    return false;
+  }
+  return it->second.dropped;
+}
+
+FrameStats FrameInFlight::GetStatsForPeer(size_t peer) const {
+  FrameStats stats(captured_time_);
+  stats.pre_encode_time = pre_encode_time_;
+  stats.encoded_time = encoded_time_;
+  stats.target_encode_bitrate = target_encode_bitrate_;
+  stats.encoded_frame_type = frame_type_;
+  stats.encoded_image_size = encoded_image_size_;
+  stats.used_encoder = used_encoder_;
+
+  absl::optional<ReceiverFrameStats> receiver_stats =
+      MaybeGetValue<ReceiverFrameStats>(receiver_stats_, peer);
+  if (receiver_stats.has_value()) {
+    stats.received_time = receiver_stats->received_time;
+    stats.decode_start_time = receiver_stats->decode_start_time;
+    stats.decode_end_time = receiver_stats->decode_end_time;
+    stats.rendered_time = receiver_stats->rendered_time;
+    stats.prev_frame_rendered_time = receiver_stats->prev_frame_rendered_time;
+    stats.rendered_frame_width = receiver_stats->rendered_frame_width;
+    stats.rendered_frame_height = receiver_stats->rendered_frame_height;
+    stats.used_decoder = receiver_stats->used_decoder;
+    stats.pre_decoded_frame_type = receiver_stats->frame_type;
+    stats.pre_decoded_image_size = receiver_stats->encoded_image_size;
+  }
+  return stats;
+}
+
+}  // namespace webrtc
diff --git a/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.h b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.h
new file mode 100644
index 0000000..a1ce8fa
--- /dev/null
+++ b/test/pc/e2e/analyzer/video/default_video_quality_analyzer_frame_in_flight.h
@@ -0,0 +1,159 @@
+/*
+ *  Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef TEST_PC_E2E_ANALYZER_VIDEO_DEFAULT_VIDEO_QUALITY_ANALYZER_FRAME_IN_FLIGHT_H_
+#define TEST_PC_E2E_ANALYZER_VIDEO_DEFAULT_VIDEO_QUALITY_ANALYZER_FRAME_IN_FLIGHT_H_
+
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/units/data_size.h"
+#include "api/units/timestamp.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_frame_type.h"
+#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer_internal_shared_objects.h"
+
+namespace webrtc {
+
+struct ReceiverFrameStats {
+  // Time when last packet of a frame was received.
+  Timestamp received_time = Timestamp::MinusInfinity();
+  Timestamp decode_start_time = Timestamp::MinusInfinity();
+  Timestamp decode_end_time = Timestamp::MinusInfinity();
+  Timestamp rendered_time = Timestamp::MinusInfinity();
+  Timestamp prev_frame_rendered_time = Timestamp::MinusInfinity();
+
+  // Type and encoded size of received frame.
+  VideoFrameType frame_type = VideoFrameType::kEmptyFrame;
+  DataSize encoded_image_size = DataSize::Bytes(0);
+
+  absl::optional<int> rendered_frame_width = absl::nullopt;
+  absl::optional<int> rendered_frame_height = absl::nullopt;
+
+  // Can be not set if frame was dropped in the network.
+  absl::optional<StreamCodecInfo> used_decoder = absl::nullopt;
+
+  bool dropped = false;
+};
+
+// Represents a frame which was sent by sender and is currently on the way to
+// multiple receivers. Some receivers may receive this frame and some don't.
+//
+// Contains all statistic associated with the frame and gathered in multiple
+// points of the video pipeline.
+//
+// Internally may store the copy of the source frame which was sent. In such
+// case this frame is "alive".
+class FrameInFlight {
+ public:
+  FrameInFlight(size_t stream,
+                VideoFrame frame,
+                Timestamp captured_time,
+                std::set<size_t> expected_receivers);
+
+  size_t stream() const { return stream_; }
+  // Returns internal copy of source `VideoFrame` or `absl::nullopt` if it was
+  // removed before.
+  const absl::optional<VideoFrame>& frame() const { return frame_; }
+  // Removes internal copy of the source `VideoFrame` to free up extra memory.
+  // Returns was frame removed or not.
+  bool RemoveFrame();
+  void SetFrameId(uint16_t id);
+
+  void AddExpectedReceiver(size_t peer) { expected_receivers_.insert(peer); }
+
+  void RemoveExpectedReceiver(size_t peer) { expected_receivers_.erase(peer); }
+
+  std::vector<size_t> GetPeersWhichDidntReceive() const;
+
+  // Returns if all peers which were expected to receive this frame actually
+  // received it or not.
+  bool HaveAllPeersReceived() const;
+
+  void SetPreEncodeTime(webrtc::Timestamp time) { pre_encode_time_ = time; }
+
+  void OnFrameEncoded(webrtc::Timestamp time,
+                      VideoFrameType frame_type,
+                      DataSize encoded_image_size,
+                      uint32_t target_encode_bitrate,
+                      StreamCodecInfo used_encoder);
+
+  bool HasEncodedTime() const { return encoded_time_.IsFinite(); }
+
+  void OnFramePreDecode(size_t peer,
+                        webrtc::Timestamp received_time,
+                        webrtc::Timestamp decode_start_time,
+                        VideoFrameType frame_type,
+                        DataSize encoded_image_size);
+
+  bool HasReceivedTime(size_t peer) const;
+
+  void OnFrameDecoded(size_t peer,
+                      webrtc::Timestamp time,
+                      StreamCodecInfo used_decoder);
+
+  bool HasDecodeEndTime(size_t peer) const;
+
+  void OnFrameRendered(size_t peer,
+                       webrtc::Timestamp time,
+                       int width,
+                       int height);
+
+  bool HasRenderedTime(size_t peer) const;
+
+  // Crash if rendered time is not set for specified `peer`.
+  webrtc::Timestamp rendered_time(size_t peer) const {
+    return receiver_stats_.at(peer).rendered_time;
+  }
+
+  // Marks that frame was dropped and wasn't seen by particular `peer`.
+  void MarkDropped(size_t peer) { receiver_stats_[peer].dropped = true; }
+  bool IsDropped(size_t peer) const;
+
+  void SetPrevFrameRenderedTime(size_t peer, webrtc::Timestamp time) {
+    receiver_stats_[peer].prev_frame_rendered_time = time;
+  }
+
+  FrameStats GetStatsForPeer(size_t peer) const;
+
+ private:
+  const size_t stream_;
+  // Set of peer's indexes who are expected to receive this frame. This is not
+  // the set of peer's indexes that received the frame. For example, if peer A
+  // was among expected receivers, it received frame and then left the call, A
+  // will be removed from this set, but the Stats for peer A still will be
+  // preserved in the FrameInFlight.
+  //
+  // This set is used to determine if this frame is expected to be received by
+  // any peer or can be safely deleted. It is responsibility of the user of this
+  // object to decide when it should be deleted.
+  std::set<size_t> expected_receivers_;
+  absl::optional<VideoFrame> frame_;
+
+  // Frame events timestamp.
+  Timestamp captured_time_;
+  Timestamp pre_encode_time_ = Timestamp::MinusInfinity();
+  Timestamp encoded_time_ = Timestamp::MinusInfinity();
+  // Type and encoded size of sent frame.
+  VideoFrameType frame_type_ = VideoFrameType::kEmptyFrame;
+  DataSize encoded_image_size_ = DataSize::Bytes(0);
+  uint32_t target_encode_bitrate_ = 0;
+  // Can be not set if frame was dropped by encoder.
+  absl::optional<StreamCodecInfo> used_encoder_ = absl::nullopt;
+  // Map from the receiver peer's index to frame stats for that peer.
+  std::map<size_t, ReceiverFrameStats> receiver_stats_;
+};
+
+}  // namespace webrtc
+
+#endif  // TEST_PC_E2E_ANALYZER_VIDEO_DEFAULT_VIDEO_QUALITY_ANALYZER_FRAME_IN_FLIGHT_H_
diff --git a/test/pc/e2e/analyzer/video/names_collection.cc b/test/pc/e2e/analyzer/video/names_collection.cc
index 845f73d..6ee2ef0 100644
--- a/test/pc/e2e/analyzer/video/names_collection.cc
+++ b/test/pc/e2e/analyzer/video/names_collection.cc
@@ -10,6 +10,8 @@
 
 #include "test/pc/e2e/analyzer/video/names_collection.h"
 
+#include <set>
+
 #include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
 
@@ -78,4 +80,14 @@
   return index;
 }
 
+std::set<size_t> NamesCollection::GetPresentIndexes() const {
+  std::set<size_t> out;
+  for (size_t i = 0; i < removed_.size(); ++i) {
+    if (!removed_[i]) {
+      out.insert(i);
+    }
+  }
+  return out;
+}
+
 }  // namespace webrtc
diff --git a/test/pc/e2e/analyzer/video/names_collection.h b/test/pc/e2e/analyzer/video/names_collection.h
index 77c4939..4b4a439 100644
--- a/test/pc/e2e/analyzer/video/names_collection.h
+++ b/test/pc/e2e/analyzer/video/names_collection.h
@@ -12,6 +12,7 @@
 #define TEST_PC_E2E_ANALYZER_VIDEO_NAMES_COLLECTION_H_
 
 #include <map>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -70,6 +71,10 @@
   // registered in the collection.
   absl::optional<size_t> RemoveIfPresent(absl::string_view name);
 
+  // Returns a set of indexes for all currently present names in the
+  // collection.
+  std::set<size_t> GetPresentIndexes() const;
+
  private:
   std::vector<std::string> names_;
   std::vector<bool> removed_;