258 lines
11 KiB
C++
258 lines
11 KiB
C++
|
|
/*
|
||
|
|
* Copyright (c) 2020 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 "api/video/video_adaptation_counters.h"
|
||
|
|
#include "call/adaptation/resource.h"
|
||
|
|
#include "call/adaptation/resource_adaptation_processor_interface.h"
|
||
|
|
#include "call/adaptation/test/fake_frame_rate_provider.h"
|
||
|
|
#include "call/adaptation/test/fake_resource.h"
|
||
|
|
#include "call/adaptation/video_source_restrictions.h"
|
||
|
|
#include "call/adaptation/video_stream_input_state_provider.h"
|
||
|
|
#include "test/gtest.h"
|
||
|
|
|
||
|
|
namespace webrtc {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
const int kDefaultFrameRate = 30;
|
||
|
|
const int kDefaultFrameSize = 1280 * 720;
|
||
|
|
|
||
|
|
class ResourceAdaptationProcessorListenerForTesting
|
||
|
|
: public ResourceAdaptationProcessorListener {
|
||
|
|
public:
|
||
|
|
ResourceAdaptationProcessorListenerForTesting()
|
||
|
|
: restrictions_updated_count_(0),
|
||
|
|
restrictions_(),
|
||
|
|
adaptation_counters_(),
|
||
|
|
reason_(nullptr) {}
|
||
|
|
~ResourceAdaptationProcessorListenerForTesting() override {}
|
||
|
|
|
||
|
|
size_t restrictions_updated_count() const {
|
||
|
|
return restrictions_updated_count_;
|
||
|
|
}
|
||
|
|
const VideoSourceRestrictions& restrictions() const { return restrictions_; }
|
||
|
|
const VideoAdaptationCounters& adaptation_counters() const {
|
||
|
|
return adaptation_counters_;
|
||
|
|
}
|
||
|
|
const Resource* reason() const { return reason_; }
|
||
|
|
|
||
|
|
// ResourceAdaptationProcessorListener implementation.
|
||
|
|
void OnVideoSourceRestrictionsUpdated(
|
||
|
|
VideoSourceRestrictions restrictions,
|
||
|
|
const VideoAdaptationCounters& adaptation_counters,
|
||
|
|
const Resource* reason) override {
|
||
|
|
++restrictions_updated_count_;
|
||
|
|
restrictions_ = restrictions;
|
||
|
|
adaptation_counters_ = adaptation_counters;
|
||
|
|
reason_ = reason;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
size_t restrictions_updated_count_;
|
||
|
|
VideoSourceRestrictions restrictions_;
|
||
|
|
VideoAdaptationCounters adaptation_counters_;
|
||
|
|
const Resource* reason_;
|
||
|
|
};
|
||
|
|
|
||
|
|
class ResourceAdaptationProcessorTest : public ::testing::Test {
|
||
|
|
public:
|
||
|
|
ResourceAdaptationProcessorTest()
|
||
|
|
: frame_rate_provider_(),
|
||
|
|
input_state_provider_(&frame_rate_provider_),
|
||
|
|
resource_("FakeResource"),
|
||
|
|
processor_(&input_state_provider_,
|
||
|
|
/*encoder_stats_observer=*/&frame_rate_provider_) {
|
||
|
|
processor_.AddAdaptationListener(&processor_listener_);
|
||
|
|
processor_.AddResource(&resource_);
|
||
|
|
}
|
||
|
|
~ResourceAdaptationProcessorTest() override {
|
||
|
|
processor_.StopResourceAdaptation();
|
||
|
|
}
|
||
|
|
|
||
|
|
void SetInputStates(bool has_input, int fps, int frame_size) {
|
||
|
|
input_state_provider_.OnHasInputChanged(has_input);
|
||
|
|
frame_rate_provider_.set_fps(fps);
|
||
|
|
input_state_provider_.OnFrameSizeObserved(frame_size);
|
||
|
|
}
|
||
|
|
|
||
|
|
void RestrictSource(VideoSourceRestrictions restrictions) {
|
||
|
|
SetInputStates(
|
||
|
|
true, restrictions.max_frame_rate().value_or(kDefaultFrameRate),
|
||
|
|
restrictions.target_pixels_per_frame().has_value()
|
||
|
|
? restrictions.target_pixels_per_frame().value()
|
||
|
|
: restrictions.max_pixels_per_frame().value_or(kDefaultFrameSize));
|
||
|
|
}
|
||
|
|
|
||
|
|
protected:
|
||
|
|
FakeFrameRateProvider frame_rate_provider_;
|
||
|
|
VideoStreamInputStateProvider input_state_provider_;
|
||
|
|
FakeResource resource_;
|
||
|
|
ResourceAdaptationProcessor processor_;
|
||
|
|
ResourceAdaptationProcessorListenerForTesting processor_listener_;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, DisabledByDefault) {
|
||
|
|
EXPECT_EQ(DegradationPreference::DISABLED,
|
||
|
|
processor_.degradation_preference());
|
||
|
|
EXPECT_EQ(DegradationPreference::DISABLED,
|
||
|
|
processor_.effective_degradation_preference());
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
// Adaptation does not happen when disabled.
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(0u, processor_listener_.restrictions_updated_count());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, InsufficientInput) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
// Adaptation does not happen if input is insufficient.
|
||
|
|
// When frame size is missing (OnFrameSizeObserved not called yet).
|
||
|
|
input_state_provider_.OnHasInputChanged(true);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(0u, processor_listener_.restrictions_updated_count());
|
||
|
|
// When "has input" is missing.
|
||
|
|
SetInputStates(false, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(0u, processor_listener_.restrictions_updated_count());
|
||
|
|
// Note: frame rate cannot be missing, if unset it is 0.
|
||
|
|
}
|
||
|
|
|
||
|
|
// These tests verify that restrictions are applied, but not exactly how much
|
||
|
|
// the source is restricted. This ensures that the VideoStreamAdapter is wired
|
||
|
|
// up correctly but not exactly how the VideoStreamAdapter generates
|
||
|
|
// restrictions. For that, see video_stream_adapter_unittest.cc.
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest,
|
||
|
|
OveruseTriggersRestrictingResolutionInMaintainFrameRate) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
EXPECT_TRUE(
|
||
|
|
processor_listener_.restrictions().max_pixels_per_frame().has_value());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest,
|
||
|
|
OveruseTriggersRestrictingFrameRateInMaintainResolution) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_RESOLUTION);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
EXPECT_TRUE(processor_listener_.restrictions().max_frame_rate().has_value());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest,
|
||
|
|
OveruseTriggersRestrictingFrameRateAndResolutionInBalanced) {
|
||
|
|
processor_.SetDegradationPreference(DegradationPreference::BALANCED);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
// Adapting multiple times eventually resticts both frame rate and resolution.
|
||
|
|
// Exactly many times we need to adapt depends on BalancedDegradationSettings,
|
||
|
|
// VideoStreamAdapter and default input states. This test requires it to be
|
||
|
|
// achieved within 4 adaptations.
|
||
|
|
for (size_t i = 0; i < 4; ++i) {
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(i + 1, processor_listener_.restrictions_updated_count());
|
||
|
|
RestrictSource(processor_listener_.restrictions());
|
||
|
|
}
|
||
|
|
EXPECT_TRUE(
|
||
|
|
processor_listener_.restrictions().max_pixels_per_frame().has_value());
|
||
|
|
EXPECT_TRUE(processor_listener_.restrictions().max_frame_rate().has_value());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, AwaitingPreviousAdaptation) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
// If we don't restrict the source then adaptation will not happen again due
|
||
|
|
// to "awaiting previous adaptation". This prevents "double-adapt".
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, CannotAdaptUpWhenUnrestricted) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kUnderuse);
|
||
|
|
EXPECT_EQ(0u, processor_listener_.restrictions_updated_count());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, UnderuseTakesUsBackToUnrestricted) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
RestrictSource(processor_listener_.restrictions());
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kUnderuse);
|
||
|
|
EXPECT_EQ(2u, processor_listener_.restrictions_updated_count());
|
||
|
|
EXPECT_EQ(VideoSourceRestrictions(), processor_listener_.restrictions());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, ResourcesCanPreventAdaptingUp) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
// Adapt down so that we can adapt up.
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
RestrictSource(processor_listener_.restrictions());
|
||
|
|
// Adapting up is prevented.
|
||
|
|
resource_.set_is_adaptation_up_allowed(false);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kUnderuse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, AdaptingTriggersOnAdaptationApplied) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, resource_.num_adaptations_applied());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest, AdaptingClearsResourceUsageState) {
|
||
|
|
processor_.SetDegradationPreference(
|
||
|
|
DegradationPreference::MAINTAIN_FRAMERATE);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize);
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(1u, processor_listener_.restrictions_updated_count());
|
||
|
|
EXPECT_FALSE(resource_.usage_state().has_value());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_F(ResourceAdaptationProcessorTest,
|
||
|
|
FailingAdaptingAlsoClearsResourceUsageState) {
|
||
|
|
processor_.SetDegradationPreference(DegradationPreference::DISABLED);
|
||
|
|
processor_.StartResourceAdaptation();
|
||
|
|
resource_.set_usage_state(ResourceUsageState::kOveruse);
|
||
|
|
EXPECT_EQ(0u, processor_listener_.restrictions_updated_count());
|
||
|
|
EXPECT_FALSE(resource_.usage_state().has_value());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace webrtc
|