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