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