Don't await adaptation after deg-pref change
Now we only await a previous adaptation if the degradataion
preference is the same as our current degradation preference.
Without this guard we can get stuck as detailed in the attached bug.
Bug: webrtc:11562
Change-Id: I91be48546446ef8d01fe901bc6889201a5b97ba6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174805
Commit-Queue: Evan Shrubsole <eshr@google.com>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31236}
diff --git a/call/adaptation/video_stream_adapter.cc b/call/adaptation/video_stream_adapter.cc
index 7b15473..4ebe00f 100644
--- a/call/adaptation/video_stream_adapter.cc
+++ b/call/adaptation/video_stream_adapter.cc
@@ -315,22 +315,6 @@
VideoAdaptationCounters adaptations_;
};
-// static
-VideoStreamAdapter::AdaptationRequest::Mode
-VideoStreamAdapter::AdaptationRequest::GetModeFromAdaptationAction(
- Adaptation::StepType step_type) {
- switch (step_type) {
- case Adaptation::StepType::kIncreaseResolution:
- return AdaptationRequest::Mode::kAdaptUp;
- case Adaptation::StepType::kDecreaseResolution:
- return AdaptationRequest::Mode::kAdaptDown;
- case Adaptation::StepType::kIncreaseFrameRate:
- return AdaptationRequest::Mode::kAdaptUp;
- case Adaptation::StepType::kDecreaseFrameRate:
- return AdaptationRequest::Mode::kAdaptDown;
- }
-}
-
VideoStreamAdapter::VideoStreamAdapter()
: source_restrictor_(std::make_unique<VideoSourceRestrictor>()),
balanced_settings_(),
@@ -381,10 +365,10 @@
RTC_DCHECK_NE(degradation_preference_, DegradationPreference::DISABLED);
RTC_DCHECK(input_state_.HasInputFrameSizeAndFramesPerSecond());
// Don't adapt if we're awaiting a previous adaptation to have an effect.
- bool last_adaptation_was_up =
- last_adaptation_request_ &&
- last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
- if (last_adaptation_was_up &&
+ bool last_request_increased_resolution =
+ last_adaptation_request_ && last_adaptation_request_->step_type_ ==
+ Adaptation::StepType::kIncreaseResolution;
+ if (last_request_increased_resolution &&
degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
input_state_.frame_size_pixels().value() <=
last_adaptation_request_->input_pixel_count_) {
@@ -453,12 +437,12 @@
Adaptation VideoStreamAdapter::GetAdaptationDown() const {
RTC_DCHECK_NE(degradation_preference_, DegradationPreference::DISABLED);
RTC_DCHECK(input_state_.HasInputFrameSizeAndFramesPerSecond());
- // Don't adapt adaptation is disabled.
- bool last_adaptation_was_down =
- last_adaptation_request_ &&
- last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
- // Don't adapt if we're awaiting a previous adaptation to have an effect.
- if (last_adaptation_was_down &&
+ // Don't adapt if we're awaiting a previous adaptation to have an effect or
+ // if we switched degradation preference.
+ bool last_request_decreased_resolution =
+ last_adaptation_request_ && last_adaptation_request_->step_type_ ==
+ Adaptation::StepType::kDecreaseResolution;
+ if (last_request_decreased_resolution &&
degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
input_state_.frame_size_pixels().value() >=
last_adaptation_request_->input_pixel_count_) {
@@ -536,8 +520,7 @@
// adapting again before this adaptation has had an effect.
last_adaptation_request_.emplace(AdaptationRequest{
input_state_.frame_size_pixels().value(),
- input_state_.frames_per_second(),
- AdaptationRequest::GetModeFromAdaptationAction(adaptation.step().type)});
+ input_state_.frames_per_second(), adaptation.step().type});
// Adapt!
source_restrictor_->ApplyAdaptationStep(adaptation.step(),
degradation_preference_);
diff --git a/call/adaptation/video_stream_adapter.h b/call/adaptation/video_stream_adapter.h
index 1f44dd5..f313e6b 100644
--- a/call/adaptation/video_stream_adapter.h
+++ b/call/adaptation/video_stream_adapter.h
@@ -147,12 +147,8 @@
int input_pixel_count_;
// Framerate received from the source at the time of the adaptation.
int framerate_fps_;
- // Indicates if request was to adapt up or down.
- enum class Mode { kAdaptUp, kAdaptDown } mode_;
-
- // This is a static method rather than an anonymous namespace function due
- // to namespace visiblity.
- static Mode GetModeFromAdaptationAction(Adaptation::StepType step_type);
+ // Degradation preference for the request.
+ Adaptation::StepType step_type_;
};
// Owner and modifier of the VideoSourceRestriction of this stream adaptor.
diff --git a/call/adaptation/video_stream_adapter_unittest.cc b/call/adaptation/video_stream_adapter_unittest.cc
index 55d604e..79247a7 100644
--- a/call/adaptation/video_stream_adapter_unittest.cc
+++ b/call/adaptation/video_stream_adapter_unittest.cc
@@ -552,6 +552,130 @@
}
}
+TEST(VideoStreamAdapterTest,
+ MaintainResolution_AdaptsUpAfterSwitchingDegradationPreference) {
+ VideoStreamAdapter adapter;
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ FakeVideoStream fake_stream(&adapter, 1280 * 720, 30,
+ kDefaultMinPixelsPerFrame);
+ // Adapt down in fps for later.
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+ EXPECT_EQ(1, adapter.adaptation_counters().fps_adaptations);
+
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationUp());
+ EXPECT_EQ(1, adapter.adaptation_counters().fps_adaptations);
+ EXPECT_EQ(0, adapter.adaptation_counters().resolution_adaptations);
+
+ // We should be able to adapt in framerate one last time after the change of
+ // degradation preference.
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ Adaptation adaptation = adapter.GetAdaptationUp();
+ EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationUp());
+ EXPECT_EQ(0, adapter.adaptation_counters().fps_adaptations);
+}
+
+TEST(VideoStreamAdapterTest,
+ MaintainFramerate_AdaptsUpAfterSwitchingDegradationPreference) {
+ VideoStreamAdapter adapter;
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
+ FakeVideoStream fake_stream(&adapter, 1280 * 720, 30,
+ kDefaultMinPixelsPerFrame);
+ // Adapt down in resolution for later.
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+ EXPECT_EQ(1, adapter.adaptation_counters().resolution_adaptations);
+
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationUp());
+ EXPECT_EQ(1, adapter.adaptation_counters().resolution_adaptations);
+ EXPECT_EQ(0, adapter.adaptation_counters().fps_adaptations);
+
+ // We should be able to adapt in framerate one last time after the change of
+ // degradation preference.
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
+ Adaptation adaptation = adapter.GetAdaptationUp();
+ EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationUp());
+ EXPECT_EQ(0, adapter.adaptation_counters().resolution_adaptations);
+}
+
+TEST(VideoStreamAdapterTest,
+ PendingResolutionIncreaseAllowsAdaptUpAfterSwitchToMaintainResolution) {
+ VideoStreamAdapter adapter;
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ FakeVideoStream fake_stream(&adapter, 1280 * 720, 30,
+ kDefaultMinPixelsPerFrame);
+ // Adapt fps down so we can adapt up later in the test.
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+ // Apply adaptation up but don't update input.
+ adapter.ApplyAdaptation(adapter.GetAdaptationUp());
+ EXPECT_EQ(Adaptation::Status::kAwaitingPreviousAdaptation,
+ adapter.GetAdaptationUp().status());
+
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ Adaptation adaptation = adapter.GetAdaptationUp();
+ EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
+}
+
+TEST(VideoStreamAdapterTest,
+ MaintainFramerate_AdaptsDownAfterSwitchingDegradationPreference) {
+ VideoStreamAdapter adapter;
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ FakeVideoStream fake_stream(&adapter, 1280 * 720, 30,
+ kDefaultMinPixelsPerFrame);
+ // Adapt down once, should change FPS.
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+ EXPECT_EQ(1, adapter.adaptation_counters().fps_adaptations);
+
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
+ // Adaptation down should apply after the degradation prefs change.
+ Adaptation adaptation = adapter.GetAdaptationDown();
+ EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
+ fake_stream.ApplyAdaptation(adaptation);
+ EXPECT_EQ(1, adapter.adaptation_counters().fps_adaptations);
+ EXPECT_EQ(1, adapter.adaptation_counters().resolution_adaptations);
+}
+
+TEST(VideoStreamAdapterTest,
+ MaintainResolution_AdaptsDownAfterSwitchingDegradationPreference) {
+ VideoStreamAdapter adapter;
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
+ FakeVideoStream fake_stream(&adapter, 1280 * 720, 30,
+ kDefaultMinPixelsPerFrame);
+ // Adapt down once, should change FPS.
+ fake_stream.ApplyAdaptation(adapter.GetAdaptationDown());
+ EXPECT_EQ(1, adapter.adaptation_counters().resolution_adaptations);
+
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ Adaptation adaptation = adapter.GetAdaptationDown();
+ EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
+ fake_stream.ApplyAdaptation(adaptation);
+
+ EXPECT_EQ(1, adapter.adaptation_counters().fps_adaptations);
+ EXPECT_EQ(1, adapter.adaptation_counters().resolution_adaptations);
+}
+
+TEST(VideoStreamAdapterTest,
+ PendingResolutionDecreaseAllowsAdaptDownAfterSwitchToMaintainResolution) {
+ VideoStreamAdapter adapter;
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
+ FakeVideoStream fake_stream(&adapter, 1280 * 720, 30,
+ kDefaultMinPixelsPerFrame);
+ // Apply adaptation but don't update the input.
+ adapter.ApplyAdaptation(adapter.GetAdaptationDown());
+ EXPECT_EQ(Adaptation::Status::kAwaitingPreviousAdaptation,
+ adapter.GetAdaptationDown().status());
+ adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
+ Adaptation adaptation = adapter.GetAdaptationDown();
+ EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
+}
+
TEST(VideoStreamAdapterTest, PeekNextRestrictions) {
VideoStreamAdapter adapter;
// Any non-disabled DegradationPreference will do.