Add audio level to CSRC class

This patch adds (optional) csrc to ContributingSources.
This will be used if using virtual audio ssrc, since
the audio level is otherwise unaccessible in that configuration.

BUG=webrtc:3333

Change-Id: Ied263b8f0850553cd637fd6bead373ed4252fd1e
Reviewed-on: https://webrtc-review.googlesource.com/c/109281
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25516}
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index ac02482..704ba796 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -437,7 +437,9 @@
     if (has_audio_level)
       last_received_rtp_audio_level_ = audio_level;
     std::vector<uint32_t> csrcs = packet.Csrcs();
-    contributing_sources_.Update(now_ms, csrcs);
+    contributing_sources_.Update(
+        now_ms, csrcs,
+        has_audio_level ? absl::optional<uint8_t>(audio_level) : absl::nullopt);
   }
 
   // Store playout timestamp for the received RTP packet
diff --git a/modules/rtp_rtcp/source/contributing_sources.cc b/modules/rtp_rtcp/source/contributing_sources.cc
index 853706c..64dc443 100644
--- a/modules/rtp_rtcp/source/contributing_sources.cc
+++ b/modules/rtp_rtcp/source/contributing_sources.cc
@@ -25,9 +25,11 @@
 ContributingSources::~ContributingSources() = default;
 
 void ContributingSources::Update(int64_t now_ms,
-                                 rtc::ArrayView<const uint32_t> csrcs) {
+                                 rtc::ArrayView<const uint32_t> csrcs,
+                                 absl::optional<uint8_t> audio_level) {
+  Entry entry = { now_ms, audio_level };
   for (uint32_t csrc : csrcs) {
-    last_seen_ms_[csrc] = now_ms;
+    active_csrcs_[csrc] = entry;
   }
   if (!next_pruning_ms_) {
     next_pruning_ms_ = now_ms + kPruningIntervalMs;
@@ -43,9 +45,16 @@
 // non-const.
 std::vector<RtpSource> ContributingSources::GetSources(int64_t now_ms) const {
   std::vector<RtpSource> sources;
-  for (auto& record : last_seen_ms_) {
-    if (record.second >= now_ms - kHistoryMs) {
-      sources.emplace_back(record.second, record.first, RtpSourceType::CSRC);
+  for (auto& record : active_csrcs_) {
+    if (record.second.last_seen_ms >= now_ms - kHistoryMs) {
+      if (record.second.audio_level.has_value()) {
+        sources.emplace_back(record.second.last_seen_ms, record.first,
+                             RtpSourceType::CSRC,
+                             *record.second.audio_level);
+      } else {
+        sources.emplace_back(record.second.last_seen_ms, record.first,
+                             RtpSourceType::CSRC);
+      }
     }
   }
 
@@ -54,15 +63,20 @@
 
 // Delete stale entries.
 void ContributingSources::DeleteOldEntries(int64_t now_ms) {
-  for (auto it = last_seen_ms_.begin(); it != last_seen_ms_.end();) {
-    if (it->second >= now_ms - kHistoryMs) {
+  for (auto it = active_csrcs_.begin(); it != active_csrcs_.end();) {
+    if (it->second.last_seen_ms >= now_ms - kHistoryMs) {
       // Still relevant.
       ++it;
     } else {
-      it = last_seen_ms_.erase(it);
+      it = active_csrcs_.erase(it);
     }
   }
   next_pruning_ms_ = now_ms + kPruningIntervalMs;
 }
 
+ContributingSources::Entry::Entry() = default;
+ContributingSources::Entry::Entry(int64_t timestamp_ms,
+                                  absl::optional<uint8_t> audio_level_arg)
+    : last_seen_ms(timestamp_ms), audio_level(audio_level_arg) {}
+
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/contributing_sources.h b/modules/rtp_rtcp/source/contributing_sources.h
index b6201ce..5e34539 100644
--- a/modules/rtp_rtcp/source/contributing_sources.h
+++ b/modules/rtp_rtcp/source/contributing_sources.h
@@ -32,18 +32,25 @@
   ContributingSources();
   ~ContributingSources();
 
-  // TODO(bugs.webrtc.org/3333): Needs to be extended with audio-level, to
-  // support RFC6465.
-  void Update(int64_t now_ms, rtc::ArrayView<const uint32_t> csrcs);
+  void Update(int64_t now_ms, rtc::ArrayView<const uint32_t> csrcs,
+              absl::optional<uint8_t> audio_level);
 
   // Returns contributing sources seen the last 10 s.
   std::vector<RtpSource> GetSources(int64_t now_ms) const;
 
  private:
+  struct Entry {
+    Entry();
+    Entry(int64_t timestamp_ms, absl::optional<uint8_t> audio_level);
+
+    int64_t last_seen_ms;
+    absl::optional<uint8_t> audio_level;
+  };
+
   void DeleteOldEntries(int64_t now_ms);
 
   // Indexed by csrc.
-  std::map<uint32_t, int64_t> last_seen_ms_;
+  std::map<uint32_t, Entry> active_csrcs_;
   absl::optional<int64_t> next_pruning_ms_;
 };
 
diff --git a/modules/rtp_rtcp/source/contributing_sources_unittest.cc b/modules/rtp_rtcp/source/contributing_sources_unittest.cc
index 8b22d26..5f1d8d3 100644
--- a/modules/rtp_rtcp/source/contributing_sources_unittest.cc
+++ b/modules/rtp_rtcp/source/contributing_sources_unittest.cc
@@ -30,7 +30,7 @@
   ContributingSources csrcs;
   constexpr uint32_t kCsrcs[] = {kCsrc1, kCsrc2};
   constexpr int64_t kTime1 = 10;
-  csrcs.Update(kTime1, kCsrcs);
+  csrcs.Update(kTime1, kCsrcs, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime1),
       UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
@@ -45,12 +45,12 @@
   constexpr uint32_t kCsrcs2[] = {kCsrc2, kCsrc3};
   constexpr int64_t kTime1 = 10;
   constexpr int64_t kTime2 = kTime1 + 5 * rtc::kNumMillisecsPerSec;
-  csrcs.Update(kTime1, kCsrcs1);
+  csrcs.Update(kTime1, kCsrcs1, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime1),
       UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
                            RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC)));
-  csrcs.Update(kTime2, kCsrcs2);
+  csrcs.Update(kTime2, kCsrcs2, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime2),
       UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
@@ -65,12 +65,12 @@
   constexpr int64_t kTime1 = 10;
   constexpr int64_t kTime2 = kTime1 + 5 * rtc::kNumMillisecsPerSec;
   constexpr int64_t kTime3 = kTime1 + 12 * rtc::kNumMillisecsPerSec;
-  csrcs.Update(kTime1, kCsrcs1);
+  csrcs.Update(kTime1, kCsrcs1, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime1),
       UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
                            RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC)));
-  csrcs.Update(kTime2, kCsrcs2);
+  csrcs.Update(kTime2, kCsrcs2, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime3),
       UnorderedElementsAre(RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC),
@@ -84,18 +84,18 @@
   constexpr int64_t kTime1 = 10;
   constexpr int64_t kTime2 = kTime1 + 10 * rtc::kNumMillisecsPerSec;
   constexpr int64_t kTime3 = kTime1 + 20 * rtc::kNumMillisecsPerSec;
-  csrcs.Update(kTime1, kCsrcs1);
+  csrcs.Update(kTime1, kCsrcs1, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime2),
       UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
                            RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC)));
-  csrcs.Update(kTime2, kCsrcs2);
+  csrcs.Update(kTime2, kCsrcs2, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime2),
       UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
                            RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC),
                            RtpSource(kTime2, kCsrc3, RtpSourceType::CSRC)));
-  csrcs.Update(kTime3, kCsrcs2);
+  csrcs.Update(kTime3, kCsrcs2, absl::nullopt);
   EXPECT_THAT(
       csrcs.GetSources(kTime3),
       UnorderedElementsAre(RtpSource(kTime3, kCsrc2, RtpSourceType::CSRC),
@@ -108,4 +108,22 @@
                            RtpSource(kTime3, kCsrc3, RtpSourceType::CSRC)));
 }
 
+TEST(ContributingSourcesTest, AudioLevel) {
+  ContributingSources csrcs;
+  constexpr uint32_t kCsrcs[] = {kCsrc1, kCsrc2};
+  constexpr int64_t kTime1 = 10;
+  csrcs.Update(kTime1, kCsrcs, 47);
+  EXPECT_THAT(
+      csrcs.GetSources(kTime1),
+      UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC, 47),
+                           RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC, 47)));
+
+  constexpr uint32_t kCsrcsSubset[] = {kCsrc1};
+  csrcs.Update(kTime1 + 1, kCsrcsSubset, absl::nullopt);
+  EXPECT_THAT(
+      csrcs.GetSources(kTime1 + 1),
+      UnorderedElementsAre(RtpSource(kTime1 + 1, kCsrc1, RtpSourceType::CSRC),
+                           RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC, 47)));
+}
+
 }  // namespace webrtc
diff --git a/video/rtp_video_stream_receiver.cc b/video/rtp_video_stream_receiver.cc
index 5495668..abf88d4 100644
--- a/video/rtp_video_stream_receiver.cc
+++ b/video/rtp_video_stream_receiver.cc
@@ -320,7 +320,8 @@
       last_received_rtp_system_time_ms_ = now_ms;
 
       std::vector<uint32_t> csrcs = packet.Csrcs();
-      contributing_sources_.Update(now_ms, csrcs);
+      contributing_sources_.Update(now_ms, csrcs,
+                                   /* audio level */absl::nullopt);
     }
     // Periodically log the RTP header of incoming packets.
     if (now_ms - last_packet_log_ms_ > kPacketLogIntervalMs) {