Implement a high-QP threshold for Android H.264.
Android hardware H.264 seems to keep a steady high-QP flow instead of
dropping frames, so framedrops aren't sufficient to detect a bad state
where downscaling would be beneficial.
BUG=webrtc:4968
R=magjed@webrtc.org, stefan@webrtc.org
Review URL: https://codereview.webrtc.org/1364253002 .
Cr-Commit-Position: refs/heads/master@{#10078}
diff --git a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
index f6a3b65..a25a3cc 100644
--- a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
+++ b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
@@ -306,11 +306,18 @@
// always = 127. Note that in SW, QP is that of the user-level range [0,
// 63].
const int kMaxQp = 127;
- quality_scaler_.Init(kMaxQp / kLowQpThresholdDenominator, true);
+ // TODO(pbos): Investigate whether high-QP thresholds make sense for VP8.
+ // This effectively disables high QP as VP8 QP can't go above this
+ // threshold.
+ const int kDisabledBadQpThreshold = kMaxQp + 1;
+ quality_scaler_.Init(kMaxQp / kLowQpThresholdDenominator,
+ kDisabledBadQpThreshold, true);
} else if (codecType_ == kVideoCodecH264) {
// H264 QP is in the range [0, 51].
const int kMaxQp = 51;
- quality_scaler_.Init(kMaxQp / kLowQpThresholdDenominator, true);
+ const int kBadQpThreshold = 40;
+ quality_scaler_.Init(kMaxQp / kLowQpThresholdDenominator, kBadQpThreshold,
+ false);
} else {
// When adding codec support to additional hardware codecs, also configure
// their QP thresholds for scaling.
diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
index 48ed02a..651406a 100644
--- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
@@ -579,8 +579,11 @@
}
rps_.Init();
+ // Disable both high-QP limits and framedropping. Both are handled by libvpx
+ // internally.
+ const int kDisabledBadQpThreshold = 64;
quality_scaler_.Init(codec_.qpMax / QualityScaler::kDefaultLowQpDenominator,
- false);
+ kDisabledBadQpThreshold, false);
quality_scaler_.ReportFramerate(codec_.maxFramerate);
return InitAndSetControlSettings();
diff --git a/webrtc/modules/video_coding/utility/include/quality_scaler.h b/webrtc/modules/video_coding/utility/include/quality_scaler.h
index 8535171..e92c55d 100644
--- a/webrtc/modules/video_coding/utility/include/quality_scaler.h
+++ b/webrtc/modules/video_coding/utility/include/quality_scaler.h
@@ -25,7 +25,9 @@
};
QualityScaler();
- void Init(int low_qp_threshold, bool use_framerate_reduction);
+ void Init(int low_qp_threshold,
+ int high_qp_threshold,
+ bool use_framerate_reduction);
void SetMinResolution(int min_width, int min_height);
void ReportFramerate(int framerate);
void ReportQP(int qp);
@@ -47,6 +49,7 @@
int framerate_;
int target_framerate_;
int low_qp_threshold_;
+ int high_qp_threshold_;
MovingAverage<int> framedrop_percent_;
MovingAverage<int> average_qp_;
Resolution res_;
diff --git a/webrtc/modules/video_coding/utility/quality_scaler.cc b/webrtc/modules/video_coding/utility/quality_scaler.cc
index f7d4651..ec77152 100644
--- a/webrtc/modules/video_coding/utility/quality_scaler.cc
+++ b/webrtc/modules/video_coding/utility/quality_scaler.cc
@@ -29,9 +29,12 @@
min_height_(kDefaultMinDownscaleDimension) {
}
-void QualityScaler::Init(int low_qp_threshold, bool use_framerate_reduction) {
+void QualityScaler::Init(int low_qp_threshold,
+ int high_qp_threshold,
+ bool use_framerate_reduction) {
ClearSamples();
low_qp_threshold_ = low_qp_threshold;
+ high_qp_threshold_ = high_qp_threshold;
use_framerate_reduction_ = use_framerate_reduction;
target_framerate_ = -1;
}
@@ -70,8 +73,10 @@
// When encoder consistently overshoots, framerate reduction and spatial
// resizing will be triggered to get a smoother video.
- if (framedrop_percent_.GetAverage(num_samples_, &avg_drop) &&
- avg_drop >= kFramedropPercentThreshold) {
+ if ((framedrop_percent_.GetAverage(num_samples_, &avg_drop) &&
+ avg_drop >= kFramedropPercentThreshold) ||
+ (average_qp_.GetAverage(num_samples_, &avg_qp) &&
+ avg_qp > high_qp_threshold_)) {
// Reducing frame rate before spatial resolution change.
// Reduce frame rate only when it is above a certain number.
// Only one reduction is allowed for now.
diff --git a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc
index d1710b6..2ce1107 100644
--- a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc
+++ b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc
@@ -21,6 +21,7 @@
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;
} // namespace
@@ -33,13 +34,18 @@
int height;
};
protected:
- enum ScaleDirection { kScaleDown, kScaleUp };
+ enum ScaleDirection {
+ kKeepScaleAtHighQp,
+ kScaleDown,
+ kScaleDownAboveHighQp,
+ kScaleUp
+ };
enum BadQualityMetric { kDropFrame, kReportLowQP };
QualityScalerTest() {
input_frame_.CreateEmptyFrame(
kWidth, kHeight, kWidth, kHalfWidth, kHalfWidth);
- qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, false);
+ qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false);
qs_.ReportFramerate(kFramerate);
qs_.OnEncodeFrame(input_frame_);
}
@@ -55,6 +61,12 @@
case kScaleDown:
qs_.ReportDroppedFrame();
break;
+ case kKeepScaleAtHighQp:
+ qs_.ReportQP(kHighQp);
+ break;
+ case kScaleDownAboveHighQp:
+ qs_.ReportQP(kHighQp + 1);
+ break;
}
qs_.OnEncodeFrame(input_frame_);
if (qs_.GetScaledResolution().width != initial_width)
@@ -117,6 +129,22 @@
EXPECT_LT(res.height, input_frame_.height());
}
+TEST_F(QualityScalerTest, KeepsScaleAtHighQp) {
+ EXPECT_FALSE(TriggerScale(kKeepScaleAtHighQp))
+ << "Downscale at high threshold which should keep scale.";
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
+ EXPECT_EQ(res.width, input_frame_.width());
+ EXPECT_EQ(res.height, input_frame_.height());
+}
+
+TEST_F(QualityScalerTest, DownscalesAboveHighQp) {
+ EXPECT_TRUE(TriggerScale(kScaleDownAboveHighQp))
+ << "No downscale within " << kNumSeconds << " seconds.";
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
+ EXPECT_LT(res.width, input_frame_.width());
+ EXPECT_LT(res.height, input_frame_.height());
+}
+
TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
for (int i = 0; i < kFramerate * kNumSeconds / 3; ++i) {
qs_.ReportQP(kNormalQp);
@@ -262,7 +290,9 @@
void QualityScalerTest::VerifyQualityAdaptation(
int initial_framerate, int seconds, bool expect_spatial_resize,
bool expect_framerate_reduction) {
- qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, true);
+ const int kDisabledBadQpThreshold = kMaxQp + 1;
+ qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator,
+ kDisabledBadQpThreshold, true);
qs_.OnEncodeFrame(input_frame_);
int init_width = qs_.GetScaledResolution().width;
int init_height = qs_.GetScaledResolution().height;