Add initial bitrate and frame resolution parameters to quality scaler.

- Scale down to VGA immediately if call starts with HD resolution
and bitrate below 500 kbps.
- Adjust QP threshold for HW VP8 encoder to scale down faster.

BUG=b/26504665
R=mflodman@webrtc.org, pbos@webrtc.org, sprang@google.com, stefan@webrtc.org

Review URL: https://codereview.webrtc.org/1672173002 .

Cr-Commit-Position: refs/heads/master@{#11692}
This commit is contained in:
Alex Glaznev 2016-02-19 15:24:06 -08:00
parent 0013dcc0c1
commit a9d0892946
5 changed files with 68 additions and 18 deletions

View File

@ -45,6 +45,7 @@ using webrtc::VideoCodecType;
using webrtc::kVideoCodecH264;
using webrtc::kVideoCodecVP8;
using webrtc::kVideoCodecVP9;
using webrtc::QualityScaler;
namespace webrtc_jni {
@ -242,7 +243,7 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
bool drop_next_input_frame_;
// Global references; must be deleted in Release().
std::vector<jobject> input_buffers_;
webrtc::QualityScaler quality_scaler_;
QualityScaler quality_scaler_;
// Dynamic resolution change, off by default.
bool scale_;
@ -355,7 +356,6 @@ int32_t MediaCodecVideoEncoder::InitEncode(
size_t /* max_payload_size */) {
const int kMinWidth = 320;
const int kMinHeight = 180;
const int kLowQpThresholdDenominator = 3;
if (codec_settings == NULL) {
ALOGE << "NULL VideoCodec instance";
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
@ -365,40 +365,52 @@ int32_t MediaCodecVideoEncoder::InitEncode(
<< "Unsupported codec " << codec_settings->codecType << " for "
<< codecType_;
ALOGD << "InitEncode request";
codec_mode_ = codec_settings->mode;
int init_width = codec_settings->width;
int init_height = codec_settings->height;
scale_ = (codecType_ != kVideoCodecVP9) && (webrtc::field_trial::FindFullName(
"WebRTC-MediaCodecVideoEncoder-AutomaticResize") == "Enabled");
ALOGD << "InitEncode request: " << init_width << " x " << init_height;
ALOGD << "Encoder automatic resize " << (scale_ ? "enabled" : "disabled");
if (scale_) {
if (codecType_ == kVideoCodecVP8) {
// QP is obtained from VP8-bitstream for HW, so the QP corresponds to the
// (internal) range: [0, 127]. And we cannot change QP_max in HW, so it is
// always = 127. Note that in SW, QP is that of the user-level range [0,
// 63].
const int kMaxQp = 127;
const int kBadQpThreshold = 95;
quality_scaler_.Init(
kMaxQp / kLowQpThresholdDenominator, kBadQpThreshold, false);
const int kLowQpThreshold = 32;
const int kBadQpThreshold = 92;
quality_scaler_.Init(kLowQpThreshold, kBadQpThreshold, false,
codec_settings->startBitrate,
codec_settings->width, codec_settings->height);
} else if (codecType_ == kVideoCodecH264) {
// H264 QP is in the range [0, 51].
const int kMaxQp = 51;
const int kLowQpThreshold = 17;
const int kBadQpThreshold = 40;
quality_scaler_.Init(
kMaxQp / kLowQpThresholdDenominator, kBadQpThreshold, false);
quality_scaler_.Init(kLowQpThreshold, kBadQpThreshold, false,
codec_settings->startBitrate,
codec_settings->width, codec_settings->height);
} else {
// When adding codec support to additional hardware codecs, also configure
// their QP thresholds for scaling.
RTC_NOTREACHED() << "Unsupported codec without configured QP thresholds.";
scale_ = false;
}
quality_scaler_.SetMinResolution(kMinWidth, kMinHeight);
quality_scaler_.ReportFramerate(codec_settings->maxFramerate);
QualityScaler::Resolution res = quality_scaler_.GetScaledResolution();
init_width = std::max(res.width, kMinWidth);
init_height = std::max(res.height, kMinHeight);
ALOGD << "Scaled resolution: " << init_width << " x " << init_height;
}
return codec_thread_->Invoke<int32_t>(
Bind(&MediaCodecVideoEncoder::InitEncodeOnCodecThread,
this,
codec_settings->width,
codec_settings->height,
init_width,
init_height,
codec_settings->startBitrate,
codec_settings->maxFramerate,
false /* use_surface */));

View File

@ -600,8 +600,10 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
// Disable both high-QP limits and framedropping. Both are handled by libvpx
// internally.
const int kDisabledBadQpThreshold = 64;
// TODO(glaznev/sprang): consider passing codec initial bitrate to quality
// scaler to avoid starting with HD for low initial bitrates.
quality_scaler_.Init(codec_.qpMax / QualityScaler::kDefaultLowQpDenominator,
kDisabledBadQpThreshold, false);
kDisabledBadQpThreshold, false, 0, 0, 0);
quality_scaler_.ReportFramerate(codec_.maxFramerate);
// Only apply scaling to improve for single-layer streams. The scaling metrics

View File

@ -14,6 +14,8 @@ namespace webrtc {
static const int kMinFps = 10;
static const int kMeasureSeconds = 5;
static const int kFramedropPercentThreshold = 60;
static const int kHdResolutionThreshold = 700 * 500;
static const int kHdBitrateThresholdKbps = 500;
const int QualityScaler::kDefaultLowQpDenominator = 3;
// Note that this is the same for width and height to permit 120x90 in both
@ -30,11 +32,27 @@ QualityScaler::QualityScaler()
void QualityScaler::Init(int low_qp_threshold,
int high_qp_threshold,
bool use_framerate_reduction) {
bool use_framerate_reduction,
int initial_bitrate_kbps,
int width,
int height) {
ClearSamples();
low_qp_threshold_ = low_qp_threshold;
high_qp_threshold_ = high_qp_threshold;
use_framerate_reduction_ = use_framerate_reduction;
// TODO(glaznev): Investigate using thresholds for other resolutions
// or threshold tables.
if (initial_bitrate_kbps > 0 &&
initial_bitrate_kbps < kHdBitrateThresholdKbps) {
// Start scaling to roughly VGA.
while (width * height > kHdResolutionThreshold) {
++downscale_shift_;
width /= 2;
height /= 2;
}
}
res_.width = width;
res_.height = height;
target_framerate_ = -1;
}

View File

@ -27,7 +27,10 @@ class QualityScaler {
QualityScaler();
void Init(int low_qp_threshold,
int high_qp_threshold,
bool use_framerate_reduction);
bool use_framerate_reduction,
int initial_bitrate_kbps,
int width,
int height);
void SetMinResolution(int min_width, int min_height);
void ReportFramerate(int framerate);
void ReportQP(int qp);

View File

@ -16,13 +16,17 @@ namespace webrtc {
namespace {
static const int kNumSeconds = 10;
static const int kWidth = 1920;
static const int kWidthVga = 640;
static const int kHalfWidth = kWidth / 2;
static const int kHeight = 1080;
static const int kHeightVga = 480;
static const int kFramerate = 30;
static const int kLowQp = 15;
static const int kNormalQp = 30;
static const int kHighQp = 40;
static const int kMaxQp = 56;
static const int kDisabledBadQpThreshold = kMaxQp + 1;
static const int kLowInitialBitrateKbps = 300;
} // namespace
class QualityScalerTest : public ::testing::Test {
@ -46,7 +50,8 @@ class QualityScalerTest : public ::testing::Test {
QualityScalerTest() {
input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth,
kHalfWidth);
qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false);
qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false,
0, 0, 0);
qs_.ReportFramerate(kFramerate);
qs_.OnEncodeFrame(input_frame_);
}
@ -296,9 +301,8 @@ void QualityScalerTest::VerifyQualityAdaptation(
int seconds,
bool expect_spatial_resize,
bool expect_framerate_reduction) {
const int kDisabledBadQpThreshold = kMaxQp + 1;
qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator,
kDisabledBadQpThreshold, true);
kDisabledBadQpThreshold, true, 0, 0, 0);
qs_.OnEncodeFrame(input_frame_);
int init_width = qs_.GetScaledResolution().width;
int init_height = qs_.GetScaledResolution().height;
@ -364,6 +368,17 @@ TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) {
1000, 2 * QualityScaler::kDefaultMinDownscaleDimension - 1);
}
TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) {
qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator,
kDisabledBadQpThreshold, true,
kLowInitialBitrateKbps, kWidth, kHeight);
qs_.OnEncodeFrame(input_frame_);
int init_width = qs_.GetScaledResolution().width;
int init_height = qs_.GetScaledResolution().height;
EXPECT_LE(init_width, kWidthVga);
EXPECT_LE(init_height, kHeightVga);
}
void QualityScalerTest::DownscaleEndsAt(int input_width,
int input_height,
int end_width,