blob: df99aed48b05189eeab7424922d7d0b5cf303518 [file] [log] [blame]
/*
* 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_state(ResourceUsageState::kStable);
second_resource->set_usage_state(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_state(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_state(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