Files
platform-external-webrtc/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc
kthelgason 876222f77d Move usage of QualityScaler to ViEEncoder.
This brings QualityScaler much more in line with OveruseFrameDetector.
The two classes are conceptually similar, and should be used in the
same way. The biggest changes in this CL are:
- Quality scaling is now only done in ViEEncoder and not in each
  encoder implementation separately.
- QualityScaler now checks the average QP asynchronously, instead of
  having to be polled on each frame.
- QualityScaler is no longer responsible for actually scaling the frames,
  but has a callback to ViEEncoder that it uses to express it's desire
  for lower resolution.

BUG=webrtc:6495

Review-Url: https://codereview.webrtc.org/2398963003
Cr-Commit-Position: refs/heads/master@{#15286}
2016-11-29 09:44:22 +00:00

178 lines
5.0 KiB
C++

/*
* Copyright (c) 2014 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 "webrtc/modules/video_coding/utility/quality_scaler.h"
#include <memory>
#include "webrtc/base/event.h"
#include "webrtc/base/task_queue.h"
#include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h"
namespace webrtc {
namespace {
static const int kFramerate = 30;
static const int kLowQp = 15;
static const int kLowQpThreshold = 18;
static const int kHighQp = 40;
static const size_t kDefaultTimeoutMs = 1000;
} // namespace
class MockScaleObserver : public ScalingObserverInterface {
public:
MockScaleObserver() : event(false, false) {}
virtual ~MockScaleObserver() {}
void ScaleUp(ScaleReason r) override {
scaled_up++;
event.Set();
}
void ScaleDown(ScaleReason r) override {
scaled_down++;
event.Set();
}
rtc::Event event;
int scaled_up = 0;
int scaled_down = 0;
};
// Pass a lower sampling period to speed up the tests.
class QualityScalerUnderTest : public QualityScaler {
public:
explicit QualityScalerUnderTest(ScalingObserverInterface* observer,
VideoEncoder::QpThresholds thresholds)
: QualityScaler(observer, thresholds, 5) {}
};
class QualityScalerTest : public ::testing::Test {
protected:
enum ScaleDirection {
kKeepScaleAtHighQp,
kScaleDown,
kScaleDownAboveHighQp,
kScaleUp
};
QualityScalerTest()
: q_(new rtc::TaskQueue("QualityScalerTestQueue")),
observer_(new MockScaleObserver()) {
rtc::Event event(false, false);
q_->PostTask([this, &event] {
qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
observer_.get(),
VideoEncoder::QpThresholds(kLowQpThreshold, kHighQp)));
event.Set();
});
EXPECT_TRUE(event.Wait(kDefaultTimeoutMs));
}
~QualityScalerTest() {
rtc::Event event(false, false);
q_->PostTask([this, &event] {
qs_.reset(nullptr);
event.Set();
});
EXPECT_TRUE(event.Wait(kDefaultTimeoutMs));
}
void TriggerScale(ScaleDirection scale_direction) {
for (int i = 0; i < kFramerate * 5; ++i) {
switch (scale_direction) {
case kScaleUp:
qs_->ReportQP(kLowQp);
break;
case kScaleDown:
qs_->ReportDroppedFrame();
break;
case kKeepScaleAtHighQp:
qs_->ReportQP(kHighQp);
break;
case kScaleDownAboveHighQp:
qs_->ReportQP(kHighQp + 1);
break;
}
}
}
std::unique_ptr<rtc::TaskQueue> q_;
std::unique_ptr<QualityScaler> qs_;
std::unique_ptr<MockScaleObserver> observer_;
};
TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
q_->PostTask([this] { TriggerScale(kScaleDown); });
EXPECT_TRUE(observer_->event.Wait(50));
EXPECT_EQ(1, observer_->scaled_down);
}
TEST_F(QualityScalerTest, KeepsScaleAtHighQp) {
q_->PostTask([this] { TriggerScale(kKeepScaleAtHighQp); });
EXPECT_FALSE(observer_->event.Wait(50));
EXPECT_EQ(0, observer_->scaled_down);
EXPECT_EQ(0, observer_->scaled_up);
}
TEST_F(QualityScalerTest, DownscalesAboveHighQp) {
q_->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); });
EXPECT_TRUE(observer_->event.Wait(50));
EXPECT_EQ(1, observer_->scaled_down);
EXPECT_EQ(0, observer_->scaled_up);
}
TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
q_->PostTask([this] {
qs_->ReportDroppedFrame();
qs_->ReportDroppedFrame();
qs_->ReportQP(kHighQp);
});
EXPECT_TRUE(observer_->event.Wait(50));
EXPECT_EQ(1, observer_->scaled_down);
EXPECT_EQ(0, observer_->scaled_up);
}
TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) {
q_->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); });
EXPECT_TRUE(observer_->event.Wait(50));
EXPECT_EQ(1, observer_->scaled_down);
EXPECT_EQ(0, observer_->scaled_up);
}
TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
q_->PostTask([this] {
qs_->ReportDroppedFrame();
qs_->ReportQP(kHighQp);
});
EXPECT_FALSE(observer_->event.Wait(50));
EXPECT_EQ(0, observer_->scaled_down);
EXPECT_EQ(0, observer_->scaled_up);
}
TEST_F(QualityScalerTest, UpscalesAfterLowQp) {
q_->PostTask([this] { TriggerScale(kScaleUp); });
EXPECT_TRUE(observer_->event.Wait(50));
EXPECT_EQ(0, observer_->scaled_down);
EXPECT_EQ(1, observer_->scaled_up);
}
TEST_F(QualityScalerTest, ScalesDownAndBackUp) {
q_->PostTask([this] { TriggerScale(kScaleDown); });
EXPECT_TRUE(observer_->event.Wait(50));
EXPECT_EQ(1, observer_->scaled_down);
EXPECT_EQ(0, observer_->scaled_up);
q_->PostTask([this] { TriggerScale(kScaleUp); });
EXPECT_TRUE(observer_->event.Wait(50));
EXPECT_EQ(1, observer_->scaled_down);
EXPECT_EQ(1, observer_->scaled_up);
}
} // namespace webrtc