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