[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:
committed by
Commit Bot
parent
8e95ea92b2
commit
a1c77f6d0d
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user