| /* |
| * 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/adaptation/resource.h" |
| #include "api/scoped_refptr.h" |
| #include "api/video/video_adaptation_counters.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 "rtc_base/event.h" |
| #include "rtc_base/gunit.h" |
| #include "rtc_base/synchronization/mutex.h" |
| #include "rtc_base/task_queue_for_test.h" |
| #include "test/gtest.h" |
| #include "test/scoped_key_value_config.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| |
| const int kDefaultFrameRate = 30; |
| const int kDefaultFrameSize = 1280 * 720; |
| constexpr TimeDelta kDefaultTimeout = TimeDelta::Seconds(5); |
| |
| class VideoSourceRestrictionsListenerForTesting |
| : public VideoSourceRestrictionsListener { |
| public: |
| VideoSourceRestrictionsListenerForTesting() |
| : restrictions_updated_count_(0), |
| restrictions_(), |
| adaptation_counters_(), |
| reason_(nullptr) {} |
| ~VideoSourceRestrictionsListenerForTesting() override {} |
| |
| size_t restrictions_updated_count() const { |
| RTC_DCHECK_RUN_ON(&sequence_checker_); |
| return restrictions_updated_count_; |
| } |
| VideoSourceRestrictions restrictions() const { |
| RTC_DCHECK_RUN_ON(&sequence_checker_); |
| return restrictions_; |
| } |
| VideoAdaptationCounters adaptation_counters() const { |
| RTC_DCHECK_RUN_ON(&sequence_checker_); |
| return adaptation_counters_; |
| } |
| rtc::scoped_refptr<Resource> reason() const { |
| RTC_DCHECK_RUN_ON(&sequence_checker_); |
| return reason_; |
| } |
| |
| // VideoSourceRestrictionsListener implementation. |
| void OnVideoSourceRestrictionsUpdated( |
| VideoSourceRestrictions restrictions, |
| const VideoAdaptationCounters& adaptation_counters, |
| rtc::scoped_refptr<Resource> reason, |
| const VideoSourceRestrictions& unfiltered_restrictions) override { |
| RTC_DCHECK_RUN_ON(&sequence_checker_); |
| ++restrictions_updated_count_; |
| restrictions_ = restrictions; |
| adaptation_counters_ = adaptation_counters; |
| reason_ = reason; |
| } |
| |
| private: |
| SequenceChecker sequence_checker_; |
| size_t restrictions_updated_count_ RTC_GUARDED_BY(&sequence_checker_); |
| VideoSourceRestrictions restrictions_ RTC_GUARDED_BY(&sequence_checker_); |
| VideoAdaptationCounters adaptation_counters_ |
| RTC_GUARDED_BY(&sequence_checker_); |
| rtc::scoped_refptr<Resource> reason_ RTC_GUARDED_BY(&sequence_checker_); |
| }; |
| |
| class ResourceAdaptationProcessorTest : public ::testing::Test { |
| public: |
| ResourceAdaptationProcessorTest() |
| : frame_rate_provider_(), |
| input_state_provider_(&frame_rate_provider_), |
| resource_(FakeResource::Create("FakeResource")), |
| other_resource_(FakeResource::Create("OtherFakeResource")), |
| video_stream_adapter_( |
| std::make_unique<VideoStreamAdapter>(&input_state_provider_, |
| &frame_rate_provider_, |
| field_trials_)), |
| processor_(std::make_unique<ResourceAdaptationProcessor>( |
| video_stream_adapter_.get())) { |
| video_stream_adapter_->AddRestrictionsListener(&restrictions_listener_); |
| processor_->AddResource(resource_); |
| processor_->AddResource(other_resource_); |
| } |
| ~ResourceAdaptationProcessorTest() override { |
| if (processor_) { |
| DestroyProcessor(); |
| } |
| } |
| |
| 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)); |
| } |
| |
| void DestroyProcessor() { |
| if (resource_) { |
| processor_->RemoveResource(resource_); |
| } |
| if (other_resource_) { |
| processor_->RemoveResource(other_resource_); |
| } |
| video_stream_adapter_->RemoveRestrictionsListener(&restrictions_listener_); |
| processor_.reset(); |
| } |
| |
| static void WaitUntilTaskQueueIdle() { |
| ASSERT_TRUE(rtc::Thread::Current()->ProcessMessages(0)); |
| } |
| |
| protected: |
| rtc::AutoThread main_thread_; |
| webrtc::test::ScopedKeyValueConfig field_trials_; |
| FakeFrameRateProvider frame_rate_provider_; |
| VideoStreamInputStateProvider input_state_provider_; |
| rtc::scoped_refptr<FakeResource> resource_; |
| rtc::scoped_refptr<FakeResource> other_resource_; |
| std::unique_ptr<VideoStreamAdapter> video_stream_adapter_; |
| std::unique_ptr<ResourceAdaptationProcessor> processor_; |
| VideoSourceRestrictionsListenerForTesting restrictions_listener_; |
| }; |
| |
| } // namespace |
| |
| TEST_F(ResourceAdaptationProcessorTest, DisabledByDefault) { |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| // Adaptation does not happen when disabled. |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, InsufficientInput) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| // Adaptation does not happen if input is insufficient. |
| // When frame size is missing (OnFrameSizeObserved not called yet). |
| input_state_provider_.OnHasInputChanged(true); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count()); |
| // When "has input" is missing. |
| SetInputStates(false, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(0u, restrictions_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) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count()); |
| EXPECT_TRUE( |
| restrictions_listener_.restrictions().max_pixels_per_frame().has_value()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| OveruseTriggersRestrictingFrameRateInMaintainResolution) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_RESOLUTION); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count()); |
| EXPECT_TRUE( |
| restrictions_listener_.restrictions().max_frame_rate().has_value()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| OveruseTriggersRestrictingFrameRateAndResolutionInBalanced) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::BALANCED); |
| 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_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(i + 1, restrictions_listener_.restrictions_updated_count()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| } |
| EXPECT_TRUE( |
| restrictions_listener_.restrictions().max_pixels_per_frame().has_value()); |
| EXPECT_TRUE( |
| restrictions_listener_.restrictions().max_frame_rate().has_value()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, AwaitingPreviousAdaptation) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1u, restrictions_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_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, CannotAdaptUpWhenUnrestricted) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, UnderuseTakesUsBackToUnrestricted) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(2u, restrictions_listener_.restrictions_updated_count()); |
| EXPECT_EQ(VideoSourceRestrictions(), restrictions_listener_.restrictions()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| ResourcesCanNotAdaptUpIfNeverAdaptedDown) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // Other resource signals under-use |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| ResourcesCanNotAdaptUpIfNotAdaptedDownAfterReset) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1u, restrictions_listener_.restrictions_updated_count()); |
| |
| video_stream_adapter_->ClearRestrictions(); |
| EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total()); |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // resource_ did not overuse after we reset the restrictions, so adapt |
| // up should be disallowed. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, OnlyMostLimitedResourceMayAdaptUp) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // `other_resource_` is most limited, resource_ can't adapt up. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // `resource_` and `other_resource_` are now most limited, so both must |
| // signal underuse to adapt up. |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| MultipleResourcesCanTriggerMultipleAdaptations) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // resource_ is not most limited so can't adapt from underuse. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| // resource_ is still not most limited so can't adapt from underuse. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // However it will be after overuse |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // Now other_resource_ can't adapt up as it is not most restricted. |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(3, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| // resource_ is limited at 3 adaptations and other_resource_ 2. |
| // With the most limited resource signalling underuse in the following |
| // order we get back to unrestricted video. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| // Both resource_ and other_resource_ are most limited. |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| // Again both are most limited. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| MostLimitedResourceAdaptationWorksAfterChangingDegradataionPreference) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| // Adapt down until we can't anymore. |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| int last_total = restrictions_listener_.adaptation_counters().Total(); |
| |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_RESOLUTION); |
| // resource_ can not adapt up since we have never reduced FPS. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(last_total, restrictions_listener_.adaptation_counters().Total()); |
| |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(last_total + 1, |
| restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| // other_resource_ is most limited so should be able to adapt up. |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(last_total, restrictions_listener_.adaptation_counters().Total()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| AdaptsDownWhenOtherResourceIsAlwaysUnderused) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| // Does not trigger adapataion because there's no restriction. |
| EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total()); |
| |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| // Adapts down even if other resource asked for adapting up. |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| |
| RestrictSource(restrictions_listener_.restrictions()); |
| other_resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| // Doesn't adapt up because adaptation is due to another resource. |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| TriggerOveruseNotOnAdaptationTaskQueue) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| TaskQueueForTest resource_task_queue("ResourceTaskQueue"); |
| resource_task_queue.PostTask( |
| [&]() { resource_->SetUsageState(ResourceUsageState::kOveruse); }); |
| |
| EXPECT_EQ_WAIT(1u, restrictions_listener_.restrictions_updated_count(), |
| kDefaultTimeout.ms()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| DestroyProcessorWhileResourceListenerDelegateHasTaskInFlight) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| // Wait for `resource_` to signal oversue first so we know that the delegate |
| // has passed it on to the processor's task queue. |
| rtc::Event resource_event; |
| TaskQueueForTest resource_task_queue("ResourceTaskQueue"); |
| resource_task_queue.PostTask([&]() { |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| resource_event.Set(); |
| }); |
| |
| EXPECT_TRUE(resource_event.Wait(kDefaultTimeout)); |
| // Now destroy the processor while handling the overuse is in flight. |
| DestroyProcessor(); |
| |
| // Because the processor was destroyed by the time the delegate's task ran, |
| // the overuse signal must not have been handled. |
| EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count()); |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| ResourceOveruseIgnoredWhenSignalledDuringRemoval) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| rtc::Event overuse_event; |
| TaskQueueForTest resource_task_queue("ResourceTaskQueue"); |
| // Queues task for `resource_` overuse while `processor_` is still listening. |
| resource_task_queue.PostTask([&]() { |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| overuse_event.Set(); |
| }); |
| EXPECT_TRUE(overuse_event.Wait(kDefaultTimeout)); |
| // Once we know the overuse task is queued, remove `resource_` so that |
| // `processor_` is not listening to it. |
| processor_->RemoveResource(resource_); |
| |
| // Runs the queued task so `processor_` gets signalled kOveruse from |
| // `resource_` even though `processor_` was not listening. |
| WaitUntilTaskQueueIdle(); |
| |
| // No restrictions should change even though `resource_` signaled `kOveruse`. |
| EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovingOnlyAdaptedResourceResetsAdaptation) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| RestrictSource(restrictions_listener_.restrictions()); |
| |
| processor_->RemoveResource(resource_); |
| EXPECT_EQ(0, restrictions_listener_.adaptation_counters().Total()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovingMostLimitedResourceSetsAdaptationToNextLimitedLevel) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::BALANCED); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| VideoSourceRestrictions next_limited_restrictions = |
| restrictions_listener_.restrictions(); |
| VideoAdaptationCounters next_limited_counters = |
| restrictions_listener_.adaptation_counters(); |
| |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| |
| // Removing most limited `resource_` should revert us back to |
| processor_->RemoveResource(resource_); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions()); |
| EXPECT_EQ(next_limited_counters, |
| restrictions_listener_.adaptation_counters()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovingMostLimitedResourceSetsAdaptationIfInputStateUnchanged) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| VideoSourceRestrictions next_limited_restrictions = |
| restrictions_listener_.restrictions(); |
| VideoAdaptationCounters next_limited_counters = |
| restrictions_listener_.adaptation_counters(); |
| |
| // Overuse twice and underuse once. After the underuse we don't restrict the |
| // source. Normally this would block future underuses. |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| |
| // Removing most limited `resource_` should revert us back to, even though we |
| // did not call RestrictSource() after `resource_` was overused. Normally |
| // adaptation for MAINTAIN_FRAMERATE would be blocked here but for removal we |
| // allow this anyways. |
| processor_->RemoveResource(resource_); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions()); |
| EXPECT_EQ(next_limited_counters, |
| restrictions_listener_.adaptation_counters()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovingResourceNotMostLimitedHasNoEffectOnLimitations) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::BALANCED); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| VideoSourceRestrictions current_restrictions = |
| restrictions_listener_.restrictions(); |
| VideoAdaptationCounters current_counters = |
| restrictions_listener_.adaptation_counters(); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| |
| // Removing most limited `resource_` should revert us back to |
| processor_->RemoveResource(other_resource_); |
| EXPECT_EQ(current_restrictions, restrictions_listener_.restrictions()); |
| EXPECT_EQ(current_counters, restrictions_listener_.adaptation_counters()); |
| |
| // Delete `other_resource_` for cleanup. |
| other_resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovingMostLimitedResourceAfterSwitchingDegradationPreferences) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| VideoSourceRestrictions next_limited_restrictions = |
| restrictions_listener_.restrictions(); |
| VideoAdaptationCounters next_limited_counters = |
| restrictions_listener_.adaptation_counters(); |
| |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_RESOLUTION); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| |
| // Revert to `other_resource_` when removing `resource_` even though the |
| // degradation preference was different when it was overused. |
| processor_->RemoveResource(resource_); |
| EXPECT_EQ(next_limited_counters, |
| restrictions_listener_.adaptation_counters()); |
| |
| // After switching back to MAINTAIN_FRAMERATE, the next most limited settings |
| // are restored. |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovingMostLimitedResourceSetsNextLimitationsInDisabled) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| VideoSourceRestrictions next_limited_restrictions = |
| restrictions_listener_.restrictions(); |
| VideoAdaptationCounters next_limited_counters = |
| restrictions_listener_.adaptation_counters(); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(2, restrictions_listener_.adaptation_counters().Total()); |
| |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::DISABLED); |
| |
| // Revert to `other_resource_` when removing `resource_` even though the |
| // current degradataion preference is disabled. |
| processor_->RemoveResource(resource_); |
| |
| // After switching back to MAINTAIN_FRAMERATE, the next most limited settings |
| // are restored. |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| EXPECT_EQ(next_limited_restrictions, restrictions_listener_.restrictions()); |
| EXPECT_EQ(next_limited_counters, |
| restrictions_listener_.adaptation_counters()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovedResourceSignalsIgnoredByProcessor) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| processor_->RemoveResource(resource_); |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| EXPECT_EQ(0u, restrictions_listener_.restrictions_updated_count()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| RemovingResourceWhenMultipleMostLimtedHasNoEffect) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| // Adapt `resource_` up and then down so that both resource's are most |
| // limited at 1 adaptation. |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| |
| // Removing `resource_` has no effect since both `resource_` and |
| // `other_resource_` are most limited. |
| processor_->RemoveResource(resource_); |
| EXPECT_EQ(1, restrictions_listener_.adaptation_counters().Total()); |
| |
| // Delete `resource_` for cleanup. |
| resource_ = nullptr; |
| } |
| |
| TEST_F(ResourceAdaptationProcessorTest, |
| ResourceOverusedAtLimitReachedWillShareMostLimited) { |
| video_stream_adapter_->SetDegradationPreference( |
| DegradationPreference::MAINTAIN_FRAMERATE); |
| SetInputStates(true, kDefaultFrameRate, kDefaultFrameSize); |
| |
| bool has_reached_min_pixels = false; |
| ON_CALL(frame_rate_provider_, OnMinPixelLimitReached()) |
| .WillByDefault(testing::Assign(&has_reached_min_pixels, true)); |
| |
| // Adapt 10 times, which should make us hit the limit. |
| for (int i = 0; i < 10; ++i) { |
| resource_->SetUsageState(ResourceUsageState::kOveruse); |
| RestrictSource(restrictions_listener_.restrictions()); |
| } |
| EXPECT_TRUE(has_reached_min_pixels); |
| auto last_update_count = restrictions_listener_.restrictions_updated_count(); |
| other_resource_->SetUsageState(ResourceUsageState::kOveruse); |
| // Now both `resource_` and `other_resource_` are most limited. Underuse of |
| // `resource_` will not adapt up. |
| resource_->SetUsageState(ResourceUsageState::kUnderuse); |
| EXPECT_EQ(last_update_count, |
| restrictions_listener_.restrictions_updated_count()); |
| } |
| |
| } // namespace webrtc |