Allow for framerate reduction for HW encoder.
R=pbos@webrtc.org, stefan@webrtc.org
TBR=glaznev@google.com
Review URL: https://webrtc-codereview.appspot.com/51159004 .
Cr-Commit-Position: refs/heads/master@{#9573}
diff --git a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
index 4db089c..25ffe91 100644
--- a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
+++ b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
@@ -100,6 +100,8 @@
void OnDroppedFrame() override;
+ int GetTargetFramerate() override;
+
private:
// CHECK-fail if not running on |codec_thread_|.
void CheckOnCodecThread();
@@ -199,6 +201,7 @@
scoped_ptr<webrtc::QualityScaler> quality_scaler_;
// Dynamic resolution change, off by default.
bool scale_;
+ int updated_framerate_;
};
MediaCodecVideoEncoder::~MediaCodecVideoEncoder() {
@@ -294,11 +297,12 @@
ALOGD("InitEncode request");
scale_ = false;
- if (codecType_ == kVideoCodecVP8) {
- quality_scaler_->Init(kMaxQP / kLowQpThresholdDenominator);
+ if (scale_ && codecType_ == kVideoCodecVP8) {
+ quality_scaler_->Init(kMaxQP / kLowQpThresholdDenominator, true);
quality_scaler_->SetMinResolution(kMinWidth, kMinHeight);
quality_scaler_->ReportFramerate(codec_settings->maxFramerate);
}
+ updated_framerate_ = codec_settings->maxFramerate;
return codec_thread_->Invoke<int32_t>(
Bind(&MediaCodecVideoEncoder::InitEncodeOnCodecThread,
this,
@@ -337,8 +341,11 @@
int32_t MediaCodecVideoEncoder::SetRates(uint32_t new_bit_rate,
uint32_t frame_rate) {
- if (codecType_ == kVideoCodecVP8)
+ if (scale_ && codecType_ == kVideoCodecVP8) {
quality_scaler_->ReportFramerate(frame_rate);
+ } else {
+ updated_framerate_ = frame_rate;
+ }
return codec_thread_->Invoke<int32_t>(
Bind(&MediaCodecVideoEncoder::SetRatesOnCodecThread,
this,
@@ -493,9 +500,13 @@
}
CHECK(frame_types->size() == 1) << "Unexpected stream count";
- const VideoFrame& input_frame = (scale_ && codecType_ == kVideoCodecVP8)
- ? quality_scaler_->GetScaledFrame(frame)
- : frame;
+ // Check framerate before spatial resolution change.
+ if (scale_ && codecType_ == kVideoCodecVP8) {
+ quality_scaler_->OnEncodeFrame(frame);
+ updated_framerate_ = quality_scaler_->GetTargetFramerate();
+ }
+ const VideoFrame& input_frame = (scale_ && codecType_ == kVideoCodecVP8) ?
+ quality_scaler_->GetScaledFrame(frame) : frame;
if (input_frame.width() != width_ || input_frame.height() != height_) {
ALOGD("Frame resolution change from %d x %d to %d x %d",
@@ -506,8 +517,6 @@
return WEBRTC_VIDEO_CODEC_OK;
}
- bool key_frame = frame_types->front() != webrtc::kDeltaFrame;
-
// Check if we accumulated too many frames in encoder input buffers
// or the encoder latency exceeds 70 ms and drop frame if so.
if (frames_in_queue_ > 0 && last_input_timestamp_ms_ >= 0) {
@@ -566,6 +575,7 @@
render_times_ms_.push_back(input_frame.render_time_ms());
frame_rtc_times_ms_.push_back(GetCurrentTimeMs());
+ bool key_frame = frame_types->front() != webrtc::kDeltaFrame;
bool encode_status = jni->CallBooleanMethod(*j_media_codec_video_encoder_,
j_encode_method_,
key_frame,
@@ -712,7 +722,7 @@
last_input_timestamp_ms_ - last_output_timestamp_ms_,
frame_encoding_time_ms);
- if (payload_size && codecType_ == kVideoCodecVP8)
+ if (payload_size && scale_ && codecType_ == kVideoCodecVP8)
quality_scaler_->ReportQP(webrtc::vp8::GetQP(payload));
// Calculate and print encoding statistics - every 3 seconds.
@@ -860,10 +870,14 @@
}
void MediaCodecVideoEncoder::OnDroppedFrame() {
- if (codecType_ == kVideoCodecVP8)
+ if (scale_ && codecType_ == kVideoCodecVP8)
quality_scaler_->ReportDroppedFrame();
}
+int MediaCodecVideoEncoder::GetTargetFramerate() {
+ return updated_framerate_;
+}
+
MediaCodecVideoEncoderFactory::MediaCodecVideoEncoderFactory() {
JNIEnv* jni = AttachCurrentThreadIfNeeded();
ScopedLocalRefFrame local_ref_frame(jni);
diff --git a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
index ee7fd85..7c4164c 100644
--- a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
@@ -504,4 +504,8 @@
streaminfos_[0].encoder->OnDroppedFrame();
}
+int SimulcastEncoderAdapter::GetTargetFramerate() {
+ return streaminfos_[0].encoder->GetTargetFramerate();
+}
+
} // namespace webrtc
diff --git a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.h b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.h
index cd78234..bca1e00 100644
--- a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.h
+++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.h
@@ -57,6 +57,8 @@
void OnDroppedFrame() override;
+ int GetTargetFramerate() override;
+
private:
struct StreamInfo {
StreamInfo()
diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
index 05e0799..31b92ea 100644
--- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
@@ -580,7 +580,8 @@
}
rps_.Init();
- quality_scaler_.Init(codec_.qpMax / QualityScaler::kDefaultLowQpDenominator);
+ quality_scaler_.Init(codec_.qpMax / QualityScaler::kDefaultLowQpDenominator,
+ false);
quality_scaler_.ReportFramerate(codec_.maxFramerate);
return InitAndSetControlSettings();
@@ -709,6 +710,8 @@
const bool use_quality_scaler = encoders_.size() == 1 &&
configurations_[0].rc_dropframe_thresh > 0 &&
codec_.codecSpecific.VP8.automaticResizeOn;
+ if (use_quality_scaler)
+ quality_scaler_.OnEncodeFrame(frame);
const VideoFrame& input_image =
use_quality_scaler ? quality_scaler_.GetScaledFrame(frame) : frame;
diff --git a/webrtc/modules/video_coding/main/interface/video_coding_defines.h b/webrtc/modules/video_coding/main/interface/video_coding_defines.h
index 259e75e..d40023c 100644
--- a/webrtc/modules/video_coding/main/interface/video_coding_defines.h
+++ b/webrtc/modules/video_coding/main/interface/video_coding_defines.h
@@ -182,6 +182,8 @@
const uint32_t width,
const uint32_t height) = 0;
+ virtual void SetTargetFramerate(int frame_rate) = 0;
+
protected:
virtual ~VCMQMSettingsCallback() {
}
diff --git a/webrtc/modules/video_coding/main/source/generic_encoder.cc b/webrtc/modules/video_coding/main/source/generic_encoder.cc
index f2cdd59..63e3976 100644
--- a/webrtc/modules/video_coding/main/source/generic_encoder.cc
+++ b/webrtc/modules/video_coding/main/source/generic_encoder.cc
@@ -214,6 +214,10 @@
return encoder_->SupportsNativeHandle();
}
+int VCMGenericEncoder::GetTargetFramerate() {
+ return encoder_->GetTargetFramerate();
+}
+
/***************************
* Callback Implementation
***************************/
diff --git a/webrtc/modules/video_coding/main/source/generic_encoder.h b/webrtc/modules/video_coding/main/source/generic_encoder.h
index 862c06b..25235b6 100644
--- a/webrtc/modules/video_coding/main/source/generic_encoder.h
+++ b/webrtc/modules/video_coding/main/source/generic_encoder.h
@@ -140,6 +140,8 @@
bool SupportsNativeHandle() const;
+ int GetTargetFramerate();
+
private:
VideoEncoder* const encoder_;
VideoEncoderRateObserver* const rate_observer_;
diff --git a/webrtc/modules/video_coding/main/source/video_sender.cc b/webrtc/modules/video_coding/main/source/video_sender.cc
index fb04b0e..93d8208 100644
--- a/webrtc/modules/video_coding/main/source/video_sender.cc
+++ b/webrtc/modules/video_coding/main/source/video_sender.cc
@@ -363,6 +363,8 @@
for (size_t i = 0; i < _nextFrameTypes.size(); ++i) {
_nextFrameTypes[i] = kVideoFrameDelta; // Default frame type.
}
+ if (qm_settings_callback_)
+ qm_settings_callback_->SetTargetFramerate(_encoder->GetTargetFramerate());
return VCM_OK;
}
diff --git a/webrtc/modules/video_coding/utility/include/quality_scaler.h b/webrtc/modules/video_coding/utility/include/quality_scaler.h
index 1d6c917..8535171 100644
--- a/webrtc/modules/video_coding/utility/include/quality_scaler.h
+++ b/webrtc/modules/video_coding/utility/include/quality_scaler.h
@@ -25,14 +25,16 @@
};
QualityScaler();
- void Init(int low_qp_threshold);
+ void Init(int low_qp_threshold, bool use_framerate_reduction);
void SetMinResolution(int min_width, int min_height);
void ReportFramerate(int framerate);
void ReportQP(int qp);
void ReportDroppedFrame();
void Reset(int framerate, int bitrate, int width, int height);
- Resolution GetScaledResolution(const VideoFrame& frame);
+ void OnEncodeFrame(const VideoFrame& frame);
+ Resolution GetScaledResolution() const;
const VideoFrame& GetScaledFrame(const VideoFrame& frame);
+ int GetTargetFramerate() const;
private:
void AdjustScale(bool up);
@@ -42,11 +44,16 @@
VideoFrame scaled_frame_;
size_t num_samples_;
+ int framerate_;
+ int target_framerate_;
int low_qp_threshold_;
MovingAverage<int> framedrop_percent_;
MovingAverage<int> average_qp_;
+ Resolution res_;
int downscale_shift_;
+ int framerate_down_;
+ bool use_framerate_reduction_;
int min_width_;
int min_height_;
};
diff --git a/webrtc/modules/video_coding/utility/quality_scaler.cc b/webrtc/modules/video_coding/utility/quality_scaler.cc
index 7a2a9c0..f7d4651 100644
--- a/webrtc/modules/video_coding/utility/quality_scaler.cc
+++ b/webrtc/modules/video_coding/utility/quality_scaler.cc
@@ -24,13 +24,16 @@
: num_samples_(0),
low_qp_threshold_(-1),
downscale_shift_(0),
+ framerate_down_(false),
min_width_(kDefaultMinDownscaleDimension),
min_height_(kDefaultMinDownscaleDimension) {
}
-void QualityScaler::Init(int low_qp_threshold) {
+void QualityScaler::Init(int low_qp_threshold, bool use_framerate_reduction) {
ClearSamples();
low_qp_threshold_ = low_qp_threshold;
+ use_framerate_reduction_ = use_framerate_reduction;
+ target_framerate_ = -1;
}
void QualityScaler::SetMinResolution(int min_width, int min_height) {
@@ -42,6 +45,7 @@
void QualityScaler::ReportFramerate(int framerate) {
num_samples_ = static_cast<size_t>(
kMeasureSeconds * (framerate < kMinFps ? kMinFps : framerate));
+ framerate_ = framerate;
}
void QualityScaler::ReportQP(int qp) {
@@ -53,41 +57,65 @@
framedrop_percent_.AddSample(100);
}
-QualityScaler::Resolution QualityScaler::GetScaledResolution(
- const VideoFrame& frame) {
+void QualityScaler::OnEncodeFrame(const VideoFrame& frame) {
// Should be set through InitEncode -> Should be set by now.
assert(low_qp_threshold_ >= 0);
assert(num_samples_ > 0);
-
- Resolution res;
- res.width = frame.width();
- res.height = frame.height();
+ res_.width = frame.width();
+ res_.height = frame.height();
// Update scale factor.
int avg_drop = 0;
int avg_qp = 0;
+
+ // 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) {
- AdjustScale(false);
+ // 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.
+ // TODO(jackychen): Allow more than one framerate reduction.
+ if (use_framerate_reduction_ && !framerate_down_ && framerate_ >= 20) {
+ target_framerate_ = framerate_ / 2;
+ framerate_down_ = true;
+ // If frame rate has been updated, clear the buffer. We don't want
+ // spatial resolution to change right after frame rate change.
+ ClearSamples();
+ } else {
+ AdjustScale(false);
+ }
} else if (average_qp_.GetAverage(num_samples_, &avg_qp) &&
avg_qp <= low_qp_threshold_) {
- AdjustScale(true);
+ if (use_framerate_reduction_ && framerate_down_) {
+ target_framerate_ = -1;
+ framerate_down_ = false;
+ ClearSamples();
+ } else {
+ AdjustScale(true);
+ }
}
assert(downscale_shift_ >= 0);
for (int shift = downscale_shift_;
- shift > 0 && (res.width >> 1 >= min_width_) &&
- (res.height >> 1 >= min_height_);
+ shift > 0 && (res_.width / 2 >= min_width_) &&
+ (res_.height / 2 >= min_height_);
--shift) {
- res.width >>= 1;
- res.height >>= 1;
+ res_.width /= 2;
+ res_.height /= 2;
}
+}
- return res;
+QualityScaler::Resolution QualityScaler::GetScaledResolution() const {
+ return res_;
+}
+
+int QualityScaler::GetTargetFramerate() const {
+ return target_framerate_;
}
const VideoFrame& QualityScaler::GetScaledFrame(const VideoFrame& frame) {
- Resolution res = GetScaledResolution(frame);
+ Resolution res = GetScaledResolution();
if (res.width == frame.width())
return frame;
diff --git a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc
index c09dffb..d1710b6 100644
--- a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc
+++ b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc
@@ -25,18 +25,28 @@
} // namespace
class QualityScalerTest : public ::testing::Test {
+ public:
+ // Temporal and spatial resolution.
+ struct Resolution {
+ int framerate;
+ int width;
+ int height;
+ };
protected:
enum ScaleDirection { kScaleDown, kScaleUp };
+ enum BadQualityMetric { kDropFrame, kReportLowQP };
QualityScalerTest() {
input_frame_.CreateEmptyFrame(
kWidth, kHeight, kWidth, kHalfWidth, kHalfWidth);
- qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator);
+ qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, false);
qs_.ReportFramerate(kFramerate);
+ qs_.OnEncodeFrame(input_frame_);
}
bool TriggerScale(ScaleDirection scale_direction) {
- int initial_width = qs_.GetScaledResolution(input_frame_).width;
+ qs_.OnEncodeFrame(input_frame_);
+ int initial_width = qs_.GetScaledResolution().width;
for (int i = 0; i < kFramerate * kNumSeconds; ++i) {
switch (scale_direction) {
case kScaleUp:
@@ -46,8 +56,8 @@
qs_.ReportDroppedFrame();
break;
}
-
- if (qs_.GetScaledResolution(input_frame_).width != initial_width)
+ qs_.OnEncodeFrame(input_frame_);
+ if (qs_.GetScaledResolution().width != initial_width)
return true;
}
@@ -60,7 +70,8 @@
}
void ExpectScaleUsingReportedResolution() {
- QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_);
+ qs_.OnEncodeFrame(input_frame_);
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
const VideoFrame& scaled_frame = qs_.GetScaledFrame(input_frame_);
EXPECT_EQ(res.width, scaled_frame.width());
EXPECT_EQ(res.height, scaled_frame.height());
@@ -70,6 +81,14 @@
void DoesNotDownscaleFrameDimensions(int width, int height);
+ Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp,
+ int num_second,
+ int initial_framerate);
+
+ void VerifyQualityAdaptation(int initial_framerate, int seconds,
+ bool expect_spatial_resize,
+ bool expect_framerate_reduction);
+
void DownscaleEndsAt(int input_width,
int input_height,
int end_width,
@@ -84,7 +103,8 @@
}
TEST_F(QualityScalerTest, ReportsOriginalResolutionInitially) {
- QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_);
+ qs_.OnEncodeFrame(input_frame_);
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
EXPECT_EQ(input_frame_.width(), res.width);
EXPECT_EQ(input_frame_.height(), res.height);
}
@@ -92,7 +112,7 @@
TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " << kNumSeconds
<< " seconds.";
- QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_);
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
EXPECT_LT(res.width, input_frame_.width());
EXPECT_LT(res.height, input_frame_.height());
}
@@ -102,7 +122,8 @@
qs_.ReportQP(kNormalQp);
qs_.ReportDroppedFrame();
qs_.ReportDroppedFrame();
- if (qs_.GetScaledResolution(input_frame_).width < input_frame_.width())
+ qs_.OnEncodeFrame(input_frame_);
+ if (qs_.GetScaledResolution().width < input_frame_.width())
return;
}
@@ -112,7 +133,8 @@
TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) {
for (int i = 0; i < kFramerate * kNumSeconds; ++i) {
qs_.ReportQP(kNormalQp);
- ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width)
+ qs_.OnEncodeFrame(input_frame_);
+ ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width)
<< "Unexpected scale on half framedrop.";
}
}
@@ -120,11 +142,13 @@
TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
for (int i = 0; i < kFramerate * kNumSeconds / 2; ++i) {
qs_.ReportQP(kNormalQp);
- ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width)
+ qs_.OnEncodeFrame(input_frame_);
+ ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width)
<< "Unexpected scale on half framedrop.";
qs_.ReportDroppedFrame();
- ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width)
+ qs_.OnEncodeFrame(input_frame_);
+ ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width)
<< "Unexpected scale on half framedrop.";
}
}
@@ -139,7 +163,8 @@
while (min_dimension >= 2 * QualityScaler::kDefaultMinDownscaleDimension) {
EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within "
<< kNumSeconds << " seconds.";
- QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_);
+ qs_.OnEncodeFrame(input_frame_);
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
min_dimension = res.width < res.height ? res.width : res.height;
++current_shift;
ASSERT_EQ(input_frame_.width() >> current_shift, res.width);
@@ -151,7 +176,8 @@
while (min_dimension < initial_min_dimension) {
EXPECT_TRUE(TriggerScale(kScaleUp)) << "No upscale within " << kNumSeconds
<< " seconds.";
- QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_);
+ qs_.OnEncodeFrame(input_frame_);
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
min_dimension = res.width < res.height ? res.width : res.height;
--current_shift;
ASSERT_EQ(input_frame_.width() >> current_shift, res.width);
@@ -186,7 +212,8 @@
for (int i = 0; i < kFramerate * kNumSeconds; ++i) {
qs_.ReportDroppedFrame();
- ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width)
+ qs_.OnEncodeFrame(input_frame_);
+ ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width)
<< "Unexpected scale of minimal-size frame.";
}
}
@@ -203,6 +230,94 @@
DoesNotDownscaleFrameDimensions(1, 1);
}
+QualityScalerTest::Resolution QualityScalerTest::TriggerResolutionChange(
+ BadQualityMetric dropframe_lowqp, int num_second, int initial_framerate) {
+ QualityScalerTest::Resolution res;
+ res.framerate = initial_framerate;
+ qs_.OnEncodeFrame(input_frame_);
+ res.width = qs_.GetScaledResolution().width;
+ res.height = qs_.GetScaledResolution().height;
+ for (int i = 0; i < kFramerate * num_second; ++i) {
+ switch (dropframe_lowqp) {
+ case kReportLowQP:
+ qs_.ReportQP(kLowQp);
+ break;
+ case kDropFrame:
+ qs_.ReportDroppedFrame();
+ break;
+ }
+ qs_.OnEncodeFrame(input_frame_);
+ // Simulate the case when SetRates is called right after reducing
+ // framerate.
+ qs_.ReportFramerate(initial_framerate);
+ res.framerate = qs_.GetTargetFramerate();
+ if (res.framerate != -1)
+ qs_.ReportFramerate(res.framerate);
+ res.width = qs_.GetScaledResolution().width;
+ res.height = qs_.GetScaledResolution().height;
+ }
+ return res;
+}
+
+void QualityScalerTest::VerifyQualityAdaptation(
+ int initial_framerate, int seconds, bool expect_spatial_resize,
+ bool expect_framerate_reduction) {
+ qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, true);
+ qs_.OnEncodeFrame(input_frame_);
+ int init_width = qs_.GetScaledResolution().width;
+ int init_height = qs_.GetScaledResolution().height;
+
+ // Test reducing framerate by dropping frame continuously.
+ QualityScalerTest::Resolution res = TriggerResolutionChange(
+ kDropFrame, seconds, initial_framerate);
+
+ if (expect_framerate_reduction) {
+ EXPECT_LT(res.framerate, initial_framerate);
+ } else {
+ // No framerate reduction, video decimator should be disabled.
+ EXPECT_EQ(-1, res.framerate);
+ }
+
+ if (expect_spatial_resize) {
+ EXPECT_LT(res.width, init_width);
+ EXPECT_LT(res.height, init_height);
+ } else {
+ EXPECT_EQ(init_width, res.width);
+ EXPECT_EQ(init_height, res.height);
+ }
+
+ // The "seconds * 1.5" is to ensure spatial resolution to recover.
+ // For example, in 10 seconds test, framerate reduction happens in the first
+ // 5 seconds from 30fps to 15fps and causes the buffer size to be half of the
+ // original one. Then it will take only 75 samples to downscale (twice in 150
+ // samples). So to recover the resolution changes, we need more than 10
+ // seconds (i.e, seconds * 1.5). This is because the framerate increases
+ // before spatial size recovers, so it will take 150 samples to recover
+ // spatial size (300 for twice).
+ res = TriggerResolutionChange(kReportLowQP, seconds * 1.5, initial_framerate);
+ EXPECT_EQ(-1, res.framerate);
+ EXPECT_EQ(init_width, res.width);
+ EXPECT_EQ(init_height, res.height);
+}
+
+// In 5 seconds test, only framerate adjusting should happen.
+TEST_F(QualityScalerTest, ChangeFramerateOnly) {
+ VerifyQualityAdaptation(kFramerate, 5, false, true);
+}
+
+// In 10 seconds test, framerate adjusting and scaling are both
+// triggered, it shows that scaling would happen after framerate
+// adjusting.
+TEST_F(QualityScalerTest, ChangeFramerateAndSpatialSize) {
+ VerifyQualityAdaptation(kFramerate, 10, true, true);
+}
+
+// When starting from a low framerate, only spatial size will be changed.
+TEST_F(QualityScalerTest, ChangeSpatialSizeOnly) {
+ qs_.ReportFramerate(kFramerate >> 1);
+ VerifyQualityAdaptation(kFramerate >> 1, 10, true, false);
+}
+
TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) {
DoesNotDownscaleFrameDimensions(
2 * QualityScaler::kDefaultMinDownscaleDimension - 1, 1000);
@@ -227,7 +342,7 @@
// Drop all frames to force-trigger downscaling.
while (true) {
TriggerScale(kScaleDown);
- QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_);
+ QualityScaler::Resolution res = qs_.GetScaledResolution();
if (last_width == res.width) {
EXPECT_EQ(last_height, res.height);
EXPECT_EQ(end_width, res.width);
diff --git a/webrtc/modules/video_processing/main/interface/video_processing.h b/webrtc/modules/video_processing/main/interface/video_processing.h
index 0cab9fc..08b9db7 100644
--- a/webrtc/modules/video_processing/main/interface/video_processing.h
+++ b/webrtc/modules/video_processing/main/interface/video_processing.h
@@ -214,6 +214,8 @@
uint32_t height,
uint32_t frame_rate) = 0;
+ virtual void SetTargetFramerate(int frame_rate) {}
+
/**
Get decimated(target) frame rate
*/
diff --git a/webrtc/modules/video_processing/main/source/frame_preprocessor.cc b/webrtc/modules/video_processing/main/source/frame_preprocessor.cc
index cf4bc81..a9d77c2 100644
--- a/webrtc/modules/video_processing/main/source/frame_preprocessor.cc
+++ b/webrtc/modules/video_processing/main/source/frame_preprocessor.cc
@@ -38,7 +38,6 @@
frame_cnt_ = 0;
}
-
void VPMFramePreprocessor::EnableTemporalDecimation(bool enable) {
vd_->EnableTemporalDecimation(enable);
}
@@ -62,12 +61,19 @@
if (ret_val < 0) return ret_val;
- ret_val = vd_->SetTargetFramerate(frame_rate);
- if (ret_val < 0) return ret_val;
-
+ vd_->SetTargetFramerate(frame_rate);
return VPM_OK;
}
+void VPMFramePreprocessor::SetTargetFramerate(int frame_rate) {
+ if (frame_rate == -1) {
+ vd_->EnableTemporalDecimation(false);
+ } else {
+ vd_->EnableTemporalDecimation(true);
+ vd_->SetTargetFramerate(frame_rate);
+ }
+}
+
void VPMFramePreprocessor::UpdateIncomingframe_rate() {
vd_->UpdateIncomingframe_rate();
}
diff --git a/webrtc/modules/video_processing/main/source/frame_preprocessor.h b/webrtc/modules/video_processing/main/source/frame_preprocessor.h
index 81e92ed..895e457 100644
--- a/webrtc/modules/video_processing/main/source/frame_preprocessor.h
+++ b/webrtc/modules/video_processing/main/source/frame_preprocessor.h
@@ -41,6 +41,9 @@
int32_t SetTargetResolution(uint32_t width, uint32_t height,
uint32_t frame_rate);
+ // Set target frame rate.
+ void SetTargetFramerate(int frame_rate);
+
// Update incoming frame rate/dimension.
void UpdateIncomingframe_rate();
diff --git a/webrtc/modules/video_processing/main/source/video_decimator.cc b/webrtc/modules/video_processing/main/source/video_decimator.cc
index bf05bd7..449c3bd 100644
--- a/webrtc/modules/video_processing/main/source/video_decimator.cc
+++ b/webrtc/modules/video_processing/main/source/video_decimator.cc
@@ -8,6 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
+#include "webrtc/base/checks.h"
#include "webrtc/modules/video_processing/main/interface/video_processing.h"
#include "webrtc/modules/video_processing/main/source/video_decimator.h"
#include "webrtc/system_wrappers/interface/tick_util.h"
@@ -36,11 +37,9 @@
enable_temporal_decimation_ = enable;
}
-int32_t VPMVideoDecimator::SetTargetFramerate(uint32_t frame_rate) {
- if (frame_rate == 0) return VPM_PARAMETER_ERROR;
-
+void VPMVideoDecimator::SetTargetFramerate(int frame_rate) {
+ DCHECK(frame_rate);
target_frame_rate_ = frame_rate;
- return VPM_OK;
}
bool VPMVideoDecimator::DropFrame() {
diff --git a/webrtc/modules/video_processing/main/source/video_decimator.h b/webrtc/modules/video_processing/main/source/video_decimator.h
index fca74ae..3d4573c 100644
--- a/webrtc/modules/video_processing/main/source/video_decimator.h
+++ b/webrtc/modules/video_processing/main/source/video_decimator.h
@@ -25,7 +25,7 @@
void EnableTemporalDecimation(bool enable);
- int32_t SetTargetFramerate(uint32_t frame_rate);
+ void SetTargetFramerate(int frame_rate);
bool DropFrame();
diff --git a/webrtc/modules/video_processing/main/source/video_processing_impl.cc b/webrtc/modules/video_processing/main/source/video_processing_impl.cc
index afb8290..80419e8 100644
--- a/webrtc/modules/video_processing/main/source/video_processing_impl.cc
+++ b/webrtc/modules/video_processing/main/source/video_processing_impl.cc
@@ -148,6 +148,11 @@
return frame_pre_processor_.SetTargetResolution(width, height, frame_rate);
}
+void VideoProcessingModuleImpl::SetTargetFramerate(int frame_rate) {
+ CriticalSectionScoped cs(&mutex_);
+ frame_pre_processor_.SetTargetFramerate(frame_rate);
+}
+
uint32_t VideoProcessingModuleImpl::Decimatedframe_rate() {
CriticalSectionScoped cs(&mutex_);
return frame_pre_processor_.Decimatedframe_rate();
diff --git a/webrtc/modules/video_processing/main/source/video_processing_impl.h b/webrtc/modules/video_processing/main/source/video_processing_impl.h
index b78c019..437d1d6 100644
--- a/webrtc/modules/video_processing/main/source/video_processing_impl.h
+++ b/webrtc/modules/video_processing/main/source/video_processing_impl.h
@@ -48,6 +48,8 @@
uint32_t height,
uint32_t frame_rate) override;
+ void SetTargetFramerate(int frame_rate) override;
+
// Get decimated values: frame rate/dimension
uint32_t Decimatedframe_rate() override;
uint32_t DecimatedWidth() const override;
diff --git a/webrtc/video/video_encoder.cc b/webrtc/video/video_encoder.cc
index fd213f8..8847a10 100644
--- a/webrtc/video/video_encoder.cc
+++ b/webrtc/video/video_encoder.cc
@@ -134,4 +134,10 @@
return encoder_->SupportsNativeHandle();
}
+int VideoEncoderSoftwareFallbackWrapper::GetTargetFramerate() {
+ if (fallback_encoder_)
+ return fallback_encoder_->GetTargetFramerate();
+ return encoder_->GetTargetFramerate();
+}
+
} // namespace webrtc
diff --git a/webrtc/video_encoder.h b/webrtc/video_encoder.h
index 776b22b..a512c98 100644
--- a/webrtc/video_encoder.h
+++ b/webrtc/video_encoder.h
@@ -125,6 +125,7 @@
return -1;
}
virtual void OnDroppedFrame() {}
+ virtual int GetTargetFramerate() { return -1; }
virtual bool SupportsNativeHandle() const { return false; }
};
@@ -151,6 +152,7 @@
int32_t SetRates(uint32_t bitrate, uint32_t framerate) override;
void OnDroppedFrame() override;
+ int GetTargetFramerate() override;
bool SupportsNativeHandle() const override;
private:
diff --git a/webrtc/video_engine/vie_encoder.cc b/webrtc/video_engine/vie_encoder.cc
index 7ed207f..0eea5f5 100644
--- a/webrtc/video_engine/vie_encoder.cc
+++ b/webrtc/video_engine/vie_encoder.cc
@@ -78,6 +78,9 @@
const uint32_t width,
const uint32_t height);
+ // Update target frame rate.
+ void SetTargetFramerate(int frame_rate);
+
private:
VideoProcessingModule* vpm_;
};
@@ -868,4 +871,8 @@
return vpm_->SetTargetResolution(width, height, frame_rate);
}
+void QMVideoSettingsCallback::SetTargetFramerate(int frame_rate) {
+ vpm_->SetTargetFramerate(frame_rate);
+}
+
} // namespace webrtc