mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 06:10:40 +01:00

This CL has been generated with the following script: for m in PLOG \ LOG_TAG \ LOG_GLEM \ LOG_GLE_EX \ LOG_GLE \ LAST_SYSTEM_ERROR \ LOG_ERRNO_EX \ LOG_ERRNO \ LOG_ERR_EX \ LOG_ERR \ LOG_V \ LOG_F \ LOG_T_F \ LOG_E \ LOG_T \ LOG_CHECK_LEVEL_V \ LOG_CHECK_LEVEL \ LOG do git grep -l $m | xargs sed -i "s,\b$m\b,RTC_$m,g" done git checkout rtc_base/logging.h git cl format Bug: webrtc:8452 Change-Id: I1a53ef3e0a5ef6e244e62b2e012b864914784600 Reviewed-on: https://webrtc-review.googlesource.com/21325 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20617}
205 lines
6.6 KiB
C++
205 lines
6.6 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 "modules/video_coding/utility/quality_scaler.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "rtc_base/task_queue.h"
|
|
|
|
// TODO(kthelgason): Some versions of Android have issues with log2.
|
|
// See https://code.google.com/p/android/issues/detail?id=212634 for details
|
|
#if defined(WEBRTC_ANDROID)
|
|
#define log2(x) (log(x) / log(2))
|
|
#endif
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
// Threshold constant used until first downscale (to permit fast rampup).
|
|
static const int kMeasureMs = 2000;
|
|
static const float kSamplePeriodScaleFactor = 2.5;
|
|
static const int kFramedropPercentThreshold = 60;
|
|
// QP scaling threshold defaults:
|
|
static const int kLowH264QpThreshold = 24;
|
|
static const int kHighH264QpThreshold = 37;
|
|
// QP is obtained from VP8-bitstream for HW, so the QP corresponds to the
|
|
// bitstream range of [0, 127] and not the user-level range of [0,63].
|
|
static const int kLowVp8QpThreshold = 29;
|
|
static const int kHighVp8QpThreshold = 95;
|
|
// QP is obtained from VP9-bitstream for HW, so the QP corresponds to the
|
|
// bitstream range of [0, 255] and not the user-level range of [0,63].
|
|
// Current VP9 settings are mapped from VP8 thresholds above.
|
|
static const int kLowVp9QpThreshold = 96;
|
|
static const int kHighVp9QpThreshold = 185;
|
|
static const int kMinFramesNeededToScale = 2 * 30;
|
|
|
|
static VideoEncoder::QpThresholds CodecTypeToDefaultThresholds(
|
|
VideoCodecType codec_type) {
|
|
int low = -1;
|
|
int high = -1;
|
|
switch (codec_type) {
|
|
case kVideoCodecH264:
|
|
low = kLowH264QpThreshold;
|
|
high = kHighH264QpThreshold;
|
|
break;
|
|
case kVideoCodecVP8:
|
|
low = kLowVp8QpThreshold;
|
|
high = kHighVp8QpThreshold;
|
|
break;
|
|
case kVideoCodecVP9:
|
|
low = kLowVp9QpThreshold;
|
|
high = kHighVp9QpThreshold;
|
|
break;
|
|
default:
|
|
RTC_NOTREACHED() << "Invalid codec type for QualityScaler.";
|
|
}
|
|
return VideoEncoder::QpThresholds(low, high);
|
|
}
|
|
} // namespace
|
|
|
|
class QualityScaler::CheckQPTask : public rtc::QueuedTask {
|
|
public:
|
|
explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) {
|
|
RTC_LOG(LS_INFO) << "Created CheckQPTask. Scheduling on queue...";
|
|
rtc::TaskQueue::Current()->PostDelayedTask(
|
|
std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
|
|
}
|
|
void Stop() {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
RTC_LOG(LS_INFO) << "Stopping QP Check task.";
|
|
stop_ = true;
|
|
}
|
|
|
|
private:
|
|
bool Run() override {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
if (stop_)
|
|
return true; // TaskQueue will free this task.
|
|
scaler_->CheckQP();
|
|
rtc::TaskQueue::Current()->PostDelayedTask(
|
|
std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
|
|
return false; // Retain the task in order to reuse it.
|
|
}
|
|
|
|
QualityScaler* const scaler_;
|
|
bool stop_ = false;
|
|
rtc::SequencedTaskChecker task_checker_;
|
|
};
|
|
|
|
QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
|
|
VideoCodecType codec_type)
|
|
: QualityScaler(observer, CodecTypeToDefaultThresholds(codec_type)) {}
|
|
|
|
QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
|
|
VideoEncoder::QpThresholds thresholds)
|
|
: QualityScaler(observer, thresholds, kMeasureMs) {}
|
|
|
|
// Protected ctor, should not be called directly.
|
|
QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
|
|
VideoEncoder::QpThresholds thresholds,
|
|
int64_t sampling_period)
|
|
: check_qp_task_(nullptr),
|
|
observer_(observer),
|
|
sampling_period_ms_(sampling_period),
|
|
fast_rampup_(true),
|
|
// Arbitrarily choose size based on 30 fps for 5 seconds.
|
|
average_qp_(5 * 30),
|
|
framedrop_percent_(5 * 30),
|
|
thresholds_(thresholds) {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
RTC_DCHECK(observer_ != nullptr);
|
|
check_qp_task_ = new CheckQPTask(this);
|
|
RTC_LOG(LS_INFO) << "QP thresholds: low: " << thresholds_.low
|
|
<< ", high: " << thresholds_.high;
|
|
}
|
|
|
|
QualityScaler::~QualityScaler() {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
check_qp_task_->Stop();
|
|
}
|
|
|
|
int64_t QualityScaler::GetSamplingPeriodMs() const {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
return fast_rampup_ ? sampling_period_ms_
|
|
: (sampling_period_ms_ * kSamplePeriodScaleFactor);
|
|
}
|
|
|
|
void QualityScaler::ReportDroppedFrame() {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
framedrop_percent_.AddSample(100);
|
|
}
|
|
|
|
void QualityScaler::ReportQP(int qp) {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
framedrop_percent_.AddSample(0);
|
|
average_qp_.AddSample(qp);
|
|
}
|
|
|
|
void QualityScaler::CheckQP() {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
// Should be set through InitEncode -> Should be set by now.
|
|
RTC_DCHECK_GE(thresholds_.low, 0);
|
|
|
|
// If we have not observed at least this many frames we can't
|
|
// make a good scaling decision.
|
|
if (framedrop_percent_.size() < kMinFramesNeededToScale)
|
|
return;
|
|
|
|
// Check if we should scale down due to high frame drop.
|
|
const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
|
|
if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
|
|
ReportQPHigh();
|
|
return;
|
|
}
|
|
|
|
// Check if we should scale up or down based on QP.
|
|
const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
|
|
if (avg_qp) {
|
|
RTC_LOG(LS_INFO) << "Checking average QP " << *avg_qp;
|
|
if (*avg_qp > thresholds_.high) {
|
|
ReportQPHigh();
|
|
return;
|
|
}
|
|
if (*avg_qp <= thresholds_.low) {
|
|
// QP has been low. We want to try a higher resolution.
|
|
ReportQPLow();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QualityScaler::ReportQPLow() {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
ClearSamples();
|
|
observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
|
|
}
|
|
|
|
void QualityScaler::ReportQPHigh() {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
ClearSamples();
|
|
observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
|
|
// If we've scaled down, wait longer before scaling up again.
|
|
if (fast_rampup_) {
|
|
fast_rampup_ = false;
|
|
}
|
|
}
|
|
|
|
void QualityScaler::ClearSamples() {
|
|
RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
|
|
framedrop_percent_.Reset();
|
|
average_qp_.Reset();
|
|
}
|
|
} // namespace webrtc
|