diff --git a/call/adaptation/resource_adaptation_processor.cc b/call/adaptation/resource_adaptation_processor.cc index 277c6737c7..414132dfc6 100644 --- a/call/adaptation/resource_adaptation_processor.cc +++ b/call/adaptation/resource_adaptation_processor.cc @@ -390,26 +390,6 @@ ResourceAdaptationProcessor::OnResourceOveruse( message.Release()); } -void ResourceAdaptationProcessor::TriggerAdaptationDueToFrameDroppedDueToSize( - rtc::scoped_refptr reason_resource) { - RTC_DCHECK_RUN_ON(resource_adaptation_queue_); - RTC_LOG(INFO) << "TriggerAdaptationDueToFrameDroppedDueToSize called"; - VideoAdaptationCounters counters_before = - stream_adapter_->adaptation_counters(); - OnResourceOveruse(reason_resource); - if (effective_degradation_preference_ == DegradationPreference::BALANCED && - stream_adapter_->adaptation_counters().fps_adaptations > - counters_before.fps_adaptations) { - // Oops, we adapted frame rate. Adapt again, maybe it will adapt resolution! - // Though this is not guaranteed... - OnResourceOveruse(reason_resource); - } - if (stream_adapter_->adaptation_counters().resolution_adaptations > - counters_before.resolution_adaptations) { - encoder_stats_observer_->OnInitialQualityResolutionAdaptDown(); - } -} - std::pair>, VideoStreamAdapter::RestrictionsWithCounters> ResourceAdaptationProcessor::FindMostLimitedResources() const { diff --git a/call/adaptation/resource_adaptation_processor.h b/call/adaptation/resource_adaptation_processor.h index 05338044f3..33e964ea1a 100644 --- a/call/adaptation/resource_adaptation_processor.h +++ b/call/adaptation/resource_adaptation_processor.h @@ -96,12 +96,6 @@ class ResourceAdaptationProcessor : public ResourceAdaptationProcessorInterface, void OnDegradationPreferenceUpdated( DegradationPreference degradation_preference) override; - // May trigger 1-2 adaptations. It is meant to reduce resolution but this is - // not guaranteed. It may adapt frame rate, which does not address the issue. - // TODO(hbos): Can we get rid of this? - void TriggerAdaptationDueToFrameDroppedDueToSize( - rtc::scoped_refptr reason_resource) override; - private: // If resource usage measurements happens off the adaptation task queue, this // class takes care of posting the measurement for the processor to handle it diff --git a/call/adaptation/resource_adaptation_processor_interface.h b/call/adaptation/resource_adaptation_processor_interface.h index e8cca26abb..cd684419c8 100644 --- a/call/adaptation/resource_adaptation_processor_interface.h +++ b/call/adaptation/resource_adaptation_processor_interface.h @@ -72,15 +72,6 @@ class ResourceAdaptationProcessorInterface { AdaptationListener* adaptation_listener) = 0; virtual void RemoveAdaptationListener( AdaptationListener* adaptation_listener) = 0; - - // May trigger one or more adaptations. It is meant to reduce resolution - - // useful if a frame was dropped due to its size - however, the implementation - // may not guarantee this (see resource_adaptation_processor.h). - // TODO(hbos): This is only part of the interface for backwards-compatiblity - // reasons. Can we replace this by something which actually satisfies the - // resolution constraints or get rid of it altogether? - virtual void TriggerAdaptationDueToFrameDroppedDueToSize( - rtc::scoped_refptr reason_resource) = 0; }; } // namespace webrtc diff --git a/call/adaptation/video_stream_adapter.cc b/call/adaptation/video_stream_adapter.cc index 39de9fb8ea..0cc03e48d6 100644 --- a/call/adaptation/video_stream_adapter.cc +++ b/call/adaptation/video_stream_adapter.cc @@ -521,6 +521,49 @@ VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::IncreaseFramerate( return new_restrictions; } +Adaptation VideoStreamAdapter::GetAdaptDownResolution() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + VideoStreamInputState input_state = input_state_provider_->InputState(); + switch (degradation_preference_) { + case DegradationPreference::DISABLED: + case DegradationPreference::MAINTAIN_RESOLUTION: { + return Adaptation(adaptation_validation_id_, + Adaptation::Status::kLimitReached, input_state, false); + } + case DegradationPreference::MAINTAIN_FRAMERATE: + return GetAdaptationDown(); + case DegradationPreference::BALANCED: { + return RestrictionsOrStateToAdaptation( + GetAdaptDownResolutionStepForBalanced(input_state), input_state); + } + default: + RTC_NOTREACHED(); + } +} + +VideoStreamAdapter::RestrictionsOrState +VideoStreamAdapter::GetAdaptDownResolutionStepForBalanced( + const VideoStreamInputState& input_state) const { + // Adapt twice if the first adaptation did not decrease resolution. + auto first_step = GetAdaptationDownStep(input_state); + if (!absl::holds_alternative(first_step)) { + return first_step; + } + auto first_restrictions = absl::get(first_step); + if (first_restrictions.counters.resolution_adaptations > + current_restrictions_.counters.resolution_adaptations) { + return first_step; + } + // We didn't decrease resolution so force it; amend a resolution resuction + // to the existing framerate reduction in |first_restrictions|. + auto second_step = DecreaseResolution(input_state, first_restrictions); + if (absl::holds_alternative(second_step)) { + return second_step; + } + // If the second step was not successful then settle for the first one. + return first_step; +} + void VideoStreamAdapter::ApplyAdaptation( const Adaptation& adaptation, rtc::scoped_refptr resource) { diff --git a/call/adaptation/video_stream_adapter.h b/call/adaptation/video_stream_adapter.h index fc88a581cb..f9202ea0d2 100644 --- a/call/adaptation/video_stream_adapter.h +++ b/call/adaptation/video_stream_adapter.h @@ -145,6 +145,11 @@ class VideoStreamAdapter { Adaptation GetAdaptationDown(); Adaptation GetAdaptationTo(const VideoAdaptationCounters& counters, const VideoSourceRestrictions& restrictions); + // Tries to adapt the resolution one step. This is used for initial frame + // dropping. Does nothing if the degradation preference is not BALANCED or + // MAINTAIN_FRAMERATE. In the case of BALANCED, it will try twice to reduce + // the resolution. If it fails twice it gives up. + Adaptation GetAdaptDownResolution(); // Updates source_restrictions() the Adaptation. void ApplyAdaptation(const Adaptation& adaptation, @@ -170,6 +175,9 @@ class VideoStreamAdapter { RestrictionsOrState GetAdaptationDownStep( const VideoStreamInputState& input_state) const RTC_RUN_ON(&sequence_checker_); + RestrictionsOrState GetAdaptDownResolutionStepForBalanced( + const VideoStreamInputState& input_state) const + RTC_RUN_ON(&sequence_checker_); Adaptation GetAdaptationUp(const VideoStreamInputState& input_state) const RTC_RUN_ON(&sequence_checker_); diff --git a/call/adaptation/video_stream_adapter_unittest.cc b/call/adaptation/video_stream_adapter_unittest.cc index 1df5a54fd0..99b9e9c8df 100644 --- a/call/adaptation/video_stream_adapter_unittest.cc +++ b/call/adaptation/video_stream_adapter_unittest.cc @@ -782,6 +782,82 @@ TEST_F(VideoStreamAdapterTest, EXPECT_EQ(0, adapter_.adaptation_counters().Total()); } +TEST_F(VideoStreamAdapterTest, + GetAdaptDownResolutionAdaptsResolutionInMaintainFramerate) { + adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); + input_state_provider_.SetInputState(1280 * 720, 30, + kDefaultMinPixelsPerFrame); + + auto adaptation = adapter_.GetAdaptDownResolution(); + EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); + EXPECT_EQ(1, adaptation.counters().resolution_adaptations); + EXPECT_EQ(0, adaptation.counters().fps_adaptations); +} + +TEST_F( + VideoStreamAdapterTest, + GetAdaptDownResolutionReturnsLimitReachedInDisabledAndMaintainResolution) { + adapter_.SetDegradationPreference(DegradationPreference::DISABLED); + input_state_provider_.SetInputState(1280 * 720, 30, + kDefaultMinPixelsPerFrame); + EXPECT_EQ(Adaptation::Status::kLimitReached, + adapter_.GetAdaptDownResolution().status()); + adapter_.SetDegradationPreference(DegradationPreference::DISABLED); + EXPECT_EQ(Adaptation::Status::kLimitReached, + adapter_.GetAdaptDownResolution().status()); +} + +TEST_F(VideoStreamAdapterTest, + GetAdaptDownResolutionAdaptsFpsAndResolutionInBalanced) { + // Note: This test depends on BALANCED implementation, but with current + // implementation and input state settings, BALANCED will adapt resolution and + // frame rate once. + adapter_.SetDegradationPreference(DegradationPreference::BALANCED); + input_state_provider_.SetInputState(1280 * 720, 30, + kDefaultMinPixelsPerFrame); + + auto adaptation = adapter_.GetAdaptDownResolution(); + EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); + EXPECT_EQ(1, adaptation.counters().resolution_adaptations); + EXPECT_EQ(1, adaptation.counters().fps_adaptations); +} + +TEST_F( + VideoStreamAdapterTest, + GetAdaptDownResolutionAdaptsOnlyResolutionIfFpsAlreadyAdapterInBalanced) { + // Note: This test depends on BALANCED implementation, but with current + // implementation and input state settings, BALANCED will adapt resolution + // only. + adapter_.SetDegradationPreference(DegradationPreference::BALANCED); + input_state_provider_.SetInputState(1280 * 720, 5, kDefaultMinPixelsPerFrame); + FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, + kDefaultMinPixelsPerFrame); + + auto first_adaptation = adapter_.GetAdaptationDown(); + fake_stream.ApplyAdaptation(first_adaptation); + + auto adaptation = adapter_.GetAdaptDownResolution(); + EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); + EXPECT_EQ(1, adaptation.counters().resolution_adaptations); + EXPECT_EQ(first_adaptation.counters().fps_adaptations, + adaptation.counters().fps_adaptations); +} + +TEST_F(VideoStreamAdapterTest, + GetAdaptDownResolutionAdaptsOnlyFpsIfResolutionLowInBalanced) { + // Note: This test depends on BALANCED implementation, but with current + // implementation and input state settings, BALANCED will adapt resolution + // only. + adapter_.SetDegradationPreference(DegradationPreference::BALANCED); + input_state_provider_.SetInputState(kDefaultMinPixelsPerFrame, 30, + kDefaultMinPixelsPerFrame); + + auto adaptation = adapter_.GetAdaptDownResolution(); + EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); + EXPECT_EQ(0, adaptation.counters().resolution_adaptations); + EXPECT_EQ(1, adaptation.counters().fps_adaptations); +} + // Death tests. // Disabled on Android because death tests misbehave on Android, see // base/test/gtest_util.h. diff --git a/video/adaptation/video_stream_encoder_resource_manager.cc b/video/adaptation/video_stream_encoder_resource_manager.cc index 52ab21399d..be1085c5e3 100644 --- a/video/adaptation/video_stream_encoder_resource_manager.cc +++ b/video/adaptation/video_stream_encoder_resource_manager.cc @@ -445,8 +445,11 @@ void VideoStreamEncoderResourceManager::OnFrameDroppedDueToSize() { // happens if the processor is destroyed. No action needed. return; } - adaptation_processor_->TriggerAdaptationDueToFrameDroppedDueToSize( - quality_scaler_resource_); + Adaptation reduce_resolution = stream_adapter_->GetAdaptDownResolution(); + if (reduce_resolution.status() == Adaptation::Status::kValid) { + stream_adapter_->ApplyAdaptation(reduce_resolution, + quality_scaler_resource_); + } }); initial_frame_dropper_->OnFrameDroppedDueToSize(); }