| /* |
| * Copyright 2019 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "call/adaptation/resource_adaptation_processor.h" |
| |
| #include "absl/types/optional.h" |
| #include "call/adaptation/resource.h" |
| #include "call/adaptation/test/fake_resource.h" |
| #include "call/adaptation/test/fake_resource_consumer_configuration.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| |
| namespace webrtc { |
| |
| // The indices of different resolutions returned by |
| // AddStandardResolutionConfigurations(). |
| static size_t k1080pIndex = 0; |
| static size_t k720pIndex = 1; |
| static size_t k360pIndex = 2; |
| static size_t k180pIndex = 3; |
| |
| void ConnectNeighbors(ResourceConsumerConfiguration* upper, |
| ResourceConsumerConfiguration* lower) { |
| upper->AddLowerNeighbor(lower); |
| lower->AddUpperNeighbor(upper); |
| } |
| |
| std::vector<FakeResourceConsumerConfiguration*> |
| AddStandardResolutionConfigurations(ResourceAdaptationProcessor* processor) { |
| std::vector<FakeResourceConsumerConfiguration*> configs; |
| configs.push_back(processor->AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(1920, 1080, 30.0, |
| 1.0))); |
| configs.push_back(processor->AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(1280, 720, 30.0, |
| 1.0))); |
| configs.push_back(processor->AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(640, 360, 30.0, |
| 1.0))); |
| configs.push_back(processor->AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(320, 180, 30.0, |
| 1.0))); |
| for (size_t i = 1; i < configs.size(); ++i) { |
| ConnectNeighbors(configs[i - 1], configs[i]); |
| } |
| return configs; |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| SingleStreamAndResourceDontAdaptDownWhenStable) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kStable)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "OnlyStream", resolution_configs[k1080pIndex])); |
| EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration()); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| SingleStreamAndResourceAdaptDownOnOveruse) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kOveruse)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| auto* consumer = processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "OnlyStream", resolution_configs[k1080pIndex])); |
| auto next_config = processor.FindNextConfiguration(); |
| EXPECT_TRUE(next_config.has_value()); |
| EXPECT_EQ(consumer, next_config->consumer); |
| EXPECT_EQ(resolution_configs[k720pIndex], next_config->configuration); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| SingleStreamAndResourceDontAdaptOnOveruseIfMinResolution) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kOveruse)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "OnlyStream", resolution_configs.back())); |
| EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration()); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| SingleStreamAndResourceAdaptUpOnUnderuse) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kUnderuse)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| auto* consumer = processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "OnlyStream", resolution_configs[k720pIndex])); |
| auto next_config = processor.FindNextConfiguration(); |
| EXPECT_TRUE(next_config.has_value()); |
| EXPECT_EQ(consumer, next_config->consumer); |
| EXPECT_EQ(resolution_configs[k1080pIndex], next_config->configuration); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| SingleStreamAndResourceDontAdaptOnUnderuseIfMaxResolution) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kUnderuse)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "OnlyStream", resolution_configs[k1080pIndex])); |
| EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration()); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| MultipleStreamsLargestStreamGetsAdaptedDownOnOveruse) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kOveruse)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| auto* first_stream = processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "FirstStream", resolution_configs[k1080pIndex])); |
| auto* second_stream = |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "SecondStream", resolution_configs[k720pIndex])); |
| // When the first stream is larger. |
| auto next_config = processor.FindNextConfiguration(); |
| EXPECT_TRUE(next_config.has_value()); |
| EXPECT_EQ(first_stream, next_config->consumer); |
| // When the second stream is larger. |
| first_stream->SetConfiguration(resolution_configs[k720pIndex]); |
| second_stream->SetConfiguration(resolution_configs[k1080pIndex]); |
| next_config = processor.FindNextConfiguration(); |
| EXPECT_TRUE(next_config.has_value()); |
| EXPECT_EQ(second_stream, next_config->consumer); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| MultipleStreamsSmallestStreamGetsAdaptedUpOnUnderuse) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kUnderuse)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| auto* first_stream = processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "FirstStream", resolution_configs[k360pIndex])); |
| auto* second_stream = |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "SecondStream", resolution_configs[k180pIndex])); |
| // When the first stream is larger. |
| auto next_config = processor.FindNextConfiguration(); |
| EXPECT_TRUE(next_config.has_value()); |
| EXPECT_EQ(second_stream, next_config->consumer); |
| // When the second stream is larger. |
| first_stream->SetConfiguration(resolution_configs[k180pIndex]); |
| second_stream->SetConfiguration(resolution_configs[k360pIndex]); |
| next_config = processor.FindNextConfiguration(); |
| EXPECT_TRUE(next_config.has_value()); |
| EXPECT_EQ(first_stream, next_config->consumer); |
| } |
| |
| // If both streams are equally valid to adapt down, the first one is preferred. |
| TEST(ResourceAdaptationProcessorTest, |
| MultipleStreamsAdaptFirstStreamWhenBothStreamsHaveSameCost) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kOveruse)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| auto* first_stream = processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "FirstStream", resolution_configs[k720pIndex])); |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "SecondStream", resolution_configs[k720pIndex])); |
| auto next_config = processor.FindNextConfiguration(); |
| EXPECT_TRUE(next_config.has_value()); |
| EXPECT_EQ(first_stream, next_config->consumer); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| MultipleResourcesAdaptDownIfAnyIsOverused) { |
| ResourceAdaptationProcessor processor; |
| auto* first_resource = processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kOveruse)); |
| auto* second_resource = processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kStable)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "OnlyStream", resolution_configs[k1080pIndex])); |
| // When the first resource is overused. |
| EXPECT_TRUE(processor.FindNextConfiguration().has_value()); |
| // When the second resource is overused. |
| first_resource->set_usage(ResourceUsageState::kStable); |
| second_resource->set_usage(ResourceUsageState::kOveruse); |
| EXPECT_TRUE(processor.FindNextConfiguration().has_value()); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| MultipleResourcesAdaptUpIfAllAreUnderused) { |
| ResourceAdaptationProcessor processor; |
| processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kUnderuse)); |
| auto* second_resource = processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kStable)); |
| auto resolution_configs = AddStandardResolutionConfigurations(&processor); |
| processor.AddConsumer(std::make_unique<ResourceConsumer>( |
| "OnlyStream", resolution_configs[k720pIndex])); |
| // When only the first resource is underused. |
| EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration()); |
| // When all resources are underused. |
| second_resource->set_usage(ResourceUsageState::kUnderuse); |
| EXPECT_TRUE(processor.FindNextConfiguration().has_value()); |
| } |
| |
| TEST(ResourceAdaptationProcessorTest, |
| HighestPreferredNeighborIsPickedWhenAdapting) { |
| ResourceAdaptationProcessor processor; |
| // Set up the following graph, where (#) is the preference. |
| // |
| // Downward arrows Upward arrows |
| // |
| // a(1) -----> b(2) a(1) <----- b(2) |
| // | ^ | ^ / ^ |
| // | / | | / | |
| // v / v | v | |
| // c(1.5) ---> d(2) c(1.5) <--- d(2) |
| // |
| auto* a = processor.AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 1.0)); |
| auto* b = processor.AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 2.0)); |
| auto* c = processor.AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 1.5)); |
| auto* d = processor.AddConfiguration( |
| std::make_unique<FakeResourceConsumerConfiguration>(1, 1, 1, 2.0)); |
| ConnectNeighbors(a, b); |
| ConnectNeighbors(a, c); |
| ConnectNeighbors(b, d); |
| ConnectNeighbors(c, b); |
| ConnectNeighbors(c, d); |
| |
| auto* resource = processor.AddResource( |
| std::make_unique<FakeResource>(ResourceUsageState::kOveruse)); |
| auto* consumer = processor.AddConsumer( |
| std::make_unique<ResourceConsumer>("OnlyStream", a)); |
| |
| // We should expect adapting down: a -> b -> d |
| EXPECT_EQ(b, processor.FindNextConfiguration()->configuration); |
| consumer->SetConfiguration(b); |
| EXPECT_EQ(d, processor.FindNextConfiguration()->configuration); |
| consumer->SetConfiguration(d); |
| |
| // We should expect to adapt up: d -> b -> c -> a |
| resource->set_usage(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(b, processor.FindNextConfiguration()->configuration); |
| consumer->SetConfiguration(b); |
| EXPECT_EQ(c, processor.FindNextConfiguration()->configuration); |
| consumer->SetConfiguration(c); |
| EXPECT_EQ(a, processor.FindNextConfiguration()->configuration); |
| } |
| |
| } // namespace webrtc |