Add a tracker for RTCRtpContributingSource and RTCRtpSynchronizationSource.

This change adds a new SourceTracker class that can do spec-compliant tracking of RTCRtpContributingSource and RTCRtpSynchronizationSource when frames are delivered to the RTCRtpReceiver's MediaStreamTrack for playout. It will replace the existing spec-incompliant ContributingSources.

Bug: webrtc:10545 webrtc:10668
Change-Id: I961adaba09d6337f2f36b301a4fabcd20de65271
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/140948
Commit-Queue: Chen Xing <chxg@google.com>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28249}
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index b70ad72..f447b3e 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -180,6 +180,8 @@
     "source/rtp_sequence_number_map.h",
     "source/rtp_utility.cc",
     "source/rtp_utility.h",
+    "source/source_tracker.cc",
+    "source/source_tracker.h",
     "source/time_util.cc",
     "source/time_util.h",
     "source/tmmbr_help.cc",
@@ -209,6 +211,7 @@
     "../../api:function_view",
     "../../api:libjingle_peerconnection_api",
     "../../api:rtp_headers",
+    "../../api:rtp_packet_info",
     "../../api:scoped_refptr",
     "../../api:transport_api",
     "../../api/audio_codecs:audio_codecs_api",
@@ -434,6 +437,7 @@
       "source/rtp_sender_video_unittest.cc",
       "source/rtp_sequence_number_map_unittest.cc",
       "source/rtp_utility_unittest.cc",
+      "source/source_tracker_unittest.cc",
       "source/time_util_unittest.cc",
       "source/ulpfec_generator_unittest.cc",
       "source/ulpfec_header_reader_writer_unittest.cc",
@@ -449,6 +453,8 @@
       "../..:webrtc_common",
       "../../api:array_view",
       "../../api:libjingle_peerconnection_api",
+      "../../api:rtp_headers",
+      "../../api:rtp_packet_info",
       "../../api:scoped_refptr",
       "../../api:transport_api",
       "../../api/transport:field_trial_based_config",
diff --git a/modules/rtp_rtcp/source/source_tracker.cc b/modules/rtp_rtcp/source/source_tracker.cc
new file mode 100644
index 0000000..2878b11
--- /dev/null
+++ b/modules/rtp_rtcp/source/source_tracker.cc
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) 2019 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 "modules/rtp_rtcp/source/source_tracker.h"
+
+#include <algorithm>
+#include <utility>
+
+namespace webrtc {
+
+constexpr int64_t SourceTracker::kTimeoutMs;
+
+SourceTracker::SourceTracker(Clock* clock) : clock_(clock) {}
+
+void SourceTracker::OnFrameDelivered(const RtpPacketInfos& packet_infos) {
+  if (packet_infos.empty()) {
+    return;
+  }
+
+  int64_t now_ms = clock_->TimeInMilliseconds();
+  rtc::CritScope lock_scope(&lock_);
+
+  for (const auto& packet_info : packet_infos) {
+    for (uint32_t csrc : packet_info.csrcs()) {
+      SourceKey key(RtpSourceType::CSRC, csrc);
+      SourceEntry& entry = UpdateEntry(key);
+
+      entry.timestamp_ms = now_ms;
+      entry.audio_level = packet_info.audio_level();
+      entry.rtp_timestamp = packet_info.rtp_timestamp();
+    }
+
+    SourceKey key(RtpSourceType::SSRC, packet_info.ssrc());
+    SourceEntry& entry = UpdateEntry(key);
+
+    entry.timestamp_ms = now_ms;
+    entry.audio_level = packet_info.audio_level();
+    entry.rtp_timestamp = packet_info.rtp_timestamp();
+  }
+
+  PruneEntries(now_ms);
+}
+
+std::vector<RtpSource> SourceTracker::GetSources() const {
+  std::vector<RtpSource> sources;
+
+  int64_t now_ms = clock_->TimeInMilliseconds();
+  rtc::CritScope lock_scope(&lock_);
+
+  PruneEntries(now_ms);
+
+  for (const auto& pair : list_) {
+    const SourceKey& key = pair.first;
+    const SourceEntry& entry = pair.second;
+
+    sources.emplace_back(entry.timestamp_ms, key.source, key.source_type,
+                         entry.audio_level, entry.rtp_timestamp);
+  }
+
+  return sources;
+}
+
+SourceTracker::SourceEntry& SourceTracker::UpdateEntry(const SourceKey& key) {
+  // We intentionally do |find() + emplace()|, instead of checking the return
+  // value of |emplace()|, for performance reasons. It's much more likely for
+  // the key to already exist than for it not to.
+  auto map_it = map_.find(key);
+  if (map_it == map_.end()) {
+    // Insert a new entry at the front of the list.
+    list_.emplace_front(key, SourceEntry());
+    map_.emplace(key, list_.begin());
+  } else if (map_it->second != list_.begin()) {
+    // Move the old entry to the front of the list.
+    list_.splice(list_.begin(), list_, map_it->second);
+  }
+
+  return list_.front().second;
+}
+
+void SourceTracker::PruneEntries(int64_t now_ms) const {
+  int64_t prune_ms = now_ms - kTimeoutMs;
+
+  while (!list_.empty() && list_.back().second.timestamp_ms < prune_ms) {
+    map_.erase(list_.back().first);
+    list_.pop_back();
+  }
+}
+
+}  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/source_tracker.h b/modules/rtp_rtcp/source/source_tracker.h
new file mode 100644
index 0000000..035b9ec
--- /dev/null
+++ b/modules/rtp_rtcp/source/source_tracker.h
@@ -0,0 +1,125 @@
+/*
+ *  Copyright (c) 2019 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 MODULES_RTP_RTCP_SOURCE_SOURCE_TRACKER_H_
+#define MODULES_RTP_RTCP_SOURCE_SOURCE_TRACKER_H_
+
+#include <cstdint>
+#include <list>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/rtp_packet_infos.h"
+#include "api/rtp_receiver_interface.h"
+#include "rtc_base/critical_section.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+//
+// Tracker for `RTCRtpContributingSource` and `RTCRtpSynchronizationSource`:
+//   - https://w3c.github.io/webrtc-pc/#dom-rtcrtpcontributingsource
+//   - https://w3c.github.io/webrtc-pc/#dom-rtcrtpsynchronizationsource
+//
+class SourceTracker {
+ public:
+  // Amount of time before the entry associated with an update is removed. See:
+  // https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getcontributingsources
+  static constexpr int64_t kTimeoutMs = 10000;  // 10 seconds
+
+  explicit SourceTracker(Clock* clock);
+
+  SourceTracker(const SourceTracker& other) = delete;
+  SourceTracker(SourceTracker&& other) = delete;
+  SourceTracker& operator=(const SourceTracker& other) = delete;
+  SourceTracker& operator=(SourceTracker&& other) = delete;
+
+  // Updates the source entries when a frame is delivered to the
+  // RTCRtpReceiver's MediaStreamTrack.
+  void OnFrameDelivered(const RtpPacketInfos& packet_infos);
+
+  // Returns an |RtpSource| for each unique SSRC and CSRC identifier updated in
+  // the last |kTimeoutMs| milliseconds. Entries appear in reverse chronological
+  // order (i.e. with the most recently updated entries appearing first).
+  std::vector<RtpSource> GetSources() const;
+
+ private:
+  struct SourceKey {
+    SourceKey(RtpSourceType source_type, uint32_t source)
+        : source_type(source_type), source(source) {}
+
+    // Type of |source|.
+    RtpSourceType source_type;
+
+    // CSRC or SSRC identifier of the contributing or synchronization source.
+    uint32_t source;
+  };
+
+  struct SourceKeyComparator {
+    bool operator()(const SourceKey& lhs, const SourceKey& rhs) const {
+      return (lhs.source_type == rhs.source_type) && (lhs.source == rhs.source);
+    }
+  };
+
+  struct SourceKeyHasher {
+    size_t operator()(const SourceKey& value) const {
+      return static_cast<size_t>(value.source_type) +
+             static_cast<size_t>(value.source) * 11076425802534262905ULL;
+    }
+  };
+
+  struct SourceEntry {
+    // Timestamp indicating the most recent time a frame from an RTP packet,
+    // originating from this source, was delivered to the RTCRtpReceiver's
+    // MediaStreamTrack. Its reference clock is the outer class's |clock_|.
+    int64_t timestamp_ms;
+
+    // Audio level from an RFC 6464 or RFC 6465 header extension received with
+    // the most recent packet used to assemble the frame associated with
+    // |timestamp_ms|. May be absent. Only relevant for audio receivers. See the
+    // specs for `RTCRtpContributingSource` for more info.
+    absl::optional<uint8_t> audio_level;
+
+    // RTP timestamp of the most recent packet used to assemble the frame
+    // associated with |timestamp_ms|.
+    uint32_t rtp_timestamp;
+  };
+
+  using SourceList = std::list<std::pair<const SourceKey, SourceEntry>>;
+  using SourceMap = std::unordered_map<SourceKey,
+                                       SourceList::iterator,
+                                       SourceKeyHasher,
+                                       SourceKeyComparator>;
+
+  // Updates an entry by creating it (if it didn't previously exist) and moving
+  // it to the front of the list. Returns a reference to the entry.
+  SourceEntry& UpdateEntry(const SourceKey& key)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
+  // Removes entries that have timed out. Marked as "const" so that we can do
+  // pruning in getters.
+  void PruneEntries(int64_t now_ms) const RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
+  Clock* const clock_;
+  rtc::CriticalSection lock_;
+
+  // Entries are stored in reverse chronological order (i.e. with the most
+  // recently updated entries appearing first). Mutability is needed for timeout
+  // pruning in const functions.
+  mutable SourceList list_ RTC_GUARDED_BY(lock_);
+  mutable SourceMap map_ RTC_GUARDED_BY(lock_);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_RTP_RTCP_SOURCE_SOURCE_TRACKER_H_
diff --git a/modules/rtp_rtcp/source/source_tracker_unittest.cc b/modules/rtp_rtcp/source/source_tracker_unittest.cc
new file mode 100644
index 0000000..d487854
--- /dev/null
+++ b/modules/rtp_rtcp/source/source_tracker_unittest.cc
@@ -0,0 +1,336 @@
+/*
+ *  Copyright (c) 2019 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 "modules/rtp_rtcp/source/source_tracker.h"
+
+#include <algorithm>
+#include <list>
+#include <random>
+#include <set>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "api/rtp_headers.h"
+#include "api/rtp_packet_info.h"
+#include "api/rtp_packet_infos.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using ::testing::Combine;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::IsEmpty;
+using ::testing::SizeIs;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+constexpr size_t kPacketInfosCountMax = 5;
+
+// Simple "guaranteed to be correct" re-implementation of |SourceTracker| for
+// dual-implementation testing purposes.
+class ExpectedSourceTracker {
+ public:
+  explicit ExpectedSourceTracker(Clock* clock) : clock_(clock) {}
+
+  void OnFrameDelivered(const RtpPacketInfos& packet_infos) {
+    const int64_t now_ms = clock_->TimeInMilliseconds();
+
+    for (const auto& packet_info : packet_infos) {
+      for (const auto& csrc : packet_info.csrcs()) {
+        entries_.emplace_front(now_ms, csrc, RtpSourceType::CSRC,
+                               packet_info.audio_level(),
+                               packet_info.rtp_timestamp());
+      }
+
+      entries_.emplace_front(now_ms, packet_info.ssrc(), RtpSourceType::SSRC,
+                             packet_info.audio_level(),
+                             packet_info.rtp_timestamp());
+    }
+
+    PruneEntries(now_ms);
+  }
+
+  std::vector<RtpSource> GetSources() const {
+    PruneEntries(clock_->TimeInMilliseconds());
+
+    return std::vector<RtpSource>(entries_.begin(), entries_.end());
+  }
+
+ private:
+  void PruneEntries(int64_t now_ms) const {
+    const int64_t prune_ms = now_ms - 10000;  // 10 seconds
+
+    std::set<std::pair<RtpSourceType, uint32_t>> seen;
+
+    auto it = entries_.begin();
+    auto end = entries_.end();
+    while (it != end) {
+      auto next = it;
+      ++next;
+
+      auto key = std::make_pair(it->source_type(), it->source_id());
+      if (!seen.insert(key).second || it->timestamp_ms() < prune_ms) {
+        entries_.erase(it);
+      }
+
+      it = next;
+    }
+  }
+
+  Clock* const clock_;
+
+  mutable std::list<RtpSource> entries_;
+};
+
+class SourceTrackerRandomTest
+    : public TestWithParam<std::tuple<uint32_t, uint32_t>> {
+ protected:
+  SourceTrackerRandomTest()
+      : ssrcs_count_(std::get<0>(GetParam())),
+        csrcs_count_(std::get<1>(GetParam())),
+        generator_(42) {}
+
+  RtpPacketInfos GeneratePacketInfos() {
+    size_t count = std::uniform_int_distribution<size_t>(
+        1, kPacketInfosCountMax)(generator_);
+
+    RtpPacketInfos::vector_type packet_infos;
+    for (size_t i = 0; i < count; ++i) {
+      packet_infos.emplace_back(GenerateSsrc(), GenerateCsrcs(),
+                                GenerateSequenceNumber(),
+                                GenerateRtpTimestamp(), GenerateAudioLevel(),
+                                GenerateReceiveTimeMs());
+    }
+
+    return RtpPacketInfos(std::move(packet_infos));
+  }
+
+  int64_t GenerateClockAdvanceTimeMilliseconds() {
+    double roll = std::uniform_real_distribution<double>(0.0, 1.0)(generator_);
+
+    if (roll < 0.05) {
+      return 0;
+    }
+
+    if (roll < 0.08) {
+      return SourceTracker::kTimeoutMs - 1;
+    }
+
+    if (roll < 0.11) {
+      return SourceTracker::kTimeoutMs;
+    }
+
+    if (roll < 0.19) {
+      return std::uniform_int_distribution<int64_t>(
+          SourceTracker::kTimeoutMs,
+          SourceTracker::kTimeoutMs * 1000)(generator_);
+    }
+
+    return std::uniform_int_distribution<int64_t>(
+        1, SourceTracker::kTimeoutMs - 1)(generator_);
+  }
+
+ private:
+  uint32_t GenerateSsrc() {
+    return std::uniform_int_distribution<uint32_t>(1, ssrcs_count_)(generator_);
+  }
+
+  std::vector<uint32_t> GenerateCsrcs() {
+    std::vector<uint32_t> csrcs;
+    for (size_t i = 1; i <= csrcs_count_ && csrcs.size() < kRtpCsrcSize; ++i) {
+      if (std::bernoulli_distribution(0.5)(generator_)) {
+        csrcs.push_back(i);
+      }
+    }
+
+    return csrcs;
+  }
+
+  uint16_t GenerateSequenceNumber() {
+    return std::uniform_int_distribution<uint16_t>()(generator_);
+  }
+
+  uint32_t GenerateRtpTimestamp() {
+    return std::uniform_int_distribution<uint32_t>()(generator_);
+  }
+
+  absl::optional<uint8_t> GenerateAudioLevel() {
+    if (std::bernoulli_distribution(0.25)(generator_)) {
+      return absl::nullopt;
+    }
+
+    // Workaround for std::uniform_int_distribution<uint8_t> not being allowed.
+    return static_cast<uint8_t>(
+        std::uniform_int_distribution<uint16_t>()(generator_));
+  }
+
+  int64_t GenerateReceiveTimeMs() {
+    return std::uniform_int_distribution<int64_t>()(generator_);
+  }
+
+  const uint32_t ssrcs_count_;
+  const uint32_t csrcs_count_;
+
+  std::mt19937 generator_;
+};
+
+}  // namespace
+
+TEST_P(SourceTrackerRandomTest, RandomOperations) {
+  constexpr size_t kIterationsCount = 200;
+
+  SimulatedClock clock(1000000000000ULL);
+  SourceTracker actual_tracker(&clock);
+  ExpectedSourceTracker expected_tracker(&clock);
+
+  ASSERT_THAT(actual_tracker.GetSources(), IsEmpty());
+  ASSERT_THAT(expected_tracker.GetSources(), IsEmpty());
+
+  for (size_t i = 0; i < kIterationsCount; ++i) {
+    RtpPacketInfos packet_infos = GeneratePacketInfos();
+
+    actual_tracker.OnFrameDelivered(packet_infos);
+    expected_tracker.OnFrameDelivered(packet_infos);
+
+    clock.AdvanceTimeMilliseconds(GenerateClockAdvanceTimeMilliseconds());
+
+    ASSERT_THAT(actual_tracker.GetSources(),
+                ElementsAreArray(expected_tracker.GetSources()));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         SourceTrackerRandomTest,
+                         Combine(/*ssrcs_count_=*/Values(1, 2, 4),
+                                 /*csrcs_count_=*/Values(0, 1, 3, 7)));
+
+TEST(SourceTrackerTest, StartEmpty) {
+  SimulatedClock clock(1000000000000ULL);
+  SourceTracker tracker(&clock);
+
+  EXPECT_THAT(tracker.GetSources(), IsEmpty());
+}
+
+TEST(SourceTrackerTest, OnFrameDeliveredRecordsSources) {
+  constexpr uint32_t kSsrc = 10;
+  constexpr uint32_t kCsrcs[] = {20, 21};
+  constexpr uint16_t kSequenceNumber = 30;
+  constexpr uint32_t kRtpTimestamp = 40;
+  constexpr absl::optional<uint8_t> kAudioLevel = 50;
+  constexpr int64_t kReceiveTimeMs = 60;
+
+  SimulatedClock clock(1000000000000ULL);
+  SourceTracker tracker(&clock);
+
+  tracker.OnFrameDelivered(RtpPacketInfos(
+      {RtpPacketInfo(kSsrc, {kCsrcs[0], kCsrcs[1]}, kSequenceNumber,
+                     kRtpTimestamp, kAudioLevel, kReceiveTimeMs)}));
+
+  int64_t timestamp_ms = clock.TimeInMilliseconds();
+
+  EXPECT_THAT(
+      tracker.GetSources(),
+      ElementsAre(RtpSource(timestamp_ms, kSsrc, RtpSourceType::SSRC,
+                            kAudioLevel, kRtpTimestamp),
+                  RtpSource(timestamp_ms, kCsrcs[1], RtpSourceType::CSRC,
+                            kAudioLevel, kRtpTimestamp),
+                  RtpSource(timestamp_ms, kCsrcs[0], RtpSourceType::CSRC,
+                            kAudioLevel, kRtpTimestamp)));
+}
+
+TEST(SourceTrackerTest, OnFrameDeliveredUpdatesSources) {
+  constexpr uint32_t kSsrc = 10;
+  constexpr uint32_t kCsrcs0 = 20;
+  constexpr uint32_t kCsrcs1 = 21;
+  constexpr uint32_t kCsrcs2 = 22;
+  constexpr uint16_t kSequenceNumber0 = 30;
+  constexpr uint16_t kSequenceNumber1 = 31;
+  constexpr uint32_t kRtpTimestamp0 = 40;
+  constexpr uint32_t kRtpTimestamp1 = 41;
+  constexpr absl::optional<uint8_t> kAudioLevel0 = 50;
+  constexpr absl::optional<uint8_t> kAudioLevel1 = absl::nullopt;
+  constexpr int64_t kReceiveTimeMs0 = 60;
+  constexpr int64_t kReceiveTimeMs1 = 61;
+
+  SimulatedClock clock(1000000000000ULL);
+  SourceTracker tracker(&clock);
+
+  tracker.OnFrameDelivered(RtpPacketInfos(
+      {RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs1}, kSequenceNumber0,
+                     kRtpTimestamp0, kAudioLevel0, kReceiveTimeMs0)}));
+
+  int64_t timestamp_ms_0 = clock.TimeInMilliseconds();
+
+  clock.AdvanceTimeMilliseconds(17);
+
+  tracker.OnFrameDelivered(RtpPacketInfos(
+      {RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs2}, kSequenceNumber1,
+                     kRtpTimestamp1, kAudioLevel1, kReceiveTimeMs1)}));
+
+  int64_t timestamp_ms_1 = clock.TimeInMilliseconds();
+
+  EXPECT_THAT(
+      tracker.GetSources(),
+      ElementsAre(RtpSource(timestamp_ms_1, kSsrc, RtpSourceType::SSRC,
+                            kAudioLevel1, kRtpTimestamp1),
+                  RtpSource(timestamp_ms_1, kCsrcs2, RtpSourceType::CSRC,
+                            kAudioLevel1, kRtpTimestamp1),
+                  RtpSource(timestamp_ms_1, kCsrcs0, RtpSourceType::CSRC,
+                            kAudioLevel1, kRtpTimestamp1),
+                  RtpSource(timestamp_ms_0, kCsrcs1, RtpSourceType::CSRC,
+                            kAudioLevel0, kRtpTimestamp0)));
+}
+
+TEST(SourceTrackerTest, TimedOutSourcesAreRemoved) {
+  constexpr uint32_t kSsrc = 10;
+  constexpr uint32_t kCsrcs0 = 20;
+  constexpr uint32_t kCsrcs1 = 21;
+  constexpr uint32_t kCsrcs2 = 22;
+  constexpr uint16_t kSequenceNumber0 = 30;
+  constexpr uint16_t kSequenceNumber1 = 31;
+  constexpr uint32_t kRtpTimestamp0 = 40;
+  constexpr uint32_t kRtpTimestamp1 = 41;
+  constexpr absl::optional<uint8_t> kAudioLevel0 = 50;
+  constexpr absl::optional<uint8_t> kAudioLevel1 = absl::nullopt;
+  constexpr int64_t kReceiveTimeMs0 = 60;
+  constexpr int64_t kReceiveTimeMs1 = 61;
+
+  SimulatedClock clock(1000000000000ULL);
+  SourceTracker tracker(&clock);
+
+  tracker.OnFrameDelivered(RtpPacketInfos(
+      {RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs1}, kSequenceNumber0,
+                     kRtpTimestamp0, kAudioLevel0, kReceiveTimeMs0)}));
+
+  clock.AdvanceTimeMilliseconds(17);
+
+  tracker.OnFrameDelivered(RtpPacketInfos(
+      {RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs2}, kSequenceNumber1,
+                     kRtpTimestamp1, kAudioLevel1, kReceiveTimeMs1)}));
+
+  int64_t timestamp_ms_1 = clock.TimeInMilliseconds();
+
+  clock.AdvanceTimeMilliseconds(SourceTracker::kTimeoutMs);
+
+  EXPECT_THAT(
+      tracker.GetSources(),
+      ElementsAre(RtpSource(timestamp_ms_1, kSsrc, RtpSourceType::SSRC,
+                            kAudioLevel1, kRtpTimestamp1),
+                  RtpSource(timestamp_ms_1, kCsrcs2, RtpSourceType::CSRC,
+                            kAudioLevel1, kRtpTimestamp1),
+                  RtpSource(timestamp_ms_1, kCsrcs0, RtpSourceType::CSRC,
+                            kAudioLevel1, kRtpTimestamp1)));
+}
+
+}  // namespace webrtc