[Adaptation] Move Balanced MinFpsDiff logic to VideoStreamAdapter

This way can double adapt right away instead of relying
on the qp scaler checking soon into the future.

Bug: webrtc:11830
Change-Id: I8e878168303cf6a4c3edcf3997dd8ac2413a4479
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/181060
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@google.com>
Cr-Commit-Position: refs/heads/master@{#31895}
This commit is contained in:
Evan Shrubsole
2020-08-10 11:01:06 +02:00
committed by Commit Bot
parent 8e95ea92b2
commit a1c77f6d0d
13 changed files with 128 additions and 485 deletions

View File

@ -86,7 +86,6 @@ class QualityScaler::CheckQpTask {
struct Result {
bool observed_enough_frames = false;
bool qp_usage_reported = false;
bool clear_qp_samples = false;
};
CheckQpTask(QualityScaler* quality_scaler, Result previous_task_result)
@ -110,49 +109,36 @@ class QualityScaler::CheckQpTask {
case QualityScaler::CheckQpResult::kInsufficientSamples: {
result_.observed_enough_frames = false;
// After this line, |this| may be deleted.
DoCompleteTask();
return;
break;
}
case QualityScaler::CheckQpResult::kNormalQp: {
result_.observed_enough_frames = true;
// After this line, |this| may be deleted.
DoCompleteTask();
return;
break;
}
case QualityScaler::CheckQpResult::kHighQp: {
result_.observed_enough_frames = true;
result_.qp_usage_reported = true;
state_ = State::kAwaitingQpUsageHandled;
rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface>
callback = ConstructCallback();
quality_scaler_->fast_rampup_ = false;
// After this line, |this| may be deleted.
quality_scaler_->handler_->OnReportQpUsageHigh(callback);
return;
quality_scaler_->handler_->OnReportQpUsageHigh();
quality_scaler_->ClearSamples();
break;
}
case QualityScaler::CheckQpResult::kLowQp: {
result_.observed_enough_frames = true;
result_.qp_usage_reported = true;
state_ = State::kAwaitingQpUsageHandled;
rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface>
callback = ConstructCallback();
// After this line, |this| may be deleted.
quality_scaler_->handler_->OnReportQpUsageLow(callback);
return;
quality_scaler_->handler_->OnReportQpUsageLow();
quality_scaler_->ClearSamples();
break;
}
}
state_ = State::kCompleted;
// Starting the next task deletes the pending task. After this line,
// |this| has been deleted.
quality_scaler_->StartNextCheckQpTask();
}),
GetCheckingQpDelayMs());
}
void OnQpUsageHandled(bool clear_qp_samples) {
RTC_DCHECK_EQ(state_, State::kAwaitingQpUsageHandled);
result_.clear_qp_samples = clear_qp_samples;
if (clear_qp_samples)
quality_scaler_->ClearSamples();
DoCompleteTask();
}
bool HasCompletedTask() const { return state_ == State::kCompleted; }
Result result() const {
@ -164,15 +150,9 @@ class QualityScaler::CheckQpTask {
enum class State {
kNotStarted,
kCheckingQp,
kAwaitingQpUsageHandled,
kCompleted,
};
// Defined after the definition of QualityScaler::CheckQpTaskHandlerCallback.
// Gets around a forward declaration issue.
rtc::scoped_refptr<QualityScaler::CheckQpTaskHandlerCallback>
ConstructCallback();
// Determines the sampling period of CheckQpTasks.
int64_t GetCheckingQpDelayMs() const {
RTC_DCHECK_RUN_ON(&quality_scaler_->task_checker_);
@ -184,10 +164,6 @@ class QualityScaler::CheckQpTask {
// Use half the interval while waiting for enough frames.
return quality_scaler_->sampling_period_ms_ / 2;
}
if (!previous_task_result_.clear_qp_samples) {
// Check shortly again.
return quality_scaler_->sampling_period_ms_ / 8;
}
if (quality_scaler_->scale_factor_ &&
!previous_task_result_.qp_usage_reported) {
// Last CheckQp did not call AdaptDown/Up, possibly reduce interval.
@ -198,15 +174,6 @@ class QualityScaler::CheckQpTask {
quality_scaler_->initial_scale_factor_;
}
void DoCompleteTask() {
RTC_DCHECK(state_ == State::kCheckingQp ||
state_ == State::kAwaitingQpUsageHandled);
state_ = State::kCompleted;
// Starting the next task deletes the pending task. After this line, |this|
// has been deleted.
quality_scaler_->StartNextCheckQpTask();
}
QualityScaler* const quality_scaler_;
State state_;
const Result previous_task_result_;
@ -215,39 +182,6 @@ class QualityScaler::CheckQpTask {
rtc::WeakPtrFactory<CheckQpTask> weak_ptr_factory_;
};
class QualityScaler::CheckQpTaskHandlerCallback
: public QualityScalerQpUsageHandlerCallbackInterface {
public:
CheckQpTaskHandlerCallback(
rtc::WeakPtr<QualityScaler::CheckQpTask> check_qp_task)
: QualityScalerQpUsageHandlerCallbackInterface(),
check_qp_task_(std::move(check_qp_task)),
was_handled_(false) {}
~CheckQpTaskHandlerCallback() { RTC_DCHECK(was_handled_); }
void OnQpUsageHandled(bool clear_qp_samples) {
RTC_DCHECK(!was_handled_);
was_handled_ = true;
if (!check_qp_task_) {
// The task has been cancelled through destruction; the result of the
// operation is ignored.
return;
}
check_qp_task_->OnQpUsageHandled(clear_qp_samples);
}
private:
// The callback may outlive the QualityScaler and its task.
rtc::WeakPtr<QualityScaler::CheckQpTask> const check_qp_task_;
bool was_handled_;
};
rtc::scoped_refptr<QualityScaler::CheckQpTaskHandlerCallback>
QualityScaler::CheckQpTask::ConstructCallback() {
return new CheckQpTaskHandlerCallback(weak_ptr_factory_.GetWeakPtr());
}
QualityScaler::QualityScaler(QualityScalerQpUsageHandlerInterface* handler,
VideoEncoder::QpThresholds thresholds)
: QualityScaler(handler, thresholds, kMeasureMs) {}
@ -401,10 +335,4 @@ void QualityScaler::ClearSamples() {
QualityScalerQpUsageHandlerInterface::~QualityScalerQpUsageHandlerInterface() {}
QualityScalerQpUsageHandlerCallbackInterface::
QualityScalerQpUsageHandlerCallbackInterface() {}
QualityScalerQpUsageHandlerCallbackInterface::
~QualityScalerQpUsageHandlerCallbackInterface() {}
} // namespace webrtc

View File

@ -112,38 +112,8 @@ class QualityScalerQpUsageHandlerInterface {
public:
virtual ~QualityScalerQpUsageHandlerInterface();
// Reacts to QP usage being too high or too low. The |callback| MUST be
// invoked when the handler is done, allowing the QualityScaler to resume
// checking for QP.
virtual void OnReportQpUsageHigh(
rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface>
callback) = 0;
virtual void OnReportQpUsageLow(
rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface>
callback) = 0;
};
// When QP is reported as high or low by the QualityScaler, it pauses checking
// for QP until the QP usage has been handled. When OnQpUsageHandled() is
// invoked, the QualityScaler resumes checking for QP. This ensures that if the
// stream is reconfigured in response to QP usage we do not include QP samples
// from before the reconfiguration the next time we check for QP.
//
// OnQpUsageHandled() MUST be invoked exactly once before this object is
// destroyed.
class QualityScalerQpUsageHandlerCallbackInterface
: public rtc::RefCountedObject<rtc::RefCountInterface> {
public:
virtual ~QualityScalerQpUsageHandlerCallbackInterface();
// If |clear_qp_samples| is true, existing QP samples are cleared before the
// next time QualityScaler checks for QP. This is usually a good idea when the
// stream is reconfigured. If |clear_qp_samples| is false, samples are not
// cleared and QualityScaler increases its frequency of checking for QP.
virtual void OnQpUsageHandled(bool clear_qp_samples) = 0;
protected:
QualityScalerQpUsageHandlerCallbackInterface();
virtual void OnReportQpUsageHigh() = 0;
virtual void OnReportQpUsageLow() = 0;
};
} // namespace webrtc

View File

@ -28,37 +28,24 @@ static const int kMinFramesNeededToScale = 60; // From quality_scaler.cc.
static const size_t kDefaultTimeoutMs = 150;
} // namespace
class MockQpUsageHandler : public QualityScalerQpUsageHandlerInterface {
class FakeQpUsageHandler : public QualityScalerQpUsageHandlerInterface {
public:
virtual ~MockQpUsageHandler() {}
~FakeQpUsageHandler() override = default;
// QualityScalerQpUsageHandlerInterface implementation.
void OnReportQpUsageHigh(
rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback)
override {
callback_ = callback;
void OnReportQpUsageHigh() override {
adapt_down_events_++;
event.Set();
if (synchronously_invoke_callback)
callback_->OnQpUsageHandled(true);
}
void OnReportQpUsageLow(
rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback)
override {
callback_ = callback;
void OnReportQpUsageLow() override {
adapt_up_events_++;
event.Set();
if (synchronously_invoke_callback)
callback_->OnQpUsageHandled(true);
}
rtc::Event event;
int adapt_up_events_ = 0;
int adapt_down_events_ = 0;
bool synchronously_invoke_callback = true;
rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback_ =
nullptr;
};
// Pass a lower sampling period to speed up the tests.
@ -83,7 +70,7 @@ class QualityScalerTest : public ::testing::Test,
QualityScalerTest()
: scoped_field_trial_(GetParam()),
task_queue_("QualityScalerTestQueue"),
handler_(new MockQpUsageHandler()) {
handler_(std::make_unique<FakeQpUsageHandler>()) {
task_queue_.SendTask(
[this] {
qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
@ -92,7 +79,7 @@ class QualityScalerTest : public ::testing::Test,
RTC_FROM_HERE);
}
~QualityScalerTest() {
~QualityScalerTest() override {
task_queue_.SendTask([this] { qs_ = nullptr; }, RTC_FROM_HERE);
}
@ -121,7 +108,7 @@ class QualityScalerTest : public ::testing::Test,
test::ScopedFieldTrials scoped_field_trial_;
TaskQueueForTest task_queue_;
std::unique_ptr<QualityScaler> qs_;
std::unique_ptr<MockQpUsageHandler> handler_;
std::unique_ptr<FakeQpUsageHandler> handler_;
};
INSTANTIATE_TEST_SUITE_P(
@ -282,34 +269,4 @@ TEST_P(QualityScalerTest, ScalesDownAndBackUpWithMinFramesNeeded) {
EXPECT_EQ(1, handler_->adapt_up_events_);
}
TEST_P(QualityScalerTest, CheckingQpAgainRequiresResolvingCallback) {
handler_->synchronously_invoke_callback = false;
task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
EXPECT_EQ(1, handler_->adapt_down_events_);
// Without invoking the callback, another downscale should not happen.
handler_->event.Reset();
rtc::Event event;
task_queue_.SendTask(
[this, &event] {
TriggerScale(kScaleDown);
event.Set();
},
RTC_FROM_HERE);
EXPECT_TRUE(event.Wait(kDefaultTimeoutMs));
EXPECT_FALSE(handler_->event.Wait(0));
EXPECT_EQ(1, handler_->adapt_down_events_);
// Resume checking for QP again by invoking the callback.
task_queue_.SendTask(
[this] {
handler_->callback_->OnQpUsageHandled(true);
TriggerScale(kScaleDown);
},
RTC_FROM_HERE);
EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
EXPECT_EQ(2, handler_->adapt_down_events_);
task_queue_.SendTask([this] { handler_->callback_->OnQpUsageHandled(true); },
RTC_FROM_HERE);
}
} // namespace webrtc