1 /*
2  *  Copyright 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/new_resource_adaptation_processor_poc.h"
12 
13 #include "absl/types/optional.h"
14 #include "call/adaptation/resource.h"
15 #include "call/adaptation/test/fake_resource.h"
16 #include "call/adaptation/test/fake_resource_consumer_configuration.h"
17 #include "test/gmock.h"
18 #include "test/gtest.h"
19 
20 namespace webrtc {
21 
22 // The indices of different resolutions returned by
23 // AddStandardResolutionConfigurations().
24 static size_t k1080pIndex = 0;
25 static size_t k720pIndex = 1;
26 static size_t k360pIndex = 2;
27 static size_t k180pIndex = 3;
28 
ConnectNeighbors(ResourceConsumerConfiguration * upper,ResourceConsumerConfiguration * lower)29 void ConnectNeighbors(ResourceConsumerConfiguration* upper,
30                       ResourceConsumerConfiguration* lower) {
31   upper->AddLowerNeighbor(lower);
32   lower->AddUpperNeighbor(upper);
33 }
34 
35 std::vector<FakeResourceConsumerConfiguration*>
AddStandardResolutionConfigurations(NewResourceAdaptationProcessorPoc * processor)36 AddStandardResolutionConfigurations(
37     NewResourceAdaptationProcessorPoc* processor) {
38   std::vector<FakeResourceConsumerConfiguration*> configs;
39   configs.push_back(processor->AddConfiguration(
40       std::make_unique<FakeResourceConsumerConfiguration>(1920, 1080, 30.0,
41                                                           1.0)));
42   configs.push_back(processor->AddConfiguration(
43       std::make_unique<FakeResourceConsumerConfiguration>(1280, 720, 30.0,
44                                                           1.0)));
45   configs.push_back(processor->AddConfiguration(
46       std::make_unique<FakeResourceConsumerConfiguration>(640, 360, 30.0,
47                                                           1.0)));
48   configs.push_back(processor->AddConfiguration(
49       std::make_unique<FakeResourceConsumerConfiguration>(320, 180, 30.0,
50                                                           1.0)));
51   for (size_t i = 1; i < configs.size(); ++i) {
52     ConnectNeighbors(configs[i - 1], configs[i]);
53   }
54   return configs;
55 }
56 
TEST(NewResourceAdaptationProcessorPocTest,SingleStreamAndResourceDontAdaptDownWhenStable)57 TEST(NewResourceAdaptationProcessorPocTest,
58      SingleStreamAndResourceDontAdaptDownWhenStable) {
59   NewResourceAdaptationProcessorPoc processor;
60   processor.AddResource(
61       std::make_unique<FakeResource>(ResourceUsageState::kStable));
62   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
63   processor.AddConsumer(std::make_unique<ResourceConsumer>(
64       "OnlyStream", resolution_configs[k1080pIndex]));
65   EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration());
66 }
67 
TEST(NewResourceAdaptationProcessorPocTest,SingleStreamAndResourceAdaptDownOnOveruse)68 TEST(NewResourceAdaptationProcessorPocTest,
69      SingleStreamAndResourceAdaptDownOnOveruse) {
70   NewResourceAdaptationProcessorPoc processor;
71   processor.AddResource(
72       std::make_unique<FakeResource>(ResourceUsageState::kOveruse));
73   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
74   auto* consumer = processor.AddConsumer(std::make_unique<ResourceConsumer>(
75       "OnlyStream", resolution_configs[k1080pIndex]));
76   auto next_config = processor.FindNextConfiguration();
77   EXPECT_TRUE(next_config.has_value());
78   EXPECT_EQ(consumer, next_config->consumer);
79   EXPECT_EQ(resolution_configs[k720pIndex], next_config->configuration);
80 }
81 
TEST(NewResourceAdaptationProcessorPocTest,SingleStreamAndResourceDontAdaptOnOveruseIfMinResolution)82 TEST(NewResourceAdaptationProcessorPocTest,
83      SingleStreamAndResourceDontAdaptOnOveruseIfMinResolution) {
84   NewResourceAdaptationProcessorPoc processor;
85   processor.AddResource(
86       std::make_unique<FakeResource>(ResourceUsageState::kOveruse));
87   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
88   processor.AddConsumer(std::make_unique<ResourceConsumer>(
89       "OnlyStream", resolution_configs.back()));
90   EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration());
91 }
92 
TEST(NewResourceAdaptationProcessorPocTest,SingleStreamAndResourceAdaptUpOnUnderuse)93 TEST(NewResourceAdaptationProcessorPocTest,
94      SingleStreamAndResourceAdaptUpOnUnderuse) {
95   NewResourceAdaptationProcessorPoc processor;
96   processor.AddResource(
97       std::make_unique<FakeResource>(ResourceUsageState::kUnderuse));
98   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
99   auto* consumer = processor.AddConsumer(std::make_unique<ResourceConsumer>(
100       "OnlyStream", resolution_configs[k720pIndex]));
101   auto next_config = processor.FindNextConfiguration();
102   EXPECT_TRUE(next_config.has_value());
103   EXPECT_EQ(consumer, next_config->consumer);
104   EXPECT_EQ(resolution_configs[k1080pIndex], next_config->configuration);
105 }
106 
TEST(NewResourceAdaptationProcessorPocTest,SingleStreamAndResourceDontAdaptOnUnderuseIfMaxResolution)107 TEST(NewResourceAdaptationProcessorPocTest,
108      SingleStreamAndResourceDontAdaptOnUnderuseIfMaxResolution) {
109   NewResourceAdaptationProcessorPoc processor;
110   processor.AddResource(
111       std::make_unique<FakeResource>(ResourceUsageState::kUnderuse));
112   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
113   processor.AddConsumer(std::make_unique<ResourceConsumer>(
114       "OnlyStream", resolution_configs[k1080pIndex]));
115   EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration());
116 }
117 
TEST(NewResourceAdaptationProcessorPocTest,MultipleStreamsLargestStreamGetsAdaptedDownOnOveruse)118 TEST(NewResourceAdaptationProcessorPocTest,
119      MultipleStreamsLargestStreamGetsAdaptedDownOnOveruse) {
120   NewResourceAdaptationProcessorPoc processor;
121   processor.AddResource(
122       std::make_unique<FakeResource>(ResourceUsageState::kOveruse));
123   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
124   auto* first_stream = processor.AddConsumer(std::make_unique<ResourceConsumer>(
125       "FirstStream", resolution_configs[k1080pIndex]));
126   auto* second_stream =
127       processor.AddConsumer(std::make_unique<ResourceConsumer>(
128           "SecondStream", resolution_configs[k720pIndex]));
129   // When the first stream is larger.
130   auto next_config = processor.FindNextConfiguration();
131   EXPECT_TRUE(next_config.has_value());
132   EXPECT_EQ(first_stream, next_config->consumer);
133   // When the second stream is larger.
134   first_stream->SetConfiguration(resolution_configs[k720pIndex]);
135   second_stream->SetConfiguration(resolution_configs[k1080pIndex]);
136   next_config = processor.FindNextConfiguration();
137   EXPECT_TRUE(next_config.has_value());
138   EXPECT_EQ(second_stream, next_config->consumer);
139 }
140 
TEST(NewResourceAdaptationProcessorPocTest,MultipleStreamsSmallestStreamGetsAdaptedUpOnUnderuse)141 TEST(NewResourceAdaptationProcessorPocTest,
142      MultipleStreamsSmallestStreamGetsAdaptedUpOnUnderuse) {
143   NewResourceAdaptationProcessorPoc processor;
144   processor.AddResource(
145       std::make_unique<FakeResource>(ResourceUsageState::kUnderuse));
146   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
147   auto* first_stream = processor.AddConsumer(std::make_unique<ResourceConsumer>(
148       "FirstStream", resolution_configs[k360pIndex]));
149   auto* second_stream =
150       processor.AddConsumer(std::make_unique<ResourceConsumer>(
151           "SecondStream", resolution_configs[k180pIndex]));
152   // When the first stream is larger.
153   auto next_config = processor.FindNextConfiguration();
154   EXPECT_TRUE(next_config.has_value());
155   EXPECT_EQ(second_stream, next_config->consumer);
156   // When the second stream is larger.
157   first_stream->SetConfiguration(resolution_configs[k180pIndex]);
158   second_stream->SetConfiguration(resolution_configs[k360pIndex]);
159   next_config = processor.FindNextConfiguration();
160   EXPECT_TRUE(next_config.has_value());
161   EXPECT_EQ(first_stream, next_config->consumer);
162 }
163 
164 // If both streams are equally valid to adapt down, the first one is preferred.
TEST(NewResourceAdaptationProcessorPocTest,MultipleStreamsAdaptFirstStreamWhenBothStreamsHaveSameCost)165 TEST(NewResourceAdaptationProcessorPocTest,
166      MultipleStreamsAdaptFirstStreamWhenBothStreamsHaveSameCost) {
167   NewResourceAdaptationProcessorPoc processor;
168   processor.AddResource(
169       std::make_unique<FakeResource>(ResourceUsageState::kOveruse));
170   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
171   auto* first_stream = processor.AddConsumer(std::make_unique<ResourceConsumer>(
172       "FirstStream", resolution_configs[k720pIndex]));
173   processor.AddConsumer(std::make_unique<ResourceConsumer>(
174       "SecondStream", resolution_configs[k720pIndex]));
175   auto next_config = processor.FindNextConfiguration();
176   EXPECT_TRUE(next_config.has_value());
177   EXPECT_EQ(first_stream, next_config->consumer);
178 }
179 
TEST(NewResourceAdaptationProcessorPocTest,MultipleResourcesAdaptDownIfAnyIsOverused)180 TEST(NewResourceAdaptationProcessorPocTest,
181      MultipleResourcesAdaptDownIfAnyIsOverused) {
182   NewResourceAdaptationProcessorPoc processor;
183   auto* first_resource = processor.AddResource(
184       std::make_unique<FakeResource>(ResourceUsageState::kOveruse));
185   auto* second_resource = processor.AddResource(
186       std::make_unique<FakeResource>(ResourceUsageState::kStable));
187   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
188   processor.AddConsumer(std::make_unique<ResourceConsumer>(
189       "OnlyStream", resolution_configs[k1080pIndex]));
190   // When the first resource is overused.
191   EXPECT_TRUE(processor.FindNextConfiguration().has_value());
192   // When the second resource is overused.
193   first_resource->set_usage_state(ResourceUsageState::kStable);
194   second_resource->set_usage_state(ResourceUsageState::kOveruse);
195   EXPECT_TRUE(processor.FindNextConfiguration().has_value());
196 }
197 
TEST(NewResourceAdaptationProcessorPocTest,MultipleResourcesAdaptUpIfAllAreUnderused)198 TEST(NewResourceAdaptationProcessorPocTest,
199      MultipleResourcesAdaptUpIfAllAreUnderused) {
200   NewResourceAdaptationProcessorPoc processor;
201   processor.AddResource(
202       std::make_unique<FakeResource>(ResourceUsageState::kUnderuse));
203   auto* second_resource = processor.AddResource(
204       std::make_unique<FakeResource>(ResourceUsageState::kStable));
205   auto resolution_configs = AddStandardResolutionConfigurations(&processor);
206   processor.AddConsumer(std::make_unique<ResourceConsumer>(
207       "OnlyStream", resolution_configs[k720pIndex]));
208   // When only the first resource is underused.
209   EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration());
210   // When all resources are underused.
211   second_resource->set_usage_state(ResourceUsageState::kUnderuse);
212   EXPECT_TRUE(processor.FindNextConfiguration().has_value());
213 }
214 
TEST(NewResourceAdaptationProcessorPocTest,HighestPreferredNeighborIsPickedWhenAdapting)215 TEST(NewResourceAdaptationProcessorPocTest,
216      HighestPreferredNeighborIsPickedWhenAdapting) {
217   NewResourceAdaptationProcessorPoc processor;
218   // Set up the following graph, where (#) is the preference.
219   //
220   //    Downward arrows          Upward arrows
221   //
222   //   a(1) -----> b(2)       a(1) <----- b(2)
223   //    |        ^  |          ^        /  ^
224   //    |     /     |          |     /     |
225   //    v  /        v          |  v        |
226   //   c(1.5) ---> d(2)       c(1.5) <--- d(2)
227   //
228   auto* a = processor.AddConfiguration(
229       std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 1.0));
230   auto* b = processor.AddConfiguration(
231       std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 2.0));
232   auto* c = processor.AddConfiguration(
233       std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 1.5));
234   auto* d = processor.AddConfiguration(
235       std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 2.0));
236   ConnectNeighbors(a, b);
237   ConnectNeighbors(a, c);
238   ConnectNeighbors(b, d);
239   ConnectNeighbors(c, b);
240   ConnectNeighbors(c, d);
241 
242   auto* resource = processor.AddResource(
243       std::make_unique<FakeResource>(ResourceUsageState::kOveruse));
244   auto* consumer = processor.AddConsumer(
245       std::make_unique<ResourceConsumer>("OnlyStream", a));
246 
247   // We should expect adapting down: a -> b -> d
248   EXPECT_EQ(b, processor.FindNextConfiguration()->configuration);
249   consumer->SetConfiguration(b);
250   EXPECT_EQ(d, processor.FindNextConfiguration()->configuration);
251   consumer->SetConfiguration(d);
252 
253   // We should expect to adapt up: d -> b -> c -> a
254   resource->set_usage_state(ResourceUsageState::kUnderuse);
255   EXPECT_EQ(b, processor.FindNextConfiguration()->configuration);
256   consumer->SetConfiguration(b);
257   EXPECT_EQ(c, processor.FindNextConfiguration()->configuration);
258   consumer->SetConfiguration(c);
259   EXPECT_EQ(a, processor.FindNextConfiguration()->configuration);
260 }
261 
262 }  // namespace webrtc
263