| /* |
| * 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/video_stream_adapter.h" |
| |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include "api/scoped_refptr.h" |
| #include "api/video/video_adaptation_reason.h" |
| #include "api/video_codecs/video_codec.h" |
| #include "api/video_codecs/video_encoder.h" |
| #include "call/adaptation/adaptation_constraint.h" |
| #include "call/adaptation/encoder_settings.h" |
| #include "call/adaptation/test/fake_frame_rate_provider.h" |
| #include "call/adaptation/test/fake_resource.h" |
| #include "call/adaptation/test/fake_video_stream_input_state_provider.h" |
| #include "call/adaptation/video_source_restrictions.h" |
| #include "call/adaptation/video_stream_input_state.h" |
| #include "rtc_base/string_encode.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "test/scoped_key_value_config.h" |
| #include "test/testsupport/rtc_expect_death.h" |
| #include "video/config/video_encoder_config.h" |
| |
| namespace webrtc { |
| |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| |
| namespace { |
| |
| const int kBalancedHighResolutionPixels = 1280 * 720; |
| const int kBalancedHighFrameRateFps = 30; |
| |
| const int kBalancedMediumResolutionPixels = 640 * 480; |
| const int kBalancedMediumFrameRateFps = 20; |
| |
| const int kBalancedLowResolutionPixels = 320 * 240; |
| const int kBalancedLowFrameRateFps = 10; |
| |
| std::string BalancedFieldTrialConfig() { |
| return "WebRTC-Video-BalancedDegradationSettings/pixels:" + |
| rtc::ToString(kBalancedLowResolutionPixels) + "|" + |
| rtc::ToString(kBalancedMediumResolutionPixels) + "|" + |
| rtc::ToString(kBalancedHighResolutionPixels) + |
| ",fps:" + rtc::ToString(kBalancedLowFrameRateFps) + "|" + |
| rtc::ToString(kBalancedMediumFrameRateFps) + "|" + |
| rtc::ToString(kBalancedHighFrameRateFps) + "/"; |
| } |
| |
| // Responsible for adjusting the inputs to VideoStreamAdapter (SetInput), such |
| // as pixels and frame rate, according to the most recent source restrictions. |
| // This helps tests that apply adaptations multiple times: if the input is not |
| // adjusted between adaptations, the subsequent adaptations fail with |
| // kAwaitingPreviousAdaptation. |
| class FakeVideoStream { |
| public: |
| FakeVideoStream(VideoStreamAdapter* adapter, |
| FakeVideoStreamInputStateProvider* provider, |
| int input_pixels, |
| int input_fps, |
| int min_pixels_per_frame) |
| : adapter_(adapter), |
| provider_(provider), |
| input_pixels_(input_pixels), |
| input_fps_(input_fps), |
| min_pixels_per_frame_(min_pixels_per_frame) { |
| provider_->SetInputState(input_pixels_, input_fps_, min_pixels_per_frame_); |
| } |
| |
| int input_pixels() const { return input_pixels_; } |
| int input_fps() const { return input_fps_; } |
| |
| // Performs ApplyAdaptation() followed by SetInput() with input pixels and |
| // frame rate adjusted according to the resulting restrictions. |
| void ApplyAdaptation(Adaptation adaptation) { |
| adapter_->ApplyAdaptation(adaptation, nullptr); |
| // Update input pixels and fps according to the resulting restrictions. |
| auto restrictions = adapter_->source_restrictions(); |
| if (restrictions.target_pixels_per_frame().has_value()) { |
| RTC_DCHECK(!restrictions.max_pixels_per_frame().has_value() || |
| restrictions.max_pixels_per_frame().value() >= |
| restrictions.target_pixels_per_frame().value()); |
| input_pixels_ = restrictions.target_pixels_per_frame().value(); |
| } else if (restrictions.max_pixels_per_frame().has_value()) { |
| input_pixels_ = restrictions.max_pixels_per_frame().value(); |
| } |
| if (restrictions.max_frame_rate().has_value()) { |
| input_fps_ = restrictions.max_frame_rate().value(); |
| } |
| provider_->SetInputState(input_pixels_, input_fps_, min_pixels_per_frame_); |
| } |
| |
| private: |
| VideoStreamAdapter* adapter_; |
| FakeVideoStreamInputStateProvider* provider_; |
| int input_pixels_; |
| int input_fps_; |
| int min_pixels_per_frame_; |
| }; |
| |
| class FakeVideoStreamAdapterListner : public VideoSourceRestrictionsListener { |
| public: |
| void OnVideoSourceRestrictionsUpdated( |
| VideoSourceRestrictions /* restrictions */, |
| const VideoAdaptationCounters& /* adaptation_counters */, |
| rtc::scoped_refptr<Resource> /* reason */, |
| const VideoSourceRestrictions& unfiltered_restrictions) override { |
| calls_++; |
| last_restrictions_ = unfiltered_restrictions; |
| } |
| |
| int calls() const { return calls_; } |
| |
| VideoSourceRestrictions last_restrictions() const { |
| return last_restrictions_; |
| } |
| |
| private: |
| int calls_ = 0; |
| VideoSourceRestrictions last_restrictions_; |
| }; |
| |
| class MockAdaptationConstraint : public AdaptationConstraint { |
| public: |
| MOCK_METHOD(bool, |
| IsAdaptationUpAllowed, |
| (const VideoStreamInputState& input_state, |
| const VideoSourceRestrictions& restrictions_before, |
| const VideoSourceRestrictions& restrictions_after), |
| (const, override)); |
| |
| // MOCK_METHOD(std::string, Name, (), (const, override)); |
| std::string Name() const override { return "MockAdaptationConstraint"; } |
| }; |
| |
| } // namespace |
| |
| class VideoStreamAdapterTest : public ::testing::Test { |
| public: |
| VideoStreamAdapterTest() |
| : field_trials_(BalancedFieldTrialConfig()), |
| resource_(FakeResource::Create("FakeResource")), |
| adapter_(&input_state_provider_, |
| &encoder_stats_observer_, |
| field_trials_) {} |
| |
| protected: |
| webrtc::test::ScopedKeyValueConfig field_trials_; |
| FakeVideoStreamInputStateProvider input_state_provider_; |
| rtc::scoped_refptr<Resource> resource_; |
| testing::StrictMock<MockVideoStreamEncoderObserver> encoder_stats_observer_; |
| VideoStreamAdapter adapter_; |
| }; |
| |
| TEST_F(VideoStreamAdapterTest, NoRestrictionsByDefault) { |
| EXPECT_EQ(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(0, adapter_.adaptation_counters().Total()); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, MaintainFramerate_DecreasesPixelsToThreeFifths) { |
| const int kInputPixels = 1280 * 720; |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| input_state_provider_.SetInputState(kInputPixels, 30, |
| kDefaultMinPixelsPerFrame); |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| adapter_.ApplyAdaptation(adaptation, nullptr); |
| EXPECT_EQ(static_cast<size_t>((kInputPixels * 3) / 5), |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, |
| MaintainFramerate_DecreasesPixelsToLimitReached) { |
| const int kMinPixelsPerFrame = 640 * 480; |
| |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| input_state_provider_.SetInputState(kMinPixelsPerFrame + 1, 30, |
| kMinPixelsPerFrame); |
| EXPECT_CALL(encoder_stats_observer_, OnMinPixelLimitReached()); |
| // Even though we are above kMinPixelsPerFrame, because adapting down would |
| // have exceeded the limit, we are said to have reached the limit already. |
| // This differs from the frame rate adaptation logic, which would have clamped |
| // to the limit in the first step and reported kLimitReached in the second |
| // step. |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kLimitReached, adaptation.status()); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, MaintainFramerate_IncreasePixelsToFiveThirds) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Go down twice, ensuring going back up is still a restricted resolution. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(2, adapter_.adaptation_counters().resolution_adaptations); |
| int input_pixels = fake_stream.input_pixels(); |
| // Go up once. The target is 5/3 and the max is 12/5 of the target. |
| const int target = (input_pixels * 5) / 3; |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationUp()); |
| EXPECT_EQ(static_cast<size_t>((target * 12) / 5), |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(static_cast<size_t>(target), |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, MaintainFramerate_IncreasePixelsToUnrestricted) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // We are unrestricted by default and should not be able to adapt up. |
| EXPECT_EQ(Adaptation::Status::kLimitReached, |
| adapter_.GetAdaptationUp().status()); |
| // If we go down once and then back up we should not have any restrictions. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationUp()); |
| EXPECT_EQ(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(0, adapter_.adaptation_counters().Total()); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, MaintainResolution_DecreasesFpsToTwoThirds) { |
| const int kInputFps = 30; |
| |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| input_state_provider_.SetInputState(1280 * 720, kInputFps, |
| kDefaultMinPixelsPerFrame); |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| adapter_.ApplyAdaptation(adaptation, nullptr); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(static_cast<double>((kInputFps * 2) / 3), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, MaintainResolution_DecreasesFpsToLimitReached) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, |
| kMinFrameRateFps + 1, kDefaultMinPixelsPerFrame); |
| // If we are not yet at the limit and the next step would exceed it, the step |
| // is clamped such that we end up exactly on the limit. |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(static_cast<double>(kMinFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| // Having reached the limit, the next adaptation down is not valid. |
| EXPECT_EQ(Adaptation::Status::kLimitReached, |
| adapter_.GetAdaptationDown().status()); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, MaintainResolution_IncreaseFpsToThreeHalves) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Go down twice, ensuring going back up is still a restricted frame rate. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(2, adapter_.adaptation_counters().fps_adaptations); |
| int input_fps = fake_stream.input_fps(); |
| // Go up once. The target is 3/2 of the input. |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(static_cast<double>((input_fps * 3) / 2), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, MaintainResolution_IncreaseFpsToUnrestricted) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // We are unrestricted by default and should not be able to adapt up. |
| EXPECT_EQ(Adaptation::Status::kLimitReached, |
| adapter_.GetAdaptationUp().status()); |
| // If we go down once and then back up we should not have any restrictions. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationUp()); |
| EXPECT_EQ(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(0, adapter_.adaptation_counters().Total()); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, Balanced_DecreaseFrameRate) { |
| adapter_.SetDegradationPreference(DegradationPreference::BALANCED); |
| input_state_provider_.SetInputState(kBalancedMediumResolutionPixels, |
| kBalancedHighFrameRateFps, |
| kDefaultMinPixelsPerFrame); |
| // If our frame rate is higher than the frame rate associated with our |
| // resolution we should try to adapt to the frame rate associated with our |
| // resolution: kBalancedMediumFrameRateFps. |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| adapter_.ApplyAdaptation(adaptation, nullptr); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(static_cast<double>(kBalancedMediumFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(0, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, Balanced_DecreaseResolution) { |
| adapter_.SetDegradationPreference(DegradationPreference::BALANCED); |
| FakeVideoStream fake_stream( |
| &adapter_, &input_state_provider_, kBalancedHighResolutionPixels, |
| kBalancedHighFrameRateFps, kDefaultMinPixelsPerFrame); |
| // If we are not below the current resolution's frame rate limit, we should |
| // adapt resolution according to "maintain-framerate" logic (three fifths). |
| // |
| // However, since we are unlimited at the start and input frame rate is not |
| // below kBalancedHighFrameRateFps, we first restrict the frame rate to |
| // kBalancedHighFrameRateFps even though that is our current frame rate. This |
| // does prevent the source from going higher, though, so it's technically not |
| // a NO-OP. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| } |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(static_cast<double>(kBalancedHighFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(0, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| // Verify "maintain-framerate" logic the second time we adapt: Frame rate |
| // restrictions remains the same and resolution goes down. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| } |
| constexpr size_t kReducedPixelsFirstStep = |
| static_cast<size_t>((kBalancedHighResolutionPixels * 3) / 5); |
| EXPECT_EQ(kReducedPixelsFirstStep, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(static_cast<double>(kBalancedHighFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| // If we adapt again, because the balanced settings' proposed frame rate is |
| // still kBalancedHighFrameRateFps, "maintain-framerate" will trigger again. |
| static_assert(kReducedPixelsFirstStep > kBalancedMediumResolutionPixels, |
| "The reduced resolution is still greater than the next lower " |
| "balanced setting resolution"); |
| constexpr size_t kReducedPixelsSecondStep = (kReducedPixelsFirstStep * 3) / 5; |
| { |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| } |
| EXPECT_EQ(kReducedPixelsSecondStep, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| EXPECT_EQ(std::nullopt, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(static_cast<double>(kBalancedHighFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(2, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| } |
| |
| // Testing when to adapt frame rate and when to adapt resolution is quite |
| // entangled, so this test covers both cases. |
| // |
| // There is an asymmetry: When we adapt down we do it in one order, but when we |
| // adapt up we don't do it in the reverse order. Instead we always try to adapt |
| // frame rate first according to balanced settings' configs and only when the |
| // frame rate is already achieved do we adjust the resolution. |
| TEST_F(VideoStreamAdapterTest, Balanced_IncreaseFrameRateAndResolution) { |
| adapter_.SetDegradationPreference(DegradationPreference::BALANCED); |
| FakeVideoStream fake_stream( |
| &adapter_, &input_state_provider_, kBalancedHighResolutionPixels, |
| kBalancedHighFrameRateFps, kDefaultMinPixelsPerFrame); |
| // The desired starting point of this test is having adapted frame rate twice. |
| // This requires performing a number of adaptations. |
| constexpr size_t kReducedPixelsFirstStep = |
| static_cast<size_t>((kBalancedHighResolutionPixels * 3) / 5); |
| constexpr size_t kReducedPixelsSecondStep = (kReducedPixelsFirstStep * 3) / 5; |
| constexpr size_t kReducedPixelsThirdStep = (kReducedPixelsSecondStep * 3) / 5; |
| static_assert(kReducedPixelsFirstStep > kBalancedMediumResolutionPixels, |
| "The first pixel reduction is greater than the balanced " |
| "settings' medium pixel configuration"); |
| static_assert(kReducedPixelsSecondStep > kBalancedMediumResolutionPixels, |
| "The second pixel reduction is greater than the balanced " |
| "settings' medium pixel configuration"); |
| static_assert(kReducedPixelsThirdStep <= kBalancedMediumResolutionPixels, |
| "The third pixel reduction is NOT greater than the balanced " |
| "settings' medium pixel configuration"); |
| // The first adaptation should affect the frame rate: See |
| // Balanced_DecreaseResolution for explanation why. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(static_cast<double>(kBalancedHighFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| // The next three adaptations affects the resolution, because we have to reach |
| // kBalancedMediumResolutionPixels before a lower frame rate is considered by |
| // BalancedDegradationSettings. The number three is derived from the |
| // static_asserts above. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(kReducedPixelsFirstStep, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(kReducedPixelsSecondStep, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(kReducedPixelsThirdStep, |
| adapter_.source_restrictions().max_pixels_per_frame()); |
| // Thus, the next adaptation will reduce frame rate to |
| // kBalancedMediumFrameRateFps. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(static_cast<double>(kBalancedMediumFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(3, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(2, adapter_.adaptation_counters().fps_adaptations); |
| // Adapt up! |
| // While our resolution is in the medium-range, the frame rate associated with |
| // the next resolution configuration up ("high") is kBalancedHighFrameRateFps |
| // and "balanced" prefers adapting frame rate if not already applied. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(static_cast<double>(kBalancedHighFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(3, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| } |
| // Now that we have already achieved the next frame rate up, we act according |
| // to "maintain-framerate". We go back up in resolution. Due to rounding |
| // errors we don't end up back at kReducedPixelsSecondStep. Rather we get to |
| // kReducedPixelsSecondStepUp, which is off by one compared to |
| // kReducedPixelsSecondStep. |
| constexpr size_t kReducedPixelsSecondStepUp = |
| (kReducedPixelsThirdStep * 5) / 3; |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(kReducedPixelsSecondStepUp, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(2, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| } |
| // Now that our resolution is back in the high-range, the next frame rate to |
| // try out is "unlimited". |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(std::nullopt, adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(2, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(0, adapter_.adaptation_counters().fps_adaptations); |
| } |
| // Now only adapting resolution remains. |
| constexpr size_t kReducedPixelsFirstStepUp = |
| (kReducedPixelsSecondStepUp * 5) / 3; |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(kReducedPixelsFirstStepUp, |
| adapter_.source_restrictions().target_pixels_per_frame()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations); |
| EXPECT_EQ(0, adapter_.adaptation_counters().fps_adaptations); |
| } |
| // The last step up should make us entirely unrestricted. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(0, adapter_.adaptation_counters().Total()); |
| } |
| } |
| |
| TEST_F(VideoStreamAdapterTest, Balanced_LimitReached) { |
| adapter_.SetDegradationPreference(DegradationPreference::BALANCED); |
| FakeVideoStream fake_stream( |
| &adapter_, &input_state_provider_, kBalancedLowResolutionPixels, |
| kBalancedLowFrameRateFps, kDefaultMinPixelsPerFrame); |
| // Attempting to adapt up while unrestricted should result in kLimitReached. |
| EXPECT_EQ(Adaptation::Status::kLimitReached, |
| adapter_.GetAdaptationUp().status()); |
| // Adapting down once result in restricted frame rate, in this case we reach |
| // the lowest possible frame rate immediately: kBalancedLowFrameRateFps. |
| EXPECT_CALL(encoder_stats_observer_, OnMinPixelLimitReached()).Times(2); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(static_cast<double>(kBalancedLowFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| // Any further adaptation must follow "maintain-framerate" rules (these are |
| // covered in more depth by the MaintainFramerate tests). This test does not |
| // assert exactly how resolution is adjusted, only that resolution always |
| // decreases and that we eventually reach kLimitReached. |
| size_t previous_resolution = kBalancedLowResolutionPixels; |
| bool did_reach_limit = false; |
| // If we have not reached the limit within 5 adaptations something is wrong... |
| for (int i = 0; i < 5; i++) { |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| if (adaptation.status() == Adaptation::Status::kLimitReached) { |
| did_reach_limit = true; |
| break; |
| } |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_LT(adapter_.source_restrictions().max_pixels_per_frame().value(), |
| previous_resolution); |
| previous_resolution = |
| adapter_.source_restrictions().max_pixels_per_frame().value(); |
| } |
| EXPECT_TRUE(did_reach_limit); |
| // Frame rate restrictions are the same as before. |
| EXPECT_EQ(static_cast<double>(kBalancedLowFrameRateFps), |
| adapter_.source_restrictions().max_frame_rate()); |
| EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations); |
| } |
| |
| // kAwaitingPreviousAdaptation is only supported in "maintain-framerate". |
| TEST_F(VideoStreamAdapterTest, |
| MaintainFramerate_AwaitingPreviousAdaptationDown) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| input_state_provider_.SetInputState(1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Adapt down once, but don't update the input. |
| adapter_.ApplyAdaptation(adapter_.GetAdaptationDown(), nullptr); |
| EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations); |
| { |
| // Having performed the adaptation, but not updated the input based on the |
| // new restrictions, adapting again in the same direction will not work. |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kAwaitingPreviousAdaptation, |
| adaptation.status()); |
| } |
| } |
| |
| // kAwaitingPreviousAdaptation is only supported in "maintain-framerate". |
| TEST_F(VideoStreamAdapterTest, MaintainFramerate_AwaitingPreviousAdaptationUp) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Perform two adaptation down so that adapting up twice is possible. |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown()); |
| EXPECT_EQ(2, adapter_.adaptation_counters().resolution_adaptations); |
| // Adapt up once, but don't update the input. |
| adapter_.ApplyAdaptation(adapter_.GetAdaptationUp(), nullptr); |
| EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations); |
| { |
| // Having performed the adaptation, but not updated the input based on the |
| // new restrictions, adapting again in the same direction will not work. |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kAwaitingPreviousAdaptation, |
| adaptation.status()); |
| } |
| } |
| |
| TEST_F(VideoStreamAdapterTest, |
| MaintainResolution_AdaptsUpAfterSwitchingDegradationPreference) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 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_F(VideoStreamAdapterTest, |
| MaintainFramerate_AdaptsUpAfterSwitchingDegradationPreference) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 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_F(VideoStreamAdapterTest, |
| PendingResolutionIncreaseAllowsAdaptUpAfterSwitchToMaintainResolution) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 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(), nullptr); |
| 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_F(VideoStreamAdapterTest, |
| MaintainFramerate_AdaptsDownAfterSwitchingDegradationPreference) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 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_F(VideoStreamAdapterTest, |
| MaintainResolution_AdaptsDownAfterSwitchingDegradationPreference) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 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_F( |
| VideoStreamAdapterTest, |
| PendingResolutionDecreaseAllowsAdaptDownAfterSwitchToMaintainResolution) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Apply adaptation but don't update the input. |
| adapter_.ApplyAdaptation(adapter_.GetAdaptationDown(), nullptr); |
| 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_F(VideoStreamAdapterTest, RestrictionBroadcasted) { |
| FakeVideoStreamAdapterListner listener; |
| adapter_.AddRestrictionsListener(&listener); |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Not broadcast on invalid ApplyAdaptation. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| adapter_.ApplyAdaptation(adaptation, nullptr); |
| EXPECT_EQ(0, listener.calls()); |
| } |
| |
| // Broadcast on ApplyAdaptation. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(1, listener.calls()); |
| EXPECT_EQ(adaptation.restrictions(), listener.last_restrictions()); |
| } |
| |
| // Broadcast on ClearRestrictions(). |
| adapter_.ClearRestrictions(); |
| EXPECT_EQ(2, listener.calls()); |
| EXPECT_EQ(VideoSourceRestrictions(), listener.last_restrictions()); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, AdaptationHasNextRestrcitions) { |
| // Any non-disabled DegradationPreference will do. |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // When adaptation is not possible. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kLimitReached, adaptation.status()); |
| EXPECT_EQ(adaptation.restrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(0, adaptation.counters().Total()); |
| } |
| // When we adapt down. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationDown(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(adaptation.restrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(adaptation.counters(), adapter_.adaptation_counters()); |
| } |
| // When we adapt up. |
| { |
| Adaptation adaptation = adapter_.GetAdaptationUp(); |
| EXPECT_EQ(Adaptation::Status::kValid, adaptation.status()); |
| fake_stream.ApplyAdaptation(adaptation); |
| EXPECT_EQ(adaptation.restrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(adaptation.counters(), adapter_.adaptation_counters()); |
| } |
| } |
| |
| TEST_F(VideoStreamAdapterTest, |
| SetDegradationPreferenceToOrFromBalancedClearsRestrictions) { |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| input_state_provider_.SetInputState(1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| adapter_.ApplyAdaptation(adapter_.GetAdaptationDown(), nullptr); |
| EXPECT_NE(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| EXPECT_NE(0, adapter_.adaptation_counters().Total()); |
| // Changing from non-balanced to balanced clears the restrictions. |
| adapter_.SetDegradationPreference(DegradationPreference::BALANCED); |
| EXPECT_EQ(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| EXPECT_EQ(0, adapter_.adaptation_counters().Total()); |
| // Apply adaptation again. |
| adapter_.ApplyAdaptation(adapter_.GetAdaptationDown(), nullptr); |
| EXPECT_NE(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| EXPECT_NE(0, adapter_.adaptation_counters().Total()); |
| // Changing from balanced to non-balanced clears the restrictions. |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| EXPECT_EQ(VideoSourceRestrictions(), adapter_.source_restrictions()); |
| 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, |
| GetAdaptDownResolutionReturnsWithStatusInDisabledAndMaintainResolution) { |
| adapter_.SetDegradationPreference(DegradationPreference::DISABLED); |
| input_state_provider_.SetInputState(1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| EXPECT_EQ(Adaptation::Status::kAdaptationDisabled, |
| adapter_.GetAdaptDownResolution().status()); |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| 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); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, |
| AdaptationDisabledStatusAlwaysWhenDegradationPreferenceDisabled) { |
| adapter_.SetDegradationPreference(DegradationPreference::DISABLED); |
| input_state_provider_.SetInputState(1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| EXPECT_EQ(Adaptation::Status::kAdaptationDisabled, |
| adapter_.GetAdaptationDown().status()); |
| EXPECT_EQ(Adaptation::Status::kAdaptationDisabled, |
| adapter_.GetAdaptationUp().status()); |
| EXPECT_EQ(Adaptation::Status::kAdaptationDisabled, |
| adapter_.GetAdaptDownResolution().status()); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, AdaptationConstraintAllowsAdaptationsUp) { |
| testing::StrictMock<MockAdaptationConstraint> adaptation_constraint; |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| adapter_.AddAdaptationConstraint(&adaptation_constraint); |
| input_state_provider_.SetInputState(1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Adapt down once so we can adapt up later. |
| auto first_adaptation = adapter_.GetAdaptationDown(); |
| fake_stream.ApplyAdaptation(first_adaptation); |
| |
| EXPECT_CALL(adaptation_constraint, |
| IsAdaptationUpAllowed(_, first_adaptation.restrictions(), _)) |
| .WillOnce(Return(true)); |
| EXPECT_EQ(Adaptation::Status::kValid, adapter_.GetAdaptationUp().status()); |
| adapter_.RemoveAdaptationConstraint(&adaptation_constraint); |
| } |
| |
| TEST_F(VideoStreamAdapterTest, AdaptationConstraintDisallowsAdaptationsUp) { |
| testing::StrictMock<MockAdaptationConstraint> adaptation_constraint; |
| adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| adapter_.AddAdaptationConstraint(&adaptation_constraint); |
| input_state_provider_.SetInputState(1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30, |
| kDefaultMinPixelsPerFrame); |
| // Adapt down once so we can adapt up later. |
| auto first_adaptation = adapter_.GetAdaptationDown(); |
| fake_stream.ApplyAdaptation(first_adaptation); |
| |
| EXPECT_CALL(adaptation_constraint, |
| IsAdaptationUpAllowed(_, first_adaptation.restrictions(), _)) |
| .WillOnce(Return(false)); |
| EXPECT_EQ(Adaptation::Status::kRejectedByConstraint, |
| adapter_.GetAdaptationUp().status()); |
| adapter_.RemoveAdaptationConstraint(&adaptation_constraint); |
| } |
| |
| // Death tests. |
| // Disabled on Android because death tests misbehave on Android, see |
| // base/test/gtest_util.h. |
| #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) |
| |
| TEST(VideoStreamAdapterDeathTest, |
| SetDegradationPreferenceInvalidatesAdaptations) { |
| webrtc::test::ScopedKeyValueConfig field_trials; |
| FakeVideoStreamInputStateProvider input_state_provider; |
| testing::StrictMock<MockVideoStreamEncoderObserver> encoder_stats_observer_; |
| VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_, |
| field_trials); |
| adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); |
| input_state_provider.SetInputState(1280 * 720, 30, kDefaultMinPixelsPerFrame); |
| Adaptation adaptation = adapter.GetAdaptationDown(); |
| adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| EXPECT_DEATH(adapter.ApplyAdaptation(adaptation, nullptr), ""); |
| } |
| |
| TEST(VideoStreamAdapterDeathTest, AdaptDownInvalidatesAdaptations) { |
| webrtc::test::ScopedKeyValueConfig field_trials; |
| FakeVideoStreamInputStateProvider input_state_provider; |
| testing::StrictMock<MockVideoStreamEncoderObserver> encoder_stats_observer_; |
| VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_, |
| field_trials); |
| adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); |
| input_state_provider.SetInputState(1280 * 720, 30, kDefaultMinPixelsPerFrame); |
| Adaptation adaptation = adapter.GetAdaptationDown(); |
| adapter.GetAdaptationDown(); |
| EXPECT_DEATH(adapter.ApplyAdaptation(adaptation, nullptr), ""); |
| } |
| |
| #endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) |
| |
| } // namespace webrtc |