diff --git a/call/adaptation/resource_adaptation_processor.cc b/call/adaptation/resource_adaptation_processor.cc index 33d05f0932..79fb9daab2 100644 --- a/call/adaptation/resource_adaptation_processor.cc +++ b/call/adaptation/resource_adaptation_processor.cc @@ -29,7 +29,7 @@ ResourceAdaptationProcessor::ResourceAdaptationProcessor( last_reported_source_restrictions_(), processing_in_progress_(false) {} -ResourceAdaptationProcessor::~ResourceAdaptationProcessor() {} +ResourceAdaptationProcessor::~ResourceAdaptationProcessor() = default; DegradationPreference ResourceAdaptationProcessor::degradation_preference() const { @@ -85,22 +85,26 @@ void ResourceAdaptationProcessor::MaybeUpdateEffectiveDegradationPreference() { void ResourceAdaptationProcessor::ResetVideoSourceRestrictions() { stream_adapter_->ClearRestrictions(); + adaptations_counts_by_resource_.clear(); MaybeUpdateVideoSourceRestrictions(nullptr); } void ResourceAdaptationProcessor::MaybeUpdateVideoSourceRestrictions( const Resource* reason) { - VideoSourceRestrictions new_soure_restrictions = + VideoSourceRestrictions new_source_restrictions = FilterRestrictionsByDegradationPreference( stream_adapter_->source_restrictions(), effective_degradation_preference_); - if (last_reported_source_restrictions_ != new_soure_restrictions) { - last_reported_source_restrictions_ = std::move(new_soure_restrictions); + if (last_reported_source_restrictions_ != new_source_restrictions) { + last_reported_source_restrictions_ = std::move(new_source_restrictions); for (auto* adaptation_listener : adaptation_listeners_) { adaptation_listener->OnVideoSourceRestrictionsUpdated( last_reported_source_restrictions_, stream_adapter_->adaptation_counters(), reason); } + if (reason) { + UpdateResourceDegradationCounts(reason); + } } } @@ -142,6 +146,10 @@ void ResourceAdaptationProcessor::OnResourceUnderuse( processing_in_progress_ = false; return; } + if (!IsResourceAllowedToAdaptUp(&reason_resource)) { + processing_in_progress_ = false; + return; + } // Update video input states and encoder settings for accurate adaptation. stream_adapter_->SetInput(input_state); // How can this stream be adapted up? @@ -202,8 +210,9 @@ void ResourceAdaptationProcessor::OnResourceOveruse( stream_adapter_->SetInput(input_state); // How can this stream be adapted up? Adaptation adaptation = stream_adapter_->GetAdaptationDown(); - if (adaptation.min_pixel_limit_reached()) + if (adaptation.min_pixel_limit_reached()) { encoder_stats_observer_->OnMinPixelLimitReached(); + } if (adaptation.status() != Adaptation::Status::kValid) { processing_in_progress_ = false; return; @@ -242,4 +251,25 @@ void ResourceAdaptationProcessor::TriggerAdaptationDueToFrameDroppedDueToSize( } } +void ResourceAdaptationProcessor::UpdateResourceDegradationCounts( + const Resource* resource) { + RTC_DCHECK(resource); + int delta = stream_adapter_->adaptation_counters().Total(); + for (const auto& adaptations : adaptations_counts_by_resource_) { + delta -= adaptations.second; + } + + // Default value is 0, inserts the value if missing. + adaptations_counts_by_resource_[resource] += delta; + RTC_DCHECK_GE(adaptations_counts_by_resource_[resource], 0); +} + +bool ResourceAdaptationProcessor::IsResourceAllowedToAdaptUp( + const Resource* resource) const { + RTC_DCHECK(resource); + const auto& adaptations = adaptations_counts_by_resource_.find(resource); + return adaptations != adaptations_counts_by_resource_.end() && + adaptations->second > 0; +} + } // namespace webrtc diff --git a/call/adaptation/resource_adaptation_processor.h b/call/adaptation/resource_adaptation_processor.h index 2bf10b955c..de38751399 100644 --- a/call/adaptation/resource_adaptation_processor.h +++ b/call/adaptation/resource_adaptation_processor.h @@ -11,6 +11,7 @@ #ifndef CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_H_ #define CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_H_ +#include #include #include @@ -76,12 +77,24 @@ class ResourceAdaptationProcessor : public ResourceAdaptationProcessorInterface, // If the filtered source restrictions are different than // |last_reported_source_restrictions_|, inform the listeners. void MaybeUpdateVideoSourceRestrictions(const Resource* reason); + // Updates the number of times the resource has degraded based on the latest + // degradation applied. + void UpdateResourceDegradationCounts(const Resource* resource); + // Returns true if a Resource has been overused in the pass and is responsible + // for creating a VideoSourceRestriction. The current algorithm counts the + // number of times the resource caused an adaptation and allows adapting up + // if that number is non-zero. This is consistent with how adaptation has + // traditionally been handled. + // TODO(crbug.com/webrtc/11553) Change this algorithm to look at the resources + // restrictions rather than just the counters. + bool IsResourceAllowedToAdaptUp(const Resource* resource) const; // Input and output. VideoStreamInputStateProvider* const input_state_provider_; VideoStreamEncoderObserver* const encoder_stats_observer_; std::vector adaptation_listeners_; std::vector resources_; + std::map adaptations_counts_by_resource_; // Adaptation strategy settings. DegradationPreference degradation_preference_; DegradationPreference effective_degradation_preference_; diff --git a/call/adaptation/resource_adaptation_processor_unittest.cc b/call/adaptation/resource_adaptation_processor_unittest.cc index 2e26dbec72..6fb56c9abd 100644 --- a/call/adaptation/resource_adaptation_processor_unittest.cc +++ b/call/adaptation/resource_adaptation_processor_unittest.cc @@ -69,10 +69,12 @@ class ResourceAdaptationProcessorTest : public ::testing::Test { : frame_rate_provider_(), input_state_provider_(&frame_rate_provider_), resource_("FakeResource"), + other_resource_("OtherFakeResource"), processor_(&input_state_provider_, /*encoder_stats_observer=*/&frame_rate_provider_) { processor_.AddAdaptationListener(&processor_listener_); processor_.AddResource(&resource_); + processor_.AddResource(&other_resource_); } ~ResourceAdaptationProcessorTest() override { processor_.StopResourceAdaptation(); @@ -96,6 +98,7 @@ class ResourceAdaptationProcessorTest : public ::testing::Test { FakeFrameRateProvider frame_rate_provider_; VideoStreamInputStateProvider input_state_provider_; FakeResource resource_; + FakeResource other_resource_; ResourceAdaptationProcessor processor_; ResourceAdaptationProcessorListenerForTesting processor_listener_; }; @@ -226,6 +229,74 @@ TEST_F(ResourceAdaptationProcessorTest, ResourcesCanPreventAdaptingUp) { EXPECT_EQ(1u, processor_listener_.restrictions_updated_count()); } +TEST_F(ResourceAdaptationProcessorTest, + ResourcesCanNotAdaptUpIfNeverAdaptedDown) { + 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()); + + // Other resource signals under-use + other_resource_.set_usage_state(ResourceUsageState::kUnderuse); + EXPECT_EQ(1u, processor_listener_.restrictions_updated_count()); +} + +TEST_F(ResourceAdaptationProcessorTest, + ResourcesCanNotAdaptUpIfNotAdaptedDownAfterReset) { + 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()); + + processor_.ResetVideoSourceRestrictions(); + EXPECT_EQ(0, processor_listener_.adaptation_counters().Total()); + other_resource_.set_usage_state(ResourceUsageState::kOveruse); + EXPECT_EQ(1, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); + + // resource_ did not overuse after we reset the restrictions, so adapt up + // should be disallowed. + resource_.set_usage_state(ResourceUsageState::kUnderuse); + EXPECT_EQ(1, processor_listener_.adaptation_counters().Total()); +} + +TEST_F(ResourceAdaptationProcessorTest, + MultipleResourcesCanTriggerMultipleAdaptations) { + processor_.SetDegradationPreference( + DegradationPreference::MAINTAIN_FRAMERATE); + processor_.StartResourceAdaptation(); + SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); + resource_.set_usage_state(ResourceUsageState::kOveruse); + EXPECT_EQ(1, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); + other_resource_.set_usage_state(ResourceUsageState::kOveruse); + EXPECT_EQ(2, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); + other_resource_.set_usage_state(ResourceUsageState::kOveruse); + EXPECT_EQ(3, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); + + resource_.set_usage_state(ResourceUsageState::kUnderuse); + EXPECT_EQ(2, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); + // Does not trigger adaptation since resource has no adaptations left. + resource_.set_usage_state(ResourceUsageState::kUnderuse); + EXPECT_EQ(2, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); + + other_resource_.set_usage_state(ResourceUsageState::kUnderuse); + EXPECT_EQ(1, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); + other_resource_.set_usage_state(ResourceUsageState::kUnderuse); + EXPECT_EQ(0, processor_listener_.adaptation_counters().Total()); + RestrictSource(processor_listener_.restrictions()); +} + TEST_F(ResourceAdaptationProcessorTest, AdaptingTriggersOnAdaptationApplied) { processor_.SetDegradationPreference( DegradationPreference::MAINTAIN_FRAMERATE);