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