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