* 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 "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) + "/";
}
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_; }
void ApplyAdaptation(Adaptation adaptation) {
adapter_->ApplyAdaptation(adaptation, nullptr);
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));
std::string Name() const override { return "MockAdaptationConstraint"; }
};
}
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(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);
EXPECT_CALL(encoder_stats_observer_, OnMinPixelLimitReached());
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);
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();
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(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);
EXPECT_EQ(Adaptation::Status::kLimitReached,
adapter_.GetAdaptationUp().status());
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(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);
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);
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);
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();
Adaptation adaptation = adapter_.GetAdaptationUp();
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);
EXPECT_EQ(Adaptation::Status::kLimitReached,
adapter_.GetAdaptationUp().status());
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);
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);
{
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);
{
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);
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);
}
TEST_F(VideoStreamAdapterTest, Balanced_IncreaseFrameRateAndResolution) {
adapter_.SetDegradationPreference(DegradationPreference::BALANCED);
FakeVideoStream fake_stream(
&adapter_, &input_state_provider_, kBalancedHighResolutionPixels,
kBalancedHighFrameRateFps, kDefaultMinPixelsPerFrame);
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");
fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown());
EXPECT_EQ(static_cast<double>(kBalancedHighFrameRateFps),
adapter_.source_restrictions().max_frame_rate());
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());
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);
{
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);
}
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);
}
{
Adaptation adaptation = adapter_.GetAdaptationUp();
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);
}
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);
}
{
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);
EXPECT_EQ(Adaptation::Status::kLimitReached,
adapter_.GetAdaptationUp().status());
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);
size_t previous_resolution = kBalancedLowResolutionPixels;
bool did_reach_limit = false;
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);
EXPECT_EQ(static_cast<double>(kBalancedLowFrameRateFps),
adapter_.source_restrictions().max_frame_rate());
EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations);
}
TEST_F(VideoStreamAdapterTest,
MaintainFramerate_AwaitingPreviousAdaptationDown) {
adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
input_state_provider_.SetInputState(1280 * 720, 30,
kDefaultMinPixelsPerFrame);
adapter_.ApplyAdaptation(adapter_.GetAdaptationDown(), nullptr);
EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations);
{
Adaptation adaptation = adapter_.GetAdaptationDown();
EXPECT_EQ(Adaptation::Status::kAwaitingPreviousAdaptation,
adaptation.status());
}
}
TEST_F(VideoStreamAdapterTest, MaintainFramerate_AwaitingPreviousAdaptationUp) {
adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30,
kDefaultMinPixelsPerFrame);
fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown());
fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown());
EXPECT_EQ(2, adapter_.adaptation_counters().resolution_adaptations);
adapter_.ApplyAdaptation(adapter_.GetAdaptationUp(), nullptr);
EXPECT_EQ(1, adapter_.adaptation_counters().resolution_adaptations);
{
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);
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);
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);
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);
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);
fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown());
adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown());
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);
fake_stream.ApplyAdaptation(adapter_.GetAdaptationDown());
EXPECT_EQ(1, adapter_.adaptation_counters().fps_adaptations);
adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
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);
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);
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);
{
Adaptation adaptation = adapter_.GetAdaptationUp();
adapter_.ApplyAdaptation(adaptation, nullptr);
EXPECT_EQ(0, listener.calls());
}
{
Adaptation adaptation = adapter_.GetAdaptationDown();
fake_stream.ApplyAdaptation(adaptation);
EXPECT_EQ(1, listener.calls());
EXPECT_EQ(adaptation.restrictions(), listener.last_restrictions());
}
adapter_.ClearRestrictions();
EXPECT_EQ(2, listener.calls());
EXPECT_EQ(VideoSourceRestrictions(), listener.last_restrictions());
}
TEST_F(VideoStreamAdapterTest, AdaptationHasNextRestrcitions) {
adapter_.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE);
FakeVideoStream fake_stream(&adapter_, &input_state_provider_, 1280 * 720, 30,
kDefaultMinPixelsPerFrame);
{
Adaptation adaptation = adapter_.GetAdaptationUp();
EXPECT_EQ(Adaptation::Status::kLimitReached, adaptation.status());
EXPECT_EQ(adaptation.restrictions(), adapter_.source_restrictions());
EXPECT_EQ(0, adaptation.counters().Total());
}
{
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());
}
{
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());
adapter_.SetDegradationPreference(DegradationPreference::BALANCED);
EXPECT_EQ(VideoSourceRestrictions(), adapter_.source_restrictions());
EXPECT_EQ(0, adapter_.adaptation_counters().Total());
adapter_.ApplyAdaptation(adapter_.GetAdaptationDown(), nullptr);
EXPECT_NE(VideoSourceRestrictions(), adapter_.source_restrictions());
EXPECT_NE(0, adapter_.adaptation_counters().Total());
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) {
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) {
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) {
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);
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);
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);
}
#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
}