You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1001 lines
47 KiB
1001 lines
47 KiB
/*
|
|
* 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 <string>
|
|
#include <utility>
|
|
|
|
#include "absl/types/optional.h"
|
|
#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 "api/video_codecs/video_encoder_config.h"
|
|
#include "call/adaptation/adaptation_constraint.h"
|
|
#include "call/adaptation/adaptation_listener.h"
|
|
#include "call/adaptation/encoder_settings.h"
|
|
#include "call/adaptation/test/fake_adaptation_listener.h"
|
|
#include "call/adaptation/test/fake_resource.h"
|
|
#include "call/adaptation/video_source_restrictions.h"
|
|
#include "call/adaptation/video_stream_input_state.h"
|
|
#include "rtc_base/string_encode.h"
|
|
#include "test/field_trial.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
#include "test/testsupport/rtc_expect_death.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) + "/";
|
|
}
|
|
|
|
class FakeVideoStreamInputStateProvider : public VideoStreamInputStateProvider {
|
|
public:
|
|
FakeVideoStreamInputStateProvider()
|
|
: VideoStreamInputStateProvider(nullptr) {}
|
|
virtual ~FakeVideoStreamInputStateProvider() = default;
|
|
|
|
void SetInputState(int input_pixels,
|
|
int input_fps,
|
|
int min_pixels_per_frame) {
|
|
VideoStreamInputState input_state;
|
|
input_state.set_has_input(true);
|
|
input_state.set_frame_size_pixels(input_pixels);
|
|
input_state.set_frames_per_second(input_fps);
|
|
input_state.set_min_pixels_per_frame(min_pixels_per_frame);
|
|
fake_input_state_ = input_state;
|
|
}
|
|
VideoStreamInputState InputState() override { return fake_input_state_; }
|
|
|
|
private:
|
|
VideoStreamInputState fake_input_state_;
|
|
};
|
|
|
|
// 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) {
|
|
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 MockAdaptationListener : public AdaptationListener {
|
|
public:
|
|
MOCK_METHOD(void,
|
|
OnAdaptationApplied,
|
|
(const VideoStreamInputState& input_state,
|
|
const VideoSourceRestrictions& restrictions_before,
|
|
const VideoSourceRestrictions& restrictions_after,
|
|
rtc::scoped_refptr<Resource> reason_resource),
|
|
(override));
|
|
};
|
|
|
|
class MockAdaptationConstraint : public AdaptationConstraint {
|
|
public:
|
|
MOCK_METHOD(bool,
|
|
IsAdaptationUpAllowed,
|
|
(const VideoStreamInputState& input_state,
|
|
const VideoSourceRestrictions& restrictions_before,
|
|
const VideoSourceRestrictions& restrictions_after,
|
|
rtc::scoped_refptr<Resource> reason_resource),
|
|
(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()),
|
|
input_state_provider_(),
|
|
resource_(FakeResource::Create("FakeResource")),
|
|
adapter_(&input_state_provider_) {}
|
|
|
|
protected:
|
|
webrtc::test::ScopedFieldTrials field_trials_;
|
|
FakeVideoStreamInputStateProvider input_state_provider_;
|
|
rtc::scoped_refptr<Resource> resource_;
|
|
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());
|
|
EXPECT_FALSE(adaptation.min_pixel_limit_reached());
|
|
adapter_.ApplyAdaptation(adaptation, nullptr);
|
|
EXPECT_EQ(static_cast<size_t>((kInputPixels * 3) / 5),
|
|
adapter_.source_restrictions().max_pixels_per_frame());
|
|
EXPECT_EQ(absl::nullopt,
|
|
adapter_.source_restrictions().target_pixels_per_frame());
|
|
EXPECT_EQ(absl::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);
|
|
// 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());
|
|
EXPECT_TRUE(adaptation.min_pixel_limit_reached());
|
|
}
|
|
|
|
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(resource_));
|
|
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(absl::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(resource_).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(resource_));
|
|
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(absl::nullopt,
|
|
adapter_.source_restrictions().max_pixels_per_frame());
|
|
EXPECT_EQ(absl::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(resource_);
|
|
EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
|
|
fake_stream.ApplyAdaptation(adaptation);
|
|
EXPECT_EQ(absl::nullopt,
|
|
adapter_.source_restrictions().max_pixels_per_frame());
|
|
EXPECT_EQ(absl::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(resource_).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(resource_));
|
|
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(absl::nullopt,
|
|
adapter_.source_restrictions().max_pixels_per_frame());
|
|
EXPECT_EQ(absl::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(absl::nullopt,
|
|
adapter_.source_restrictions().max_pixels_per_frame());
|
|
EXPECT_EQ(absl::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(absl::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(absl::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(resource_);
|
|
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(resource_);
|
|
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(resource_);
|
|
EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
|
|
fake_stream.ApplyAdaptation(adaptation);
|
|
EXPECT_EQ(absl::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(resource_);
|
|
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(resource_);
|
|
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(resource_).status());
|
|
// Adapting down once result in restricted frame rate, in this case we reach
|
|
// the lowest possible frame rate immediately: kBalancedLowFrameRateFps.
|
|
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(resource_), 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(resource_);
|
|
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(resource_));
|
|
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(resource_);
|
|
EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
|
|
fake_stream.ApplyAdaptation(adapter_.GetAdaptationUp(resource_));
|
|
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(resource_));
|
|
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(resource_);
|
|
EXPECT_EQ(Adaptation::Status::kValid, adaptation.status());
|
|
fake_stream.ApplyAdaptation(adapter_.GetAdaptationUp(resource_));
|
|
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(resource_), nullptr);
|
|
EXPECT_EQ(Adaptation::Status::kAwaitingPreviousAdaptation,
|
|
adapter_.GetAdaptationUp(resource_).status());
|
|
|
|
adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION);
|
|
Adaptation adaptation = adapter_.GetAdaptationUp(resource_);
|
|
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(resource_);
|
|
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(resource_);
|
|
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(resource_);
|
|
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(resource_).status());
|
|
EXPECT_EQ(Adaptation::Status::kAdaptationDisabled,
|
|
adapter_.GetAdaptDownResolution().status());
|
|
}
|
|
|
|
TEST_F(VideoStreamAdapterTest, AdaptationListenerReceivesSignalOnAdaptation) {
|
|
testing::StrictMock<MockAdaptationListener> adaptation_listener;
|
|
adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
|
|
adapter_.AddAdaptationListener(&adaptation_listener);
|
|
input_state_provider_.SetInputState(1280 * 720, 30,
|
|
kDefaultMinPixelsPerFrame);
|
|
VideoSourceRestrictions restrictions_before;
|
|
VideoSourceRestrictions restrictions_after;
|
|
EXPECT_CALL(adaptation_listener, OnAdaptationApplied)
|
|
.WillOnce(DoAll(SaveArg<1>(&restrictions_before),
|
|
SaveArg<2>(&restrictions_after)));
|
|
auto adaptation = adapter_.GetAdaptationDown();
|
|
adapter_.ApplyAdaptation(adaptation, nullptr);
|
|
EXPECT_EQ(VideoSourceRestrictions(), restrictions_before);
|
|
EXPECT_EQ(adaptation.restrictions(), restrictions_after);
|
|
|
|
// Clean up.
|
|
adapter_.RemoveAdaptationListener(&adaptation_listener);
|
|
}
|
|
|
|
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(), _, resource_))
|
|
.WillOnce(Return(true));
|
|
EXPECT_EQ(Adaptation::Status::kValid,
|
|
adapter_.GetAdaptationUp(resource_).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(), _, resource_))
|
|
.WillOnce(Return(false));
|
|
EXPECT_EQ(Adaptation::Status::kRejectedByConstraint,
|
|
adapter_.GetAdaptationUp(resource_).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) {
|
|
FakeVideoStreamInputStateProvider input_state_provider;
|
|
VideoStreamAdapter adapter(&input_state_provider);
|
|
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) {
|
|
FakeVideoStreamInputStateProvider input_state_provider;
|
|
VideoStreamAdapter adapter(&input_state_provider);
|
|
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
|