xref: /aosp_15_r20/external/webrtc/call/adaptation/resource_adaptation_processor_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "call/adaptation/resource_adaptation_processor.h"
12 
13 #include "api/adaptation/resource.h"
14 #include "api/scoped_refptr.h"
15 #include "api/video/video_adaptation_counters.h"
16 #include "call/adaptation/resource_adaptation_processor_interface.h"
17 #include "call/adaptation/test/fake_frame_rate_provider.h"
18 #include "call/adaptation/test/fake_resource.h"
19 #include "call/adaptation/video_source_restrictions.h"
20 #include "call/adaptation/video_stream_input_state_provider.h"
21 #include "rtc_base/event.h"
22 #include "rtc_base/gunit.h"
23 #include "rtc_base/synchronization/mutex.h"
24 #include "rtc_base/task_queue_for_test.h"
25 #include "test/gtest.h"
26 #include "test/scoped_key_value_config.h"
27 
28 namespace webrtc {
29 
30 namespace {
31 
32 const int kDefaultFrameRate = 30;
33 const int kDefaultFrameSize = 1280 * 720;
34 constexpr TimeDelta kDefaultTimeout = TimeDelta::Seconds(5);
35 
36 class VideoSourceRestrictionsListenerForTesting
37     : public VideoSourceRestrictionsListener {
38  public:
VideoSourceRestrictionsListenerForTesting()39   VideoSourceRestrictionsListenerForTesting()
40       : restrictions_updated_count_(0),
41         restrictions_(),
42         adaptation_counters_(),
43         reason_(nullptr) {}
~VideoSourceRestrictionsListenerForTesting()44   ~VideoSourceRestrictionsListenerForTesting() override {}
45 
restrictions_updated_count() const46   size_t restrictions_updated_count() const {
47     RTC_DCHECK_RUN_ON(&sequence_checker_);
48     return restrictions_updated_count_;
49   }
restrictions() const50   VideoSourceRestrictions restrictions() const {
51     RTC_DCHECK_RUN_ON(&sequence_checker_);
52     return restrictions_;
53   }
adaptation_counters() const54   VideoAdaptationCounters adaptation_counters() const {
55     RTC_DCHECK_RUN_ON(&sequence_checker_);
56     return adaptation_counters_;
57   }
reason() const58   rtc::scoped_refptr<Resource> reason() const {
59     RTC_DCHECK_RUN_ON(&sequence_checker_);
60     return reason_;
61   }
62 
63   // VideoSourceRestrictionsListener implementation.
OnVideoSourceRestrictionsUpdated(VideoSourceRestrictions restrictions,const VideoAdaptationCounters & adaptation_counters,rtc::scoped_refptr<Resource> reason,const VideoSourceRestrictions & unfiltered_restrictions)64   void OnVideoSourceRestrictionsUpdated(
65       VideoSourceRestrictions restrictions,
66       const VideoAdaptationCounters& adaptation_counters,
67       rtc::scoped_refptr<Resource> reason,
68       const VideoSourceRestrictions& unfiltered_restrictions) override {
69     RTC_DCHECK_RUN_ON(&sequence_checker_);
70     ++restrictions_updated_count_;
71     restrictions_ = restrictions;
72     adaptation_counters_ = adaptation_counters;
73     reason_ = reason;
74   }
75 
76  private:
77   SequenceChecker sequence_checker_;
78   size_t restrictions_updated_count_ RTC_GUARDED_BY(&sequence_checker_);
79   VideoSourceRestrictions restrictions_ RTC_GUARDED_BY(&sequence_checker_);
80   VideoAdaptationCounters adaptation_counters_
81       RTC_GUARDED_BY(&sequence_checker_);
82   rtc::scoped_refptr<Resource> reason_ RTC_GUARDED_BY(&sequence_checker_);
83 };
84 
85 class ResourceAdaptationProcessorTest : public ::testing::Test {
86  public:
ResourceAdaptationProcessorTest()87   ResourceAdaptationProcessorTest()
88       : frame_rate_provider_(),
89         input_state_provider_(&frame_rate_provider_),
90         resource_(FakeResource::Create("FakeResource")),
91         other_resource_(FakeResource::Create("OtherFakeResource")),
92         video_stream_adapter_(
93             std::make_unique<VideoStreamAdapter>(&input_state_provider_,
94                                                  &frame_rate_provider_,
95                                                  field_trials_)),
96         processor_(std::make_unique<ResourceAdaptationProcessor>(
97             video_stream_adapter_.get())) {
98     video_stream_adapter_->AddRestrictionsListener(&restrictions_listener_);
99     processor_->AddResource(resource_);
100     processor_->AddResource(other_resource_);
101   }
~ResourceAdaptationProcessorTest()102   ~ResourceAdaptationProcessorTest() override {
103     if (processor_) {
104       DestroyProcessor();
105     }
106   }
107 
SetInputStates(bool has_input,int fps,int frame_size)108   void SetInputStates(bool has_input, int fps, int frame_size) {
109     input_state_provider_.OnHasInputChanged(has_input);
110     frame_rate_provider_.set_fps(fps);
111     input_state_provider_.OnFrameSizeObserved(frame_size);
112   }
113 
RestrictSource(VideoSourceRestrictions restrictions)114   void RestrictSource(VideoSourceRestrictions restrictions) {
115     SetInputStates(
116         true, restrictions.max_frame_rate().value_or(kDefaultFrameRate),
117         restrictions.target_pixels_per_frame().has_value()
118             ? restrictions.target_pixels_per_frame().value()
119             : restrictions.max_pixels_per_frame().value_or(kDefaultFrameSize));
120   }
121 
DestroyProcessor()122   void DestroyProcessor() {
123     if (resource_) {
124       processor_->RemoveResource(resource_);
125     }
126     if (other_resource_) {
127       processor_->RemoveResource(other_resource_);
128     }
129     video_stream_adapter_->RemoveRestrictionsListener(&restrictions_listener_);
130     processor_.reset();
131   }
132 
WaitUntilTaskQueueIdle()133   static void WaitUntilTaskQueueIdle() {
134     ASSERT_TRUE(rtc::Thread::Current()->ProcessMessages(0));
135   }
136 
137  protected:
138   rtc::AutoThread main_thread_;
139   webrtc::test::ScopedKeyValueConfig field_trials_;
140   FakeFrameRateProvider frame_rate_provider_;
141   VideoStreamInputStateProvider input_state_provider_;
142   rtc::scoped_refptr<FakeResource> resource_;
143   rtc::scoped_refptr<FakeResource> other_resource_;
144   std::unique_ptr<VideoStreamAdapter> video_stream_adapter_;
145   std::unique_ptr<ResourceAdaptationProcessor> processor_;
146   VideoSourceRestrictionsListenerForTesting restrictions_listener_;
147 };
148 
149 }  // namespace
150 
TEST_F(ResourceAdaptationProcessorTest,DisabledByDefault)151 TEST_F(ResourceAdaptationProcessorTest, DisabledByDefault) {
152   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
153   // Adaptation does not happen when disabled.
154   resource_->SetUsageState(ResourceUsageState::kOveruse);
155   EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count());
156 }
157 
TEST_F(ResourceAdaptationProcessorTest,InsufficientInput)158 TEST_F(ResourceAdaptationProcessorTest, InsufficientInput) {
159   video_stream_adapter_->SetDegradationPreference(
160       DegradationPreference::MAINTAIN_FRAMERATE);
161   // Adaptation does not happen if input is insufficient.
162   // When frame size is missing (OnFrameSizeObserved not called yet).
163   input_state_provider_.OnHasInputChanged(true);
164   resource_->SetUsageState(ResourceUsageState::kOveruse);
165   EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count());
166   // When "has input" is missing.
167   SetInputStates(false, kDefaultFrameRate, kDefaultFrameSize);
168   resource_->SetUsageState(ResourceUsageState::kOveruse);
169   EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count());
170   // Note: frame rate cannot be missing, if unset it is 0.
171 }
172 
173 // These tests verify that restrictions are applied, but not exactly how much
174 // the source is restricted. This ensures that the VideoStreamAdapter is wired
175 // up correctly but not exactly how the VideoStreamAdapter generates
176 // restrictions. For that, see video_stream_adapter_unittest.cc.
TEST_F(ResourceAdaptationProcessorTest,OveruseTriggersRestrictingResolutionInMaintainFrameRate)177 TEST_F(ResourceAdaptationProcessorTest,
178        OveruseTriggersRestrictingResolutionInMaintainFrameRate) {
179   video_stream_adapter_->SetDegradationPreference(
180       DegradationPreference::MAINTAIN_FRAMERATE);
181   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
182   resource_->SetUsageState(ResourceUsageState::kOveruse);
183   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
184   EXPECT_TRUE(
185       restrictions_listener_.restrictions().max_pixels_per_frame().has_value());
186 }
187 
TEST_F(ResourceAdaptationProcessorTest,OveruseTriggersRestrictingFrameRateInMaintainResolution)188 TEST_F(ResourceAdaptationProcessorTest,
189        OveruseTriggersRestrictingFrameRateInMaintainResolution) {
190   video_stream_adapter_->SetDegradationPreference(
191       DegradationPreference::MAINTAIN_RESOLUTION);
192   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
193   resource_->SetUsageState(ResourceUsageState::kOveruse);
194   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
195   EXPECT_TRUE(
196       restrictions_listener_.restrictions().max_frame_rate().has_value());
197 }
198 
TEST_F(ResourceAdaptationProcessorTest,OveruseTriggersRestrictingFrameRateAndResolutionInBalanced)199 TEST_F(ResourceAdaptationProcessorTest,
200        OveruseTriggersRestrictingFrameRateAndResolutionInBalanced) {
201   video_stream_adapter_->SetDegradationPreference(
202       DegradationPreference::BALANCED);
203   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
204   // Adapting multiple times eventually resticts both frame rate and
205   // resolution. Exactly many times we need to adapt depends on
206   // BalancedDegradationSettings, VideoStreamAdapter and default input
207   // states. This test requires it to be achieved within 4 adaptations.
208   for (size_t i = 0; i < 4; ++i) {
209     resource_->SetUsageState(ResourceUsageState::kOveruse);
210     EXPECT_EQ(i + 1, restrictions_listener_.restrictions_updated_count());
211     RestrictSource(restrictions_listener_.restrictions());
212   }
213   EXPECT_TRUE(
214       restrictions_listener_.restrictions().max_pixels_per_frame().has_value());
215   EXPECT_TRUE(
216       restrictions_listener_.restrictions().max_frame_rate().has_value());
217 }
218 
TEST_F(ResourceAdaptationProcessorTest,AwaitingPreviousAdaptation)219 TEST_F(ResourceAdaptationProcessorTest, AwaitingPreviousAdaptation) {
220   video_stream_adapter_->SetDegradationPreference(
221       DegradationPreference::MAINTAIN_FRAMERATE);
222   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
223   resource_->SetUsageState(ResourceUsageState::kOveruse);
224   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
225   // If we don't restrict the source then adaptation will not happen again
226   // due to "awaiting previous adaptation". This prevents "double-adapt".
227   resource_->SetUsageState(ResourceUsageState::kOveruse);
228   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
229 }
230 
TEST_F(ResourceAdaptationProcessorTest,CannotAdaptUpWhenUnrestricted)231 TEST_F(ResourceAdaptationProcessorTest, CannotAdaptUpWhenUnrestricted) {
232   video_stream_adapter_->SetDegradationPreference(
233       DegradationPreference::MAINTAIN_FRAMERATE);
234   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
235   resource_->SetUsageState(ResourceUsageState::kUnderuse);
236   EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count());
237 }
238 
TEST_F(ResourceAdaptationProcessorTest,UnderuseTakesUsBackToUnrestricted)239 TEST_F(ResourceAdaptationProcessorTest, UnderuseTakesUsBackToUnrestricted) {
240   video_stream_adapter_->SetDegradationPreference(
241       DegradationPreference::MAINTAIN_FRAMERATE);
242   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
243   resource_->SetUsageState(ResourceUsageState::kOveruse);
244   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
245   RestrictSource(restrictions_listener_.restrictions());
246   resource_->SetUsageState(ResourceUsageState::kUnderuse);
247   EXPECT_EQ(2u, restrictions_listener_.restrictions_updated_count());
248   EXPECT_EQ(VideoSourceRestrictions(), restrictions_listener_.restrictions());
249 }
250 
TEST_F(ResourceAdaptationProcessorTest,ResourcesCanNotAdaptUpIfNeverAdaptedDown)251 TEST_F(ResourceAdaptationProcessorTest,
252        ResourcesCanNotAdaptUpIfNeverAdaptedDown) {
253   video_stream_adapter_->SetDegradationPreference(
254       DegradationPreference::MAINTAIN_FRAMERATE);
255   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
256   resource_->SetUsageState(ResourceUsageState::kOveruse);
257   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
258   RestrictSource(restrictions_listener_.restrictions());
259 
260   // Other resource signals under-use
261   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
262   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
263 }
264 
TEST_F(ResourceAdaptationProcessorTest,ResourcesCanNotAdaptUpIfNotAdaptedDownAfterReset)265 TEST_F(ResourceAdaptationProcessorTest,
266        ResourcesCanNotAdaptUpIfNotAdaptedDownAfterReset) {
267   video_stream_adapter_->SetDegradationPreference(
268       DegradationPreference::MAINTAIN_FRAMERATE);
269   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
270   resource_->SetUsageState(ResourceUsageState::kOveruse);
271   EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count());
272 
273   video_stream_adapter_->ClearRestrictions();
274   EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total());
275   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
276   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
277   RestrictSource(restrictions_listener_.restrictions());
278 
279   // resource_ did not overuse after we reset the restrictions, so adapt
280   // up should be disallowed.
281   resource_->SetUsageState(ResourceUsageState::kUnderuse);
282   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
283 }
284 
TEST_F(ResourceAdaptationProcessorTest,OnlyMostLimitedResourceMayAdaptUp)285 TEST_F(ResourceAdaptationProcessorTest, OnlyMostLimitedResourceMayAdaptUp) {
286   video_stream_adapter_->SetDegradationPreference(
287       DegradationPreference::MAINTAIN_FRAMERATE);
288   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
289   resource_->SetUsageState(ResourceUsageState::kOveruse);
290   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
291   RestrictSource(restrictions_listener_.restrictions());
292   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
293   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
294   RestrictSource(restrictions_listener_.restrictions());
295 
296   // `other_resource_` is most limited, resource_ can't adapt up.
297   resource_->SetUsageState(ResourceUsageState::kUnderuse);
298   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
299   RestrictSource(restrictions_listener_.restrictions());
300   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
301   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
302   RestrictSource(restrictions_listener_.restrictions());
303 
304   // `resource_` and `other_resource_` are now most limited, so both must
305   // signal underuse to adapt up.
306   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
307   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
308   RestrictSource(restrictions_listener_.restrictions());
309   resource_->SetUsageState(ResourceUsageState::kUnderuse);
310   EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total());
311   RestrictSource(restrictions_listener_.restrictions());
312 }
313 
TEST_F(ResourceAdaptationProcessorTest,MultipleResourcesCanTriggerMultipleAdaptations)314 TEST_F(ResourceAdaptationProcessorTest,
315        MultipleResourcesCanTriggerMultipleAdaptations) {
316   video_stream_adapter_->SetDegradationPreference(
317       DegradationPreference::MAINTAIN_FRAMERATE);
318   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
319   resource_->SetUsageState(ResourceUsageState::kOveruse);
320   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
321   RestrictSource(restrictions_listener_.restrictions());
322   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
323   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
324   RestrictSource(restrictions_listener_.restrictions());
325   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
326   EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total());
327   RestrictSource(restrictions_listener_.restrictions());
328 
329   // resource_ is not most limited so can't adapt from underuse.
330   resource_->SetUsageState(ResourceUsageState::kUnderuse);
331   EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total());
332   RestrictSource(restrictions_listener_.restrictions());
333   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
334   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
335   RestrictSource(restrictions_listener_.restrictions());
336   // resource_ is still not most limited so can't adapt from underuse.
337   resource_->SetUsageState(ResourceUsageState::kUnderuse);
338   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
339   RestrictSource(restrictions_listener_.restrictions());
340 
341   // However it will be after overuse
342   resource_->SetUsageState(ResourceUsageState::kOveruse);
343   EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total());
344   RestrictSource(restrictions_listener_.restrictions());
345 
346   // Now other_resource_ can't adapt up as it is not most restricted.
347   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
348   EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total());
349   RestrictSource(restrictions_listener_.restrictions());
350 
351   // resource_ is limited at 3 adaptations and other_resource_ 2.
352   // With the most limited resource signalling underuse in the following
353   // order we get back to unrestricted video.
354   resource_->SetUsageState(ResourceUsageState::kUnderuse);
355   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
356   RestrictSource(restrictions_listener_.restrictions());
357   // Both resource_ and other_resource_ are most limited.
358   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
359   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
360   RestrictSource(restrictions_listener_.restrictions());
361   resource_->SetUsageState(ResourceUsageState::kUnderuse);
362   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
363   RestrictSource(restrictions_listener_.restrictions());
364   // Again both are most limited.
365   resource_->SetUsageState(ResourceUsageState::kUnderuse);
366   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
367   RestrictSource(restrictions_listener_.restrictions());
368   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
369   EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total());
370 }
371 
TEST_F(ResourceAdaptationProcessorTest,MostLimitedResourceAdaptationWorksAfterChangingDegradataionPreference)372 TEST_F(ResourceAdaptationProcessorTest,
373        MostLimitedResourceAdaptationWorksAfterChangingDegradataionPreference) {
374   video_stream_adapter_->SetDegradationPreference(
375       DegradationPreference::MAINTAIN_FRAMERATE);
376   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
377   // Adapt down until we can't anymore.
378   resource_->SetUsageState(ResourceUsageState::kOveruse);
379   RestrictSource(restrictions_listener_.restrictions());
380   resource_->SetUsageState(ResourceUsageState::kOveruse);
381   RestrictSource(restrictions_listener_.restrictions());
382   resource_->SetUsageState(ResourceUsageState::kOveruse);
383   RestrictSource(restrictions_listener_.restrictions());
384   resource_->SetUsageState(ResourceUsageState::kOveruse);
385   RestrictSource(restrictions_listener_.restrictions());
386   resource_->SetUsageState(ResourceUsageState::kOveruse);
387   RestrictSource(restrictions_listener_.restrictions());
388   int last_total = restrictions_listener_.adaptation_counters().Total();
389 
390   video_stream_adapter_->SetDegradationPreference(
391       DegradationPreference::MAINTAIN_RESOLUTION);
392   // resource_ can not adapt up since we have never reduced FPS.
393   resource_->SetUsageState(ResourceUsageState::kUnderuse);
394   EXPECT_EQ(last_total, restrictions_listener_.adaptation_counters().Total());
395 
396   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
397   EXPECT_EQ(last_total + 1,
398             restrictions_listener_.adaptation_counters().Total());
399   RestrictSource(restrictions_listener_.restrictions());
400   // other_resource_ is most limited so should be able to adapt up.
401   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
402   EXPECT_EQ(last_total, restrictions_listener_.adaptation_counters().Total());
403 }
404 
TEST_F(ResourceAdaptationProcessorTest,AdaptsDownWhenOtherResourceIsAlwaysUnderused)405 TEST_F(ResourceAdaptationProcessorTest,
406        AdaptsDownWhenOtherResourceIsAlwaysUnderused) {
407   video_stream_adapter_->SetDegradationPreference(
408       DegradationPreference::MAINTAIN_FRAMERATE);
409   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
410   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
411   // Does not trigger adapataion because there's no restriction.
412   EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total());
413 
414   RestrictSource(restrictions_listener_.restrictions());
415   resource_->SetUsageState(ResourceUsageState::kOveruse);
416   // Adapts down even if other resource asked for adapting up.
417   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
418 
419   RestrictSource(restrictions_listener_.restrictions());
420   other_resource_->SetUsageState(ResourceUsageState::kUnderuse);
421   // Doesn't adapt up because adaptation is due to another resource.
422   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
423   RestrictSource(restrictions_listener_.restrictions());
424 }
425 
TEST_F(ResourceAdaptationProcessorTest,TriggerOveruseNotOnAdaptationTaskQueue)426 TEST_F(ResourceAdaptationProcessorTest,
427        TriggerOveruseNotOnAdaptationTaskQueue) {
428   video_stream_adapter_->SetDegradationPreference(
429       DegradationPreference::MAINTAIN_FRAMERATE);
430   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
431 
432   TaskQueueForTest resource_task_queue("ResourceTaskQueue");
433   resource_task_queue.PostTask(
434       [&]() { resource_->SetUsageState(ResourceUsageState::kOveruse); });
435 
436   EXPECT_EQ_WAIT(1u, restrictions_listener_.restrictions_updated_count(),
437                  kDefaultTimeout.ms());
438 }
439 
TEST_F(ResourceAdaptationProcessorTest,DestroyProcessorWhileResourceListenerDelegateHasTaskInFlight)440 TEST_F(ResourceAdaptationProcessorTest,
441        DestroyProcessorWhileResourceListenerDelegateHasTaskInFlight) {
442   video_stream_adapter_->SetDegradationPreference(
443       DegradationPreference::MAINTAIN_FRAMERATE);
444   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
445 
446   // Wait for `resource_` to signal oversue first so we know that the delegate
447   // has passed it on to the processor's task queue.
448   rtc::Event resource_event;
449   TaskQueueForTest resource_task_queue("ResourceTaskQueue");
450   resource_task_queue.PostTask([&]() {
451     resource_->SetUsageState(ResourceUsageState::kOveruse);
452     resource_event.Set();
453   });
454 
455   EXPECT_TRUE(resource_event.Wait(kDefaultTimeout));
456   // Now destroy the processor while handling the overuse is in flight.
457   DestroyProcessor();
458 
459   // Because the processor was destroyed by the time the delegate's task ran,
460   // the overuse signal must not have been handled.
461   EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count());
462 }
463 
TEST_F(ResourceAdaptationProcessorTest,ResourceOveruseIgnoredWhenSignalledDuringRemoval)464 TEST_F(ResourceAdaptationProcessorTest,
465        ResourceOveruseIgnoredWhenSignalledDuringRemoval) {
466   video_stream_adapter_->SetDegradationPreference(
467       DegradationPreference::MAINTAIN_FRAMERATE);
468   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
469 
470   rtc::Event overuse_event;
471   TaskQueueForTest resource_task_queue("ResourceTaskQueue");
472   // Queues task for `resource_` overuse while `processor_` is still listening.
473   resource_task_queue.PostTask([&]() {
474     resource_->SetUsageState(ResourceUsageState::kOveruse);
475     overuse_event.Set();
476   });
477   EXPECT_TRUE(overuse_event.Wait(kDefaultTimeout));
478   // Once we know the overuse task is queued, remove `resource_` so that
479   // `processor_` is not listening to it.
480   processor_->RemoveResource(resource_);
481 
482   // Runs the queued task so `processor_` gets signalled kOveruse from
483   // `resource_` even though `processor_` was not listening.
484   WaitUntilTaskQueueIdle();
485 
486   // No restrictions should change even though `resource_` signaled `kOveruse`.
487   EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count());
488 
489   // Delete `resource_` for cleanup.
490   resource_ = nullptr;
491 }
492 
TEST_F(ResourceAdaptationProcessorTest,RemovingOnlyAdaptedResourceResetsAdaptation)493 TEST_F(ResourceAdaptationProcessorTest,
494        RemovingOnlyAdaptedResourceResetsAdaptation) {
495   video_stream_adapter_->SetDegradationPreference(
496       DegradationPreference::MAINTAIN_FRAMERATE);
497   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
498 
499   resource_->SetUsageState(ResourceUsageState::kOveruse);
500   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
501   RestrictSource(restrictions_listener_.restrictions());
502 
503   processor_->RemoveResource(resource_);
504   EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total());
505 
506   // Delete `resource_` for cleanup.
507   resource_ = nullptr;
508 }
509 
TEST_F(ResourceAdaptationProcessorTest,RemovingMostLimitedResourceSetsAdaptationToNextLimitedLevel)510 TEST_F(ResourceAdaptationProcessorTest,
511        RemovingMostLimitedResourceSetsAdaptationToNextLimitedLevel) {
512   video_stream_adapter_->SetDegradationPreference(
513       DegradationPreference::BALANCED);
514   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
515 
516   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
517   RestrictSource(restrictions_listener_.restrictions());
518   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
519   VideoSourceRestrictions next_limited_restrictions =
520       restrictions_listener_.restrictions();
521   VideoAdaptationCounters next_limited_counters =
522       restrictions_listener_.adaptation_counters();
523 
524   resource_->SetUsageState(ResourceUsageState::kOveruse);
525   RestrictSource(restrictions_listener_.restrictions());
526   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
527 
528   // Removing most limited `resource_` should revert us back to
529   processor_->RemoveResource(resource_);
530   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
531   EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions());
532   EXPECT_EQ(next_limited_counters,
533             restrictions_listener_.adaptation_counters());
534 
535   // Delete `resource_` for cleanup.
536   resource_ = nullptr;
537 }
538 
TEST_F(ResourceAdaptationProcessorTest,RemovingMostLimitedResourceSetsAdaptationIfInputStateUnchanged)539 TEST_F(ResourceAdaptationProcessorTest,
540        RemovingMostLimitedResourceSetsAdaptationIfInputStateUnchanged) {
541   video_stream_adapter_->SetDegradationPreference(
542       DegradationPreference::MAINTAIN_FRAMERATE);
543   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
544 
545   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
546   RestrictSource(restrictions_listener_.restrictions());
547   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
548   VideoSourceRestrictions next_limited_restrictions =
549       restrictions_listener_.restrictions();
550   VideoAdaptationCounters next_limited_counters =
551       restrictions_listener_.adaptation_counters();
552 
553   // Overuse twice and underuse once. After the underuse we don't restrict the
554   // source. Normally this would block future underuses.
555   resource_->SetUsageState(ResourceUsageState::kOveruse);
556   RestrictSource(restrictions_listener_.restrictions());
557   resource_->SetUsageState(ResourceUsageState::kOveruse);
558   RestrictSource(restrictions_listener_.restrictions());
559   resource_->SetUsageState(ResourceUsageState::kUnderuse);
560   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
561 
562   // Removing most limited `resource_` should revert us back to, even though we
563   // did not call RestrictSource() after `resource_` was overused. Normally
564   // adaptation for MAINTAIN_FRAMERATE would be blocked here but for removal we
565   // allow this anyways.
566   processor_->RemoveResource(resource_);
567   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
568   EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions());
569   EXPECT_EQ(next_limited_counters,
570             restrictions_listener_.adaptation_counters());
571 
572   // Delete `resource_` for cleanup.
573   resource_ = nullptr;
574 }
575 
TEST_F(ResourceAdaptationProcessorTest,RemovingResourceNotMostLimitedHasNoEffectOnLimitations)576 TEST_F(ResourceAdaptationProcessorTest,
577        RemovingResourceNotMostLimitedHasNoEffectOnLimitations) {
578   video_stream_adapter_->SetDegradationPreference(
579       DegradationPreference::BALANCED);
580   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
581 
582   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
583   RestrictSource(restrictions_listener_.restrictions());
584   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
585 
586   resource_->SetUsageState(ResourceUsageState::kOveruse);
587   RestrictSource(restrictions_listener_.restrictions());
588   VideoSourceRestrictions current_restrictions =
589       restrictions_listener_.restrictions();
590   VideoAdaptationCounters current_counters =
591       restrictions_listener_.adaptation_counters();
592   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
593 
594   // Removing most limited `resource_` should revert us back to
595   processor_->RemoveResource(other_resource_);
596   EXPECT_EQ(current_restrictions, restrictions_listener_.restrictions());
597   EXPECT_EQ(current_counters, restrictions_listener_.adaptation_counters());
598 
599   // Delete `other_resource_` for cleanup.
600   other_resource_ = nullptr;
601 }
602 
TEST_F(ResourceAdaptationProcessorTest,RemovingMostLimitedResourceAfterSwitchingDegradationPreferences)603 TEST_F(ResourceAdaptationProcessorTest,
604        RemovingMostLimitedResourceAfterSwitchingDegradationPreferences) {
605   video_stream_adapter_->SetDegradationPreference(
606       DegradationPreference::MAINTAIN_FRAMERATE);
607   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
608 
609   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
610   RestrictSource(restrictions_listener_.restrictions());
611   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
612   VideoSourceRestrictions next_limited_restrictions =
613       restrictions_listener_.restrictions();
614   VideoAdaptationCounters next_limited_counters =
615       restrictions_listener_.adaptation_counters();
616 
617   video_stream_adapter_->SetDegradationPreference(
618       DegradationPreference::MAINTAIN_RESOLUTION);
619   resource_->SetUsageState(ResourceUsageState::kOveruse);
620   RestrictSource(restrictions_listener_.restrictions());
621   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
622 
623   // Revert to `other_resource_` when removing `resource_` even though the
624   // degradation preference was different when it was overused.
625   processor_->RemoveResource(resource_);
626   EXPECT_EQ(next_limited_counters,
627             restrictions_listener_.adaptation_counters());
628 
629   // After switching back to MAINTAIN_FRAMERATE, the next most limited settings
630   // are restored.
631   video_stream_adapter_->SetDegradationPreference(
632       DegradationPreference::MAINTAIN_FRAMERATE);
633   EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions());
634 
635   // Delete `resource_` for cleanup.
636   resource_ = nullptr;
637 }
638 
TEST_F(ResourceAdaptationProcessorTest,RemovingMostLimitedResourceSetsNextLimitationsInDisabled)639 TEST_F(ResourceAdaptationProcessorTest,
640        RemovingMostLimitedResourceSetsNextLimitationsInDisabled) {
641   video_stream_adapter_->SetDegradationPreference(
642       DegradationPreference::MAINTAIN_FRAMERATE);
643   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
644 
645   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
646   RestrictSource(restrictions_listener_.restrictions());
647   VideoSourceRestrictions next_limited_restrictions =
648       restrictions_listener_.restrictions();
649   VideoAdaptationCounters next_limited_counters =
650       restrictions_listener_.adaptation_counters();
651   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
652   resource_->SetUsageState(ResourceUsageState::kOveruse);
653   RestrictSource(restrictions_listener_.restrictions());
654   EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total());
655 
656   video_stream_adapter_->SetDegradationPreference(
657       DegradationPreference::DISABLED);
658 
659   // Revert to `other_resource_` when removing `resource_` even though the
660   // current degradataion preference is disabled.
661   processor_->RemoveResource(resource_);
662 
663   // After switching back to MAINTAIN_FRAMERATE, the next most limited settings
664   // are restored.
665   video_stream_adapter_->SetDegradationPreference(
666       DegradationPreference::MAINTAIN_FRAMERATE);
667   EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions());
668   EXPECT_EQ(next_limited_counters,
669             restrictions_listener_.adaptation_counters());
670 
671   // Delete `resource_` for cleanup.
672   resource_ = nullptr;
673 }
674 
TEST_F(ResourceAdaptationProcessorTest,RemovedResourceSignalsIgnoredByProcessor)675 TEST_F(ResourceAdaptationProcessorTest,
676        RemovedResourceSignalsIgnoredByProcessor) {
677   video_stream_adapter_->SetDegradationPreference(
678       DegradationPreference::MAINTAIN_FRAMERATE);
679   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
680 
681   processor_->RemoveResource(resource_);
682   resource_->SetUsageState(ResourceUsageState::kOveruse);
683   EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count());
684 
685   // Delete `resource_` for cleanup.
686   resource_ = nullptr;
687 }
688 
TEST_F(ResourceAdaptationProcessorTest,RemovingResourceWhenMultipleMostLimtedHasNoEffect)689 TEST_F(ResourceAdaptationProcessorTest,
690        RemovingResourceWhenMultipleMostLimtedHasNoEffect) {
691   video_stream_adapter_->SetDegradationPreference(
692       DegradationPreference::MAINTAIN_FRAMERATE);
693   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
694 
695   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
696   RestrictSource(restrictions_listener_.restrictions());
697   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
698   // Adapt `resource_` up and then down so that both resource's are most
699   // limited at 1 adaptation.
700   resource_->SetUsageState(ResourceUsageState::kOveruse);
701   RestrictSource(restrictions_listener_.restrictions());
702   resource_->SetUsageState(ResourceUsageState::kUnderuse);
703   RestrictSource(restrictions_listener_.restrictions());
704   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
705 
706   // Removing `resource_` has no effect since both `resource_` and
707   // `other_resource_` are most limited.
708   processor_->RemoveResource(resource_);
709   EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total());
710 
711   // Delete `resource_` for cleanup.
712   resource_ = nullptr;
713 }
714 
TEST_F(ResourceAdaptationProcessorTest,ResourceOverusedAtLimitReachedWillShareMostLimited)715 TEST_F(ResourceAdaptationProcessorTest,
716        ResourceOverusedAtLimitReachedWillShareMostLimited) {
717   video_stream_adapter_->SetDegradationPreference(
718       DegradationPreference::MAINTAIN_FRAMERATE);
719   SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
720 
721   bool has_reached_min_pixels = false;
722   ON_CALL(frame_rate_provider_, OnMinPixelLimitReached())
723       .WillByDefault(testing::Assign(&has_reached_min_pixels, true));
724 
725   // Adapt 10 times, which should make us hit the limit.
726   for (int i = 0; i < 10; ++i) {
727     resource_->SetUsageState(ResourceUsageState::kOveruse);
728     RestrictSource(restrictions_listener_.restrictions());
729   }
730   EXPECT_TRUE(has_reached_min_pixels);
731   auto last_update_count = restrictions_listener_.restrictions_updated_count();
732   other_resource_->SetUsageState(ResourceUsageState::kOveruse);
733   // Now both `resource_` and `other_resource_` are most limited. Underuse of
734   // `resource_` will not adapt up.
735   resource_->SetUsageState(ResourceUsageState::kUnderuse);
736   EXPECT_EQ(last_update_count,
737             restrictions_listener_.restrictions_updated_count());
738 }
739 
740 }  // namespace webrtc
741