[Adaptation] Multi-processor support for injected Resources.

Because a single Resource only has a single ResourceListener, injected
Resources only gets wired up to the stream's ResourceAdaptationProcessor
that was last to call SetResourceListener. This could potentially lead
to the relevant stream not adapting based on the injected resource
because it got wired up to the wrong stream's processor.

This CL fixes this issue by introducing BroadcastResourceListener. By
listening to 1 resource (the injected one), it can spawn N "adapter"
resources that mirror's the injected resource's usage signal, allowing
all ResourceAdaptationProcessor's to react to the signal.

This is wired up in Call, and tests are updated to verify the signal
gets through.

Bug: chromium:1101263, webrtc:11720
Change-Id: I8a37284cb9a68f08ca1bdb1ee050b7144c451297
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/178386
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Evan Shrubsole <eshr@google.com>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31612}
diff --git a/call/BUILD.gn b/call/BUILD.gn
index 79c53b6..5f7c603 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -291,6 +291,7 @@
     "../system_wrappers:field_trial",
     "../system_wrappers:metrics",
     "../video",
+    "adaptation:resource_adaptation",
   ]
   absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
 }
diff --git a/call/adaptation/BUILD.gn b/call/adaptation/BUILD.gn
index d070a43..055fc43 100644
--- a/call/adaptation/BUILD.gn
+++ b/call/adaptation/BUILD.gn
@@ -14,6 +14,8 @@
     "adaptation_constraint.h",
     "adaptation_listener.cc",
     "adaptation_listener.h",
+    "broadcast_resource_listener.cc",
+    "broadcast_resource_listener.h",
     "encoder_settings.cc",
     "encoder_settings.h",
     "resource_adaptation_processor.cc",
@@ -57,6 +59,7 @@
     testonly = true
 
     sources = [
+      "broadcast_resource_listener_unittest.cc",
       "resource_adaptation_processor_unittest.cc",
       "resource_unittest.cc",
       "video_source_restrictions_unittest.cc",
@@ -96,6 +99,7 @@
       "test/fake_frame_rate_provider.h",
       "test/fake_resource.cc",
       "test/fake_resource.h",
+      "test/mock_resource_listener.h",
     ]
     deps = [
       ":resource_adaptation",
diff --git a/call/adaptation/broadcast_resource_listener.cc b/call/adaptation/broadcast_resource_listener.cc
new file mode 100644
index 0000000..2a4d8ca
--- /dev/null
+++ b/call/adaptation/broadcast_resource_listener.cc
@@ -0,0 +1,120 @@
+/*
+ *  Copyright 2020 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 "call/adaptation/broadcast_resource_listener.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/critical_section.h"
+#include "rtc_base/ref_counted_object.h"
+
+namespace webrtc {
+
+// The AdapterResource redirects resource usage measurements from its parent to
+// a single ResourceListener.
+class BroadcastResourceListener::AdapterResource : public Resource {
+ public:
+  explicit AdapterResource(std::string name) : name_(std::move(name)) {}
+  ~AdapterResource() override { RTC_DCHECK(!listener_); }
+
+  // The parent is letting us know we have a usage neasurement.
+  void OnResourceUsageStateMeasured(ResourceUsageState usage_state) {
+    rtc::CritScope crit(&lock_);
+    if (!listener_)
+      return;
+    listener_->OnResourceUsageStateMeasured(this, usage_state);
+  }
+
+  // Resource implementation.
+  std::string Name() const override { return name_; }
+  void SetResourceListener(ResourceListener* listener) override {
+    rtc::CritScope crit(&lock_);
+    RTC_DCHECK(!listener_ || !listener);
+    listener_ = listener;
+  }
+
+ private:
+  const std::string name_;
+  rtc::CriticalSection lock_;
+  ResourceListener* listener_ RTC_GUARDED_BY(lock_) = nullptr;
+};
+
+BroadcastResourceListener::BroadcastResourceListener(
+    rtc::scoped_refptr<Resource> source_resource)
+    : source_resource_(source_resource), is_listening_(false) {
+  RTC_DCHECK(source_resource_);
+}
+
+BroadcastResourceListener::~BroadcastResourceListener() {
+  RTC_DCHECK(!is_listening_);
+}
+
+rtc::scoped_refptr<Resource> BroadcastResourceListener::SourceResource() const {
+  return source_resource_;
+}
+
+void BroadcastResourceListener::StartListening() {
+  rtc::CritScope crit(&lock_);
+  RTC_DCHECK(!is_listening_);
+  source_resource_->SetResourceListener(this);
+  is_listening_ = true;
+}
+
+void BroadcastResourceListener::StopListening() {
+  rtc::CritScope crit(&lock_);
+  RTC_DCHECK(is_listening_);
+  RTC_DCHECK(adapters_.empty());
+  source_resource_->SetResourceListener(nullptr);
+  is_listening_ = false;
+}
+
+rtc::scoped_refptr<Resource>
+BroadcastResourceListener::CreateAdapterResource() {
+  rtc::CritScope crit(&lock_);
+  RTC_DCHECK(is_listening_);
+  rtc::scoped_refptr<AdapterResource> adapter =
+      new rtc::RefCountedObject<AdapterResource>(source_resource_->Name() +
+                                                 "Adapter");
+  adapters_.push_back(adapter);
+  return adapter;
+}
+
+void BroadcastResourceListener::RemoveAdapterResource(
+    rtc::scoped_refptr<Resource> resource) {
+  rtc::CritScope crit(&lock_);
+  auto it = std::find(adapters_.begin(), adapters_.end(), resource);
+  RTC_DCHECK(it != adapters_.end());
+  adapters_.erase(it);
+}
+
+std::vector<rtc::scoped_refptr<Resource>>
+BroadcastResourceListener::GetAdapterResources() {
+  std::vector<rtc::scoped_refptr<Resource>> resources;
+  rtc::CritScope crit(&lock_);
+  for (const auto& adapter : adapters_) {
+    resources.push_back(adapter);
+  }
+  return resources;
+}
+
+void BroadcastResourceListener::OnResourceUsageStateMeasured(
+    rtc::scoped_refptr<Resource> resource,
+    ResourceUsageState usage_state) {
+  RTC_DCHECK_EQ(resource, source_resource_);
+  rtc::CritScope crit(&lock_);
+  for (const auto& adapter : adapters_) {
+    adapter->OnResourceUsageStateMeasured(usage_state);
+  }
+}
+
+}  // namespace webrtc
diff --git a/call/adaptation/broadcast_resource_listener.h b/call/adaptation/broadcast_resource_listener.h
new file mode 100644
index 0000000..f0d035d
--- /dev/null
+++ b/call/adaptation/broadcast_resource_listener.h
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2020 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 CALL_ADAPTATION_BROADCAST_RESOURCE_LISTENER_H_
+#define CALL_ADAPTATION_BROADCAST_RESOURCE_LISTENER_H_
+
+#include <vector>
+
+#include "api/adaptation/resource.h"
+#include "api/scoped_refptr.h"
+#include "rtc_base/critical_section.h"
+
+namespace webrtc {
+
+// Responsible for forwarding 1 resource usage measurement to N listeners by
+// creating N "adapter" resources.
+//
+// Example:
+// If we have ResourceA, ResourceListenerX and ResourceListenerY we can create a
+// BroadcastResourceListener that listens to ResourceA, use CreateAdapter() to
+// spawn adapter resources ResourceX and ResourceY and let ResourceListenerX
+// listen to ResourceX and ResourceListenerY listen to ResourceY. When ResourceA
+// makes a measurement it will be echoed by both ResourceX and ResourceY.
+//
+// TODO(https://crbug.com/webrtc/11565): When the ResourceAdaptationProcessor is
+// moved to call there will only be one ResourceAdaptationProcessor that needs
+// to listen to the injected resources. When this is the case, delete this class
+// and DCHECK that a Resource's listener is never overwritten.
+class BroadcastResourceListener : public ResourceListener {
+ public:
+  explicit BroadcastResourceListener(
+      rtc::scoped_refptr<Resource> source_resource);
+  ~BroadcastResourceListener() override;
+
+  rtc::scoped_refptr<Resource> SourceResource() const;
+  void StartListening();
+  void StopListening();
+
+  // Creates a Resource that redirects any resource usage measurements that
+  // BroadcastResourceListener receives to its listener.
+  rtc::scoped_refptr<Resource> CreateAdapterResource();
+
+  // Unregister the adapter from the BroadcastResourceListener; it will no
+  // longer receive resource usage measurement and will no longer be referenced.
+  // Use this to prevent memory leaks of old adapters.
+  void RemoveAdapterResource(rtc::scoped_refptr<Resource> resource);
+  std::vector<rtc::scoped_refptr<Resource>> GetAdapterResources();
+
+  // ResourceListener implementation.
+  void OnResourceUsageStateMeasured(rtc::scoped_refptr<Resource> resource,
+                                    ResourceUsageState usage_state) override;
+
+ private:
+  class AdapterResource;
+  friend class AdapterResource;
+
+  const rtc::scoped_refptr<Resource> source_resource_;
+  rtc::CriticalSection lock_;
+  bool is_listening_ RTC_GUARDED_BY(lock_);
+  // The AdapterResource unregisters itself prior to destruction, guaranteeing
+  // that these pointers are safe to use.
+  std::vector<rtc::scoped_refptr<AdapterResource>> adapters_
+      RTC_GUARDED_BY(lock_);
+};
+
+}  // namespace webrtc
+
+#endif  // CALL_ADAPTATION_BROADCAST_RESOURCE_LISTENER_H_
diff --git a/call/adaptation/broadcast_resource_listener_unittest.cc b/call/adaptation/broadcast_resource_listener_unittest.cc
new file mode 100644
index 0000000..9cd8050
--- /dev/null
+++ b/call/adaptation/broadcast_resource_listener_unittest.cc
@@ -0,0 +1,121 @@
+/*
+ *  Copyright 2020 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 "call/adaptation/broadcast_resource_listener.h"
+
+#include "call/adaptation/test/fake_resource.h"
+#include "call/adaptation/test/mock_resource_listener.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+using ::testing::_;
+using ::testing::StrictMock;
+
+TEST(BroadcastResourceListenerTest, CreateAndRemoveAdapterResource) {
+  rtc::scoped_refptr<FakeResource> source_resource =
+      FakeResource::Create("SourceResource");
+  BroadcastResourceListener broadcast_resource_listener(source_resource);
+  broadcast_resource_listener.StartListening();
+
+  EXPECT_TRUE(broadcast_resource_listener.GetAdapterResources().empty());
+  rtc::scoped_refptr<Resource> adapter =
+      broadcast_resource_listener.CreateAdapterResource();
+  StrictMock<MockResourceListener> listener;
+  adapter->SetResourceListener(&listener);
+  EXPECT_EQ(std::vector<rtc::scoped_refptr<Resource>>{adapter},
+            broadcast_resource_listener.GetAdapterResources());
+
+  // The removed adapter is not referenced by the broadcaster.
+  broadcast_resource_listener.RemoveAdapterResource(adapter);
+  EXPECT_TRUE(broadcast_resource_listener.GetAdapterResources().empty());
+  // The removed adapter is not forwarding measurements.
+  EXPECT_CALL(listener, OnResourceUsageStateMeasured(_, _)).Times(0);
+  source_resource->SetUsageState(ResourceUsageState::kOveruse);
+  // Cleanup.
+  adapter->SetResourceListener(nullptr);
+  broadcast_resource_listener.StopListening();
+}
+
+TEST(BroadcastResourceListenerTest, AdapterNameIsBasedOnSourceResourceName) {
+  rtc::scoped_refptr<FakeResource> source_resource =
+      FakeResource::Create("FooBarResource");
+  BroadcastResourceListener broadcast_resource_listener(source_resource);
+  broadcast_resource_listener.StartListening();
+
+  rtc::scoped_refptr<Resource> adapter =
+      broadcast_resource_listener.CreateAdapterResource();
+  EXPECT_EQ("FooBarResourceAdapter", adapter->Name());
+
+  broadcast_resource_listener.RemoveAdapterResource(adapter);
+  broadcast_resource_listener.StopListening();
+}
+
+TEST(BroadcastResourceListenerTest, AdaptersForwardsUsageMeasurements) {
+  rtc::scoped_refptr<FakeResource> source_resource =
+      FakeResource::Create("SourceResource");
+  BroadcastResourceListener broadcast_resource_listener(source_resource);
+  broadcast_resource_listener.StartListening();
+
+  StrictMock<MockResourceListener> destination_listener1;
+  StrictMock<MockResourceListener> destination_listener2;
+  rtc::scoped_refptr<Resource> adapter1 =
+      broadcast_resource_listener.CreateAdapterResource();
+  adapter1->SetResourceListener(&destination_listener1);
+  rtc::scoped_refptr<Resource> adapter2 =
+      broadcast_resource_listener.CreateAdapterResource();
+  adapter2->SetResourceListener(&destination_listener2);
+
+  // Expect kOveruse to be echoed.
+  EXPECT_CALL(destination_listener1, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([adapter1](rtc::scoped_refptr<Resource> resource,
+                           ResourceUsageState usage_state) {
+        EXPECT_EQ(adapter1, resource);
+        EXPECT_EQ(ResourceUsageState::kOveruse, usage_state);
+      });
+  EXPECT_CALL(destination_listener2, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([adapter2](rtc::scoped_refptr<Resource> resource,
+                           ResourceUsageState usage_state) {
+        EXPECT_EQ(adapter2, resource);
+        EXPECT_EQ(ResourceUsageState::kOveruse, usage_state);
+      });
+  source_resource->SetUsageState(ResourceUsageState::kOveruse);
+
+  // Expect kUnderuse to be echoed.
+  EXPECT_CALL(destination_listener1, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([adapter1](rtc::scoped_refptr<Resource> resource,
+                           ResourceUsageState usage_state) {
+        EXPECT_EQ(adapter1, resource);
+        EXPECT_EQ(ResourceUsageState::kUnderuse, usage_state);
+      });
+  EXPECT_CALL(destination_listener2, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([adapter2](rtc::scoped_refptr<Resource> resource,
+                           ResourceUsageState usage_state) {
+        EXPECT_EQ(adapter2, resource);
+        EXPECT_EQ(ResourceUsageState::kUnderuse, usage_state);
+      });
+  source_resource->SetUsageState(ResourceUsageState::kUnderuse);
+
+  // Adapters have to be unregistered before they or the broadcaster is
+  // destroyed, ensuring safe use of raw pointers.
+  adapter1->SetResourceListener(nullptr);
+  adapter2->SetResourceListener(nullptr);
+
+  broadcast_resource_listener.RemoveAdapterResource(adapter1);
+  broadcast_resource_listener.RemoveAdapterResource(adapter2);
+  broadcast_resource_listener.StopListening();
+}
+
+}  // namespace webrtc
diff --git a/call/adaptation/resource_unittest.cc b/call/adaptation/resource_unittest.cc
index ee57f91..a2291df 100644
--- a/call/adaptation/resource_unittest.cc
+++ b/call/adaptation/resource_unittest.cc
@@ -14,6 +14,7 @@
 
 #include "api/scoped_refptr.h"
 #include "call/adaptation/test/fake_resource.h"
+#include "call/adaptation/test/mock_resource_listener.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 
@@ -22,15 +23,6 @@
 using ::testing::_;
 using ::testing::StrictMock;
 
-class MockResourceListener : public ResourceListener {
- public:
-  MOCK_METHOD(void,
-              OnResourceUsageStateMeasured,
-              (rtc::scoped_refptr<Resource> resource,
-               ResourceUsageState usage_state),
-              (override));
-};
-
 class ResourceTest : public ::testing::Test {
  public:
   ResourceTest() : fake_resource_(FakeResource::Create("FakeResource")) {}
diff --git a/call/adaptation/test/mock_resource_listener.h b/call/adaptation/test/mock_resource_listener.h
new file mode 100644
index 0000000..f0f998f
--- /dev/null
+++ b/call/adaptation/test/mock_resource_listener.h
@@ -0,0 +1,31 @@
+/*
+ *  Copyright 2020 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 CALL_ADAPTATION_TEST_MOCK_RESOURCE_LISTENER_H_
+#define CALL_ADAPTATION_TEST_MOCK_RESOURCE_LISTENER_H_
+
+#include "api/adaptation/resource.h"
+
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockResourceListener : public ResourceListener {
+ public:
+  MOCK_METHOD(void,
+              OnResourceUsageStateMeasured,
+              (rtc::scoped_refptr<Resource> resource,
+               ResourceUsageState usage_state),
+              (override));
+};
+
+}  // namespace webrtc
+
+#endif  // CALL_ADAPTATION_TEST_MOCK_RESOURCE_LISTENER_H_
diff --git a/call/call.cc b/call/call.cc
index a76e737..ace83be 100644
--- a/call/call.cc
+++ b/call/call.cc
@@ -25,6 +25,7 @@
 #include "audio/audio_receive_stream.h"
 #include "audio/audio_send_stream.h"
 #include "audio/audio_state.h"
+#include "call/adaptation/broadcast_resource_listener.h"
 #include "call/bitrate_allocator.h"
 #include "call/flexfec_receive_stream_impl.h"
 #include "call/receive_time_calculator.h"
@@ -168,6 +169,47 @@
 
 namespace internal {
 
+// Wraps an injected resource in a BroadcastResourceListener and handles adding
+// and removing adapter resources to individual VideoSendStreams.
+class ResourceVideoSendStreamForwarder {
+ public:
+  ResourceVideoSendStreamForwarder(
+      rtc::scoped_refptr<webrtc::Resource> resource)
+      : broadcast_resource_listener_(resource) {
+    broadcast_resource_listener_.StartListening();
+  }
+  ~ResourceVideoSendStreamForwarder() {
+    RTC_DCHECK(adapter_resources_.empty());
+    broadcast_resource_listener_.StopListening();
+  }
+
+  rtc::scoped_refptr<webrtc::Resource> Resource() const {
+    return broadcast_resource_listener_.SourceResource();
+  }
+
+  void OnCreateVideoSendStream(VideoSendStream* video_send_stream) {
+    RTC_DCHECK(adapter_resources_.find(video_send_stream) ==
+               adapter_resources_.end());
+    auto adapter_resource =
+        broadcast_resource_listener_.CreateAdapterResource();
+    video_send_stream->AddAdaptationResource(adapter_resource);
+    adapter_resources_.insert(
+        std::make_pair(video_send_stream, adapter_resource));
+  }
+
+  void OnDestroyVideoSendStream(VideoSendStream* video_send_stream) {
+    auto it = adapter_resources_.find(video_send_stream);
+    RTC_DCHECK(it != adapter_resources_.end());
+    broadcast_resource_listener_.RemoveAdapterResource(it->second);
+    adapter_resources_.erase(it);
+  }
+
+ private:
+  BroadcastResourceListener broadcast_resource_listener_;
+  std::map<VideoSendStream*, rtc::scoped_refptr<webrtc::Resource>>
+      adapter_resources_;
+};
+
 class Call final : public webrtc::Call,
                    public PacketReceiver,
                    public RecoveredPacketReceiver,
@@ -335,8 +377,9 @@
       RTC_GUARDED_BY(worker_thread_);
   std::set<VideoSendStream*> video_send_streams_ RTC_GUARDED_BY(worker_thread_);
 
-  std::vector<rtc::scoped_refptr<Resource>> adaptation_resources_
-      RTC_GUARDED_BY(worker_thread_);
+  // Each forwarder wraps an adaptation resource that was added to the call.
+  std::vector<std::unique_ptr<ResourceVideoSendStreamForwarder>>
+      adaptation_resource_forwarders_ RTC_GUARDED_BY(worker_thread_);
 
   using RtpStateMap = std::map<uint32_t, RtpState>;
   RtpStateMap suspended_audio_send_ssrcs_ RTC_GUARDED_BY(worker_thread_);
@@ -860,9 +903,9 @@
     video_send_ssrcs_[ssrc] = send_stream;
   }
   video_send_streams_.insert(send_stream);
-  // Add resources that were previously added to the call to the new stream.
-  for (const auto& adaptation_resource : adaptation_resources_) {
-    send_stream->AddAdaptationResource(adaptation_resource);
+  // Forward resources that were previously added to the call to the new stream.
+  for (const auto& resource_forwarder : adaptation_resource_forwarders_) {
+    resource_forwarder->OnCreateVideoSendStream(send_stream);
   }
 
   UpdateAggregateNetworkState();
@@ -902,6 +945,10 @@
       ++it;
     }
   }
+  // Stop forwarding resources to the stream being destroyed.
+  for (const auto& resource_forwarder : adaptation_resource_forwarders_) {
+    resource_forwarder->OnDestroyVideoSendStream(send_stream_impl);
+  }
   video_send_streams_.erase(send_stream_impl);
 
   RTC_CHECK(send_stream_impl != nullptr);
@@ -1030,9 +1077,11 @@
 
 void Call::AddAdaptationResource(rtc::scoped_refptr<Resource> resource) {
   RTC_DCHECK_RUN_ON(worker_thread_);
-  adaptation_resources_.push_back(resource);
-  for (VideoSendStream* stream : video_send_streams_) {
-    stream->AddAdaptationResource(resource);
+  adaptation_resource_forwarders_.push_back(
+      std::make_unique<ResourceVideoSendStreamForwarder>(resource));
+  const auto& resource_forwarder = adaptation_resource_forwarders_.back();
+  for (VideoSendStream* send_stream : video_send_streams_) {
+    resource_forwarder->OnCreateVideoSendStream(send_stream);
   }
 }
 
diff --git a/call/call_unittest.cc b/call/call_unittest.cc
index d7a1f23..e165107 100644
--- a/call/call_unittest.cc
+++ b/call/call_unittest.cc
@@ -26,6 +26,7 @@
 #include "audio/audio_receive_stream.h"
 #include "audio/audio_send_stream.h"
 #include "call/adaptation/test/fake_resource.h"
+#include "call/adaptation/test/mock_resource_listener.h"
 #include "call/audio_state.h"
 #include "modules/audio_device/include/mock_audio_device.h"
 #include "modules/audio_processing/include/mock_audio_processing.h"
@@ -38,7 +39,9 @@
 
 namespace {
 
+using ::testing::_;
 using ::testing::Contains;
+using ::testing::StrictMock;
 
 struct CallHelper {
   explicit CallHelper(bool use_null_audio_processing) {
@@ -72,6 +75,20 @@
 
 namespace webrtc {
 
+namespace {
+
+rtc::scoped_refptr<Resource> FindResourceWhoseNameContains(
+    const std::vector<rtc::scoped_refptr<Resource>>& resources,
+    const std::string& name_contains) {
+  for (const auto& resource : resources) {
+    if (resource->Name().find(name_contains) != std::string::npos)
+      return resource;
+  }
+  return nullptr;
+}
+
+}  // namespace
+
 TEST(CallTest, ConstructDestruct) {
   for (bool use_null_audio_processing : {false, true}) {
     CallHelper call(use_null_audio_processing);
@@ -345,14 +362,50 @@
       bitrate_allocator_factory.get();
   VideoEncoderConfig encoder_config;
   encoder_config.max_bitrate_bps = 1337;
-  VideoSendStream* stream =
-      call->CreateVideoSendStream(std::move(config), std::move(encoder_config));
-  EXPECT_NE(stream, nullptr);
-  // Add a fake resource. It should get added to the stream.
+  VideoSendStream* stream1 =
+      call->CreateVideoSendStream(config.Copy(), encoder_config.Copy());
+  EXPECT_NE(stream1, nullptr);
+  config.rtp.ssrcs = {43};
+  VideoSendStream* stream2 =
+      call->CreateVideoSendStream(config.Copy(), encoder_config.Copy());
+  EXPECT_NE(stream2, nullptr);
+  // Add a fake resource.
   auto fake_resource = FakeResource::Create("FakeResource");
   call->AddAdaptationResource(fake_resource);
-  EXPECT_THAT(stream->GetAdaptationResources(), Contains(fake_resource));
-  call->DestroyVideoSendStream(stream);
+  // An adapter resource mirroring the |fake_resource| should now be present on
+  // both streams.
+  auto injected_resource1 = FindResourceWhoseNameContains(
+      stream1->GetAdaptationResources(), fake_resource->Name());
+  EXPECT_TRUE(injected_resource1);
+  auto injected_resource2 = FindResourceWhoseNameContains(
+      stream2->GetAdaptationResources(), fake_resource->Name());
+  EXPECT_TRUE(injected_resource2);
+  // Overwrite the real resource listeners with mock ones to verify the signal
+  // gets through.
+  injected_resource1->SetResourceListener(nullptr);
+  StrictMock<MockResourceListener> resource_listener1;
+  EXPECT_CALL(resource_listener1, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([injected_resource1](rtc::scoped_refptr<Resource> resource,
+                                     ResourceUsageState usage_state) {
+        EXPECT_EQ(injected_resource1, resource);
+        EXPECT_EQ(ResourceUsageState::kOveruse, usage_state);
+      });
+  injected_resource1->SetResourceListener(&resource_listener1);
+  injected_resource2->SetResourceListener(nullptr);
+  StrictMock<MockResourceListener> resource_listener2;
+  EXPECT_CALL(resource_listener2, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([injected_resource2](rtc::scoped_refptr<Resource> resource,
+                                     ResourceUsageState usage_state) {
+        EXPECT_EQ(injected_resource2, resource);
+        EXPECT_EQ(ResourceUsageState::kOveruse, usage_state);
+      });
+  injected_resource2->SetResourceListener(&resource_listener2);
+  // The kOveruse signal should get to our resource listeners.
+  fake_resource->SetUsageState(ResourceUsageState::kOveruse);
+  call->DestroyVideoSendStream(stream1);
+  call->DestroyVideoSendStream(stream2);
 }
 
 TEST(CallTest, AddAdaptationResourceBeforeCreatingVideoSendStream) {
@@ -374,12 +427,47 @@
       bitrate_allocator_factory.get();
   VideoEncoderConfig encoder_config;
   encoder_config.max_bitrate_bps = 1337;
-  VideoSendStream* stream =
-      call->CreateVideoSendStream(std::move(config), std::move(encoder_config));
-  EXPECT_NE(stream, nullptr);
-  // The fake resource should automatically get added to the stream.
-  EXPECT_THAT(stream->GetAdaptationResources(), Contains(fake_resource));
-  call->DestroyVideoSendStream(stream);
+  VideoSendStream* stream1 =
+      call->CreateVideoSendStream(config.Copy(), encoder_config.Copy());
+  EXPECT_NE(stream1, nullptr);
+  config.rtp.ssrcs = {43};
+  VideoSendStream* stream2 =
+      call->CreateVideoSendStream(config.Copy(), encoder_config.Copy());
+  EXPECT_NE(stream2, nullptr);
+  // An adapter resource mirroring the |fake_resource| should be present on both
+  // streams.
+  auto injected_resource1 = FindResourceWhoseNameContains(
+      stream1->GetAdaptationResources(), fake_resource->Name());
+  EXPECT_TRUE(injected_resource1);
+  auto injected_resource2 = FindResourceWhoseNameContains(
+      stream2->GetAdaptationResources(), fake_resource->Name());
+  EXPECT_TRUE(injected_resource2);
+  // Overwrite the real resource listeners with mock ones to verify the signal
+  // gets through.
+  injected_resource1->SetResourceListener(nullptr);
+  StrictMock<MockResourceListener> resource_listener1;
+  EXPECT_CALL(resource_listener1, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([injected_resource1](rtc::scoped_refptr<Resource> resource,
+                                     ResourceUsageState usage_state) {
+        EXPECT_EQ(injected_resource1, resource);
+        EXPECT_EQ(ResourceUsageState::kUnderuse, usage_state);
+      });
+  injected_resource1->SetResourceListener(&resource_listener1);
+  injected_resource2->SetResourceListener(nullptr);
+  StrictMock<MockResourceListener> resource_listener2;
+  EXPECT_CALL(resource_listener2, OnResourceUsageStateMeasured(_, _))
+      .Times(1)
+      .WillOnce([injected_resource2](rtc::scoped_refptr<Resource> resource,
+                                     ResourceUsageState usage_state) {
+        EXPECT_EQ(injected_resource2, resource);
+        EXPECT_EQ(ResourceUsageState::kUnderuse, usage_state);
+      });
+  injected_resource2->SetResourceListener(&resource_listener2);
+  // The kUnderuse signal should get to our resource listeners.
+  fake_resource->SetUsageState(ResourceUsageState::kUnderuse);
+  call->DestroyVideoSendStream(stream1);
+  call->DestroyVideoSendStream(stream2);
 }
 
 TEST(CallTest, SharedModuleThread) {