The Resource interface (previously a skeleton not used outside of testing) is updated to inform listeners of changes to resource usage. Debugging methods are removed (Name, UsageUnitsOfMeasurements, CurrentUsage). The interface is implemented by OveruseFrameDetectorResourceAdaptationModule's inner classes EncodeUsageResource and QualityScalerResource. The new ResourceUsageListener interface is implemented by OveruseFrameDetectorResourceAdaptationModule. In order to avoid adding AdaptationObserverInterface::AdaptReason to the ResourceUsageListener interface, the module figures out if the reason is "kCpu" or "kQuality" by looking which Resource object triggered OnResourceUsageStateMeasured(). These resources no longer need an explicit reference to OveruseFrameDetectorResourceAdaptationModule and could potentially be used by a different module. In this CL, AdaptationObserverInterface::AdaptDown()'s return value is still needed by QualityScaler. This is mirrored in the return value of ResourceUsageListener::OnResourceUsageStateMeasured(). A TODO is added to remove it and a comment explains how the current implementation seems to break the contract of the method (as was the case prior to this CL). Follow-up work include: - Move EncodeUsageResource and QualityScalerResource to separate files. - Make resources injectable, allowing fake resources in testing and removing OnResourceOveruseForTesting() methods. (Investigate adding the necessary input signals to the Resource interface or relevant sub-interfaces so that the module does not need to know which Resource implementation is used.) - And more! See whiteboard :) Bug: webrtc:11222 Change-Id: I0a46ace4a2e617874e3ee97e67e3a199fef420a2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168180 Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@google.com> Cr-Commit-Position: refs/heads/master@{#30469}
262 lines
11 KiB
C++
262 lines
11 KiB
C++
/*
|
|
* 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
|