/* * 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 AddStandardResolutionConfigurations(ResourceAdaptationProcessor* processor) { std::vector configs; configs.push_back(processor->AddConfiguration( std::make_unique(1920, 1080, 30.0, 1.0))); configs.push_back(processor->AddConfiguration( std::make_unique(1280, 720, 30.0, 1.0))); configs.push_back(processor->AddConfiguration( std::make_unique(640, 360, 30.0, 1.0))); configs.push_back(processor->AddConfiguration( std::make_unique(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(ResourceUsageState::kStable)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); processor.AddConsumer(std::make_unique( "OnlyStream", resolution_configs[k1080pIndex])); EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration()); } TEST(ResourceAdaptationProcessorTest, SingleStreamAndResourceAdaptDownOnOveruse) { ResourceAdaptationProcessor processor; processor.AddResource( std::make_unique(ResourceUsageState::kOveruse)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); auto* consumer = processor.AddConsumer(std::make_unique( "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(ResourceUsageState::kOveruse)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); processor.AddConsumer(std::make_unique( "OnlyStream", resolution_configs.back())); EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration()); } TEST(ResourceAdaptationProcessorTest, SingleStreamAndResourceAdaptUpOnUnderuse) { ResourceAdaptationProcessor processor; processor.AddResource( std::make_unique(ResourceUsageState::kUnderuse)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); auto* consumer = processor.AddConsumer(std::make_unique( "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(ResourceUsageState::kUnderuse)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); processor.AddConsumer(std::make_unique( "OnlyStream", resolution_configs[k1080pIndex])); EXPECT_EQ(absl::nullopt, processor.FindNextConfiguration()); } TEST(ResourceAdaptationProcessorTest, MultipleStreamsLargestStreamGetsAdaptedDownOnOveruse) { ResourceAdaptationProcessor processor; processor.AddResource( std::make_unique(ResourceUsageState::kOveruse)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); auto* first_stream = processor.AddConsumer(std::make_unique( "FirstStream", resolution_configs[k1080pIndex])); auto* second_stream = processor.AddConsumer(std::make_unique( "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(ResourceUsageState::kUnderuse)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); auto* first_stream = processor.AddConsumer(std::make_unique( "FirstStream", resolution_configs[k360pIndex])); auto* second_stream = processor.AddConsumer(std::make_unique( "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(ResourceUsageState::kOveruse)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); auto* first_stream = processor.AddConsumer(std::make_unique( "FirstStream", resolution_configs[k720pIndex])); processor.AddConsumer(std::make_unique( "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(ResourceUsageState::kOveruse)); auto* second_resource = processor.AddResource( std::make_unique(ResourceUsageState::kStable)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); processor.AddConsumer(std::make_unique( "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(ResourceUsageState::kUnderuse)); auto* second_resource = processor.AddResource( std::make_unique(ResourceUsageState::kStable)); auto resolution_configs = AddStandardResolutionConfigurations(&processor); processor.AddConsumer(std::make_unique( "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(1, 1, 1, 1.0)); auto* b = processor.AddConfiguration( std::make_unique(1, 1, 1, 2.0)); auto* c = processor.AddConfiguration( std::make_unique(1, 1, 1, 1.5)); auto* d = processor.AddConfiguration( std::make_unique(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(ResourceUsageState::kOveruse)); auto* consumer = processor.AddConsumer( std::make_unique("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